重要的事情說三遍:一切都是對象,一切都是對象廓脆,一切都是對象筛谚!
該文章主要總結了關于js原型鏈的幾個關鍵點,方便自己回憶停忿。
1驾讲、js中對象分為普通對象和函數對象。
2席赂、凡是通過new Function()
創(chuàng)建的都是函數對象吮铭,包括Function自己也是通過new Function()
創(chuàng)建的。
3颅停、每個對象(普通對象和函數對象)都有一個__proto__
屬性谓晌,prototype
屬性只有函數對象有。
4癞揉、原型對象就是一個普通對象纸肉,此普通對象的構造函數是Object
。但有例外喊熟,Function.prototype
是函數對象柏肪,但他沒有prototype
屬性。默認情況下芥牌,任何構造函數.prototype的原型對象都是Object.prototype
**
5烦味、每個對象的proto屬性指向自身構造函數的prototype;
例子證明:
var f = function(){};
console.log(typeof Person.prototype);//Object
console.log(typeof Function.prototype);// Function胳泉,這個特殊
console.log(typeof Object.prototype);// Object
console.log(f.prototype.__proto__ === Object.prototype);// true
console.log(Function.prototype.__proto__ === Object.prototype);// true
總結:原型對象.prototype === undefined拐叉。why? 原型對象是普通對象,普通對象沒有prototype
屬性(Function.prototype
是個例外扇商,但也可以妥協的這么理解)
5凤瘦、實例并沒有constructor
這個屬性,實例通過__proto__
屬性找到他的原型對象案铺,此原型對象中有constructor
這個屬性蔬芥。
6、原型鏈的實現是通過__proto__
實現的控汉。無論是普通對象還是函數對象(我們假設都叫 o
)笔诵,要尋找他的原型對象,都是通過o.__proto__
去尋找的姑子。
7乎婿、最頂層:Object.prototype.__proto__ === null
測試題:
function Person(name) {
this.name = name
}
// 修改原型
Person.prototype.getName = function() {};
var p = new Person('jack');
console.log(p.__proto__ === Person.prototype); // true
console.log(p.__proto__ === p.constructor.prototype); // true
解析:
A: JS 在創(chuàng)建對象(不論是普通對象還是函數對象)的時候,都有一個叫做__proto__
的內置屬性街佑,用于指向創(chuàng)建它的構造函數的原型對象谢翎。
B: p.constructor
訪問到p的原型對象(即Person.prototype
)中的constructor
屬性捍靠,該屬性默認指向Person
。
function Person(name) {
this.name = name
}
// 重寫原型
Person.prototype = {
getName: function() {}
};
var p = new Person('jack');
console.log(p.__proto__ === Person.prototype); // true
console.log(p.__proto__ === p.constructor.prototype); // false
解析:
A:p.constructor
訪問到p的原型對象(即Person.prototype
)中的constructor
屬性森逮,因為重寫了Person.prototype
對象榨婆,其構造函數已改變?yōu)?code>Object,即Person.prototype.constructor
已指向了Object
褒侧。所以p.constructor.prototype === Object.prototype;
進一步:
function Person(name) {
this.name = name
}
// 重寫原型
Person.prototype = [1];
var p = new Person('jack');
console.log(p.__proto__ === Person.prototype); // true
console.log(p.constructor.prototype === Array.prototype); // true
文章參考為最詳盡的 JS 原型與原型鏈終極詳解良风,沒有「可能是」。文章中有多處錯誤及含糊不清闷供,若有疑問烟央,以本文為主。
不知道為什么歪脏,突然想來個面試題:
var F = function () {
var c = 'c';
this.d = 'd';
var a = function () {
console.log('inner a');
}
};
Object.prototype.a = function () {
console.log('a');
};
Function.prototype.b = function () {
console.log('b');
};
var f = new F();
console.log(f); //請在控制臺查看他的數據結構
f.a(); // a
F.a(); // a
F.b(); // b
console.log(F.c); //undefined why? 我也不是很懂吊档,但返回undefined還是可以預期的
console.log(F.d); //undefined why? d綁定到的是this對象,this對象是new出來的唾糯,這個構造函數又不是new它自己出來的
console.log(f.c); //undefined why? c又沒綁定到this對象,當然訪問不到
console.log(f.d); //d why? d綁定到了this對象鬼贱,當然訪問的到
f.b(); // Uncaught TypeError: f.b is not a function
思考為什么執(zhí)行結果是這樣的移怯?
解釋:
- 看 f.a(),首先查找自己對象里有沒有a这难?沒有舟误,那好,去f的原型對象中找姻乓。f的原型對象怎么表示嵌溢?f.proto唄,他指向什么蹋岩?對象的構造函數.prototype唄赖草,就是F.prototype,F.prototype的原型對象又是誰呢剪个?也就是F.prototype.proto指向誰呢秧骑?Object.prototype唄。為啥扣囊?引用我上面自己說的那句話:
默認情況下乎折,任何 構造函數.prototype 的原型對象都是 Object.prototype
看 F.a(),首先查找自己對象里有沒有a侵歇?F方法定義中好像有個定義的a方法骂澄,但是不能訪問啊(原因待續(xù))惕虑,那好吧坟冲,再去F的原型對象中找磨镶。F的原型對象怎么表示?F.proto唄樱衷,他指向什么棋嘲?對象的構造函數.prototype唄(F是個特殊的對象,是函數對象矩桂,它是由Function new出來的沸移,也就是說,它的構造函數是Function)侄榴,就是Funtion.prototype雹锣,Funtion.prototype的原型對象又是誰呢?也就是Funtion.prototype.proto指向誰呢癞蚕?Object.prototype唄蕊爵。
F.b() 請參考上面第二條
f.b() 請參考上面第一條
擴展閱讀 jQuery原理(附帶部分new的原理),主要看new的部分
新近補充:
prototype 是構造函數的屬性桦山,所以直接設置是 ConstructorFunction.prototype = ......攒射,相當于給類設置原型。
Object.setPrototypeOf() 是給對象設置原型恒水,是為了讓大量 obj.proto = .... 這種寫法更優(yōu)雅会放,有更好的兼容性。
https://segmentfault.com/q/1010000010145735