一、原型链
- 对于原型链,会从创建对象的方式、原型、构造函数、实例、原型链、
instanceof
的原理、new
运算符这几个方面分析。
- 对于创建对象的方式,如下所示:
- 字面量的方式,如下所示:
var o1 = {name: 'o1'};
var o2 = new Object({name: 'o2'});
- 构造函数的方式,如下所示:
var M = function (name) { this.name = name; };
var o3 = new M('o3');
Object.create
的方式,如下所示: var p = {name: 'p'};
var o4 = Object.create(p);
- 对于原型、构造函数、实例、原型链这几个的关系,如下所示:
- 在
JavaScript
中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype
属性,这个属性指向函数的原型对象,使用原型对象的好处是所有对象实例共享它所包含的属性和方法
- 原型链解决的主要是继承问题。每个对象拥有一个原型对象,通过
proto
指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null(Object.proptotype.__proto__ 指向的是null)
。这种关系被称为原型链(prototype chain)
,通过原型链一个对象可以拥有定义在其他对象中的属性和方法
prototype
是构造函数的属性,__proto__
是每个实例都有的属性,可以访问 [[prototype]]
属性,实例的 __proto__
与其构造函数的 prototype
指向的是同一个对象
- 原型链是原型对象创建过程的历史记录,当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的
__proto__
隐式原型上查找,即它的构造函数的prototype
,如果还没有找到就会再在构造函数的prototype
的__proto__
中查找,这样一层一层向上查找就会形成一个链式结构
- 所有函数的
__proto__
都是指向Function
的prototype
,构造函数new
出来的对象__proto__
指向构造函数的prototype
,非构造函数实例化出的对象或者对象的prototype
的__proto__
指向Object
的prototype
,Object
的prototype
指向null
- 对于
instanceof
的原理,instanceof
主要用于判断某个实例是否属于某个类型,也可用于判断某个实例是否是其父类型或者祖先类型的实例。instanceof
主要的实现原理就是只要右边变量的 prototype
在左边变量的原型链上即可。因此,instanceof
在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype
,如果查找失败,则会返回 false
。
- 对于
new
运算符,如下所示:
- 一个新对象被创建,它继承自
foo.prototype
- 构造函数
foo
被执行,执行的时候,相应的参数会被传入,同时上下文 this
会被指定为这个新实例。new foo
等同于 new foo()
,只能用在不传递任何参数的情况
- 如果构造函数返回了一个对象,那么这个对象会去取代整个
new
出来的结果。如果构造函数没有返回对象,那么 new
出来的结果为步骤一创建的对象
6.对于原型链的代码,如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>原型链</title>
</head>
<body>
<script type="text/javascript">
M.prototype.say = function () {
console.log('say hi');
};
var o5 = new M('o5');
var new2 = function (func) {
var o = Object.create(func.prototype);
var k = func.call(o);
if (typeof k === 'object') {
return k;
} else {
return o;
}
};
</script>
</body>
</html>
二、面向对象
- 对于面向对象,分为类与实例和类与继承,类与实例包括类的声明、生成实例,类与继承包括实现继承和继承的几种方式。
- 对于类的声明,如下所示:
- 对于生成实例,如下所示:
console.log(new Animal(), new Animal2());
- 对于类与继承的实现及方式,如下所示:
- 借助构造函数实现继承,但是缺点是
child1
没有继承 Parent1
原型对象的方法,代码如下所示:
function Parent1 () {
this.name = 'parent1';
}
Parent1.prototype.say = function () {
};
function Child1 () {
Parent1.call(this);
this.type = 'child1';
}
console.log(new Child1(), new Child1().say());
- 借助原型链实现继承,但是缺点是
s1
改变的东西,s2
也会看到,代码如下所示:
function Parent2 () {
this.name = 'parent2';
this.play = [1, 2, 3];
}
function Child2 () {
this.type = 'child2';
}
Child2.prototype = new Parent2();
var s1 = new Child2();
var s2 = new Child2();
console.log(s1.play, s2.play);
s1.play.push(4);
- 组合方式,
Parent3
初始化了两次,指向 Parent3
构造函数,代码如下所示:
function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
function Child3 () {
Parent3.call(this);
this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);
- 组合继承的优化一,但是缺点是指向
Parent4
的构造函数,无法区分实例是由父类创建的,还是子类创建的,代码如下所示:
function Parent4 () {
this.name = 'parent4';
this.play = [1, 2, 3];
}
function Child4 () {
Parent4.call(this);
this.type = 'child4';
}
Child4.prototype = Parent4.prototype;
var s5 = new Child4();
var s6 = new Child4();
console.log(s5, s6);
console.log(s5 instanceof Child4, s5 instanceof Parent4);
console.log(s5.constructor);
function Parent5 () {
this.name = 'parent5';
this.play = [1, 2, 3];
}
function Child5 () {
Parent5.call(this);
this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);
三、通信类
- 对于通信类,分为 同源策略及限制、前后端的通信、创建
ajax
的过程和 跨域通信的方式。
- 对于同源策略及限制,同源策源限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互,这是一个用于隔离潜在恶意文件的关键的安全机制,如下所示:
Cookie、LocalStorage和IndexDB
无法读取
DOM
无法获得
Ajax
请求不能发送
- 对于前后端的通信,如下所示:
- 对于创建
ajax
的过程,如下所示:
XMLHttpRequest
对象的工作流程
- 兼容性处理(
XMLHttpRequest
只有高级浏览器中支持,低版本中要用xmlhttp=new ActiveXObject(“Microsoft.XMLHTTP”);
)
- 事件的触发条件
- 事件的触发顺序
- 对于跨域通信的方式,如下所示:
JSONP
Hash
postMessage
WebSocket
CORS
jsonp.js
的代码,如下所示:
var util = {};
util.indexOf = function (array, item) {
for (var i = 0; i < array.length; i++) {
if (array[i] === item) {
return i;
}
}
return -1;
};
util.isFunction = function (source) {
return '[object Function]' === Object.prototype.toString.call(source);
};
util.isIE = function () {
var myNav = navigator.userAgent.toLowerCase();
return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
};
util.extend = function (dst, obj) {
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
dst[i] = obj[i];
}
}
};
util.getName = function (prefix) {
return prefix + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
};
util.createScript = function (url, charset) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
charset && script.setAttribute('charset', charset);
script.setAttribute('src', url);
script.async = true;
return script;
};
util.jsonp = function (url, onsuccess, onerror, charset) {
var callbackName = util.getName('tt_player');
window[callbackName] = function () {
if (onsuccess && util.isFunction(onsuccess)) {
onsuccess(arguments[0]);
}
};
var script = util.createScript(url + '&callback=' + callbackName, charset);
script.onload = script.onreadystatechange = function () {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
script.onload = script.onreadystatechange = null;
if (script.parentNode) {
script.parentNode.removeChild(script);
}
window[callbackName] = null;
}
};
script.onerror = function () {
if (onerror && util.isFunction(onerror)) {
onerror();
}
};
document.getElementsByTagName('head')[0].appendChild(script);
};
util.json = function (options) {
var opt = {
url: '',
type: 'get',
data: {},
success: function () {},
error: function () {},
};
util.extend(opt, options);
if (opt.url) {
var xhr = XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP');
var data = opt.data,
url = opt.url,
type = opt.type.toUpperCase(),
dataArr = [];
for (var k in data) {
dataArr.push(k + '=' + data[k]);
}
if (type === 'GET') {
url = url + '?' + dataArr.join('&');
xhr.open(type, url.replace(/\?$/g, ''), true);
xhr.send();
}
if (type === 'POST') {
xhr.open(type, url, true);
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(dataArr.join('&'));
}
xhr.onload = function () {
if (xhr.status === 200 || xhr.status === 304) {
var res;
if (opt.success && opt.success instanceof Function) {
res = xhr.responseText;
if (typeof res ==== 'string') {
res = JSON.parse(res);
opt.success.call(xhr, res);
}
}
} else {
if (opt.error && opt.error instanceof Function) {
opt.error.call(xhr, res);
}
}
};
}
};
util.crc32 = function (url) {
var a = document.createElement('a');
a.href = url;
var T = (function () {
var c = 0,
table = new Array(256);
for (var n = 0; n != 256; ++n) {
c = n;
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
table[n] = c;
}
return typeof Int32Array !=== 'undefined' ? new Int32Array(table) : table;
})();
var crc32_str = function (str) {
var C = -1;
for (var i = 0, L = str.length, c, d; i < L;) {
c = str.charCodeAt(i++);
if (c < 0x80) {
C = (C >>> 8) ^ T[(C ^ c) & 0xFF];
} else if (c < 0x800) {
C = (C >>> 8) ^ T[(C ^ (192 | ((c >> 6) & 31))) & 0xFF];
C = (C >>> 8) ^ T[(C ^ (128 | (c & 63))) & 0xFF];
} else if (c >= 0xD800 && c < 0xE000) {
c = (c & 1023) + 64;
d = str.charCodeAt(i++) & 1023;
C = (C >>> 8) ^ T[(C ^ (240 | ((c >> 8) & 7))) & 0xFF];
C = (C >>> 8) ^ T[(C ^ (128 | ((c >> 2) & 63))) & 0xFF];
C = (C >>> 8) ^ T[(C ^ (128 | ((d >> 6) & 15) | ((c & 3) << 4))) & 0xFF];
C = (C >>> 8) ^ T[(C ^ (128 | (d & 63))) & 0xFF];
} else {
C = (C >>> 8) ^ T[(C ^ (224 | ((c >> 12) & 15))) & 0xFF];
C = (C >>> 8) ^ T[(C ^ (128 | ((c >> 6) & 63))) & 0xFF];
C = (C >>> 8) ^ T[(C ^ (128 | (c & 63))) & 0xFF];
}
}
return C ^ -1;
};
var r = a.pathname + '?r=' + Math.random().toString(10).substring(2);
if (r[0] != '/') {
r = '/' + r;
}
var s = crc32_str(r) >>> 0;
var is_web = location.protocol.indexOf('http') > -1;
return (is_web ? [location.protocol, a.hostname] : ['http:', a.hostname]).join('//') + r + '&s=' + s;
};
export default util;
- 对于通信类,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>通信类</title>
</head>
<body>
<script type="text/javascript">
</script>
<script src="http://www.abc.com/?data=name&callback=jsonp" charset="utf-8"></script>
<script type="text/javascript">
</script>
<script type="text/javascript">
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'data';
window.onhashchange = function () {
var data = window.location.hash;
};
Bwindow.postMessage('data', 'http://B.com');
Awindow.addEventListener('message', function (event) {
console.log(event.origin);
console.log(event.source);
console.log(event.data);
}, false);
var ws = new WebSocket('wss://echo.websocket.org');
ws.onopen = function (evt) {
console.log('Connection open ...');
ws.send('Hello WebSockets!');
};
ws.onmessage = function (evt) {
console.log('Received Message: ', evt.data);
ws.close();
};
ws.onclose = function (evt) {
console.log('Connection closed.');
};
fetch('/some/url/', {
method: 'get',
}).then(function (response) {
}).catch(function (err) {
});
</script>
</body>
</html>
四、安全类
- 安全类,分为
CSRF
和 XSS
。
- 对于
CSRF
的基本概念,CSRF
通常称为跨站请求伪造,英文名Cross-site request forgery
, 缩写 CSRF
。
- 对于
CSRF
的攻击原理,登录过后记录 cookie
,其他网站利用接口,用cookie
攻击,引诱点击。
- 对于
CSRF
的防御措施,如下所示:
- 对于
XSS
的基本概念,XSS
通常称为 跨站脚本攻击,英文名 cross-site script
,缩写 XSS
。
- 对于
XSS
的攻击原理,Scripting
是可以获取页面数据、获取 Cookies
、劫持前端逻辑、发送请求。
- 对于
XSS
的防御措施,如下所示:
- 对于
JavaScript
代码,转义 '\'
或者转换成 json
- 按白名单保留部分标签和属性
- 使用
js-xss
这个第三方库
CSRF
和 XSS
的区别,XSS
是向页面注入 js
运行,CSRF
是利用本身的漏洞去执行接口,CSRF
要登录网站。