一、原型鏈
- 對於原型鏈,會從創建對象的方式、原型、構造函數、實例、原型鏈、
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
要登錄網站。