先看一張圖栽烂!展示了構(gòu)造函數(shù)躏仇、原型和實(shí)例之間的關(guān)系。
從圖中可看出腺办,js 中與原型相關(guān)的屬性:對(duì)象有 [[prototype]]
屬性(內(nèi)部屬性)焰手、函數(shù)對(duì)象有prototype
屬性、原型對(duì)象有constructor
屬性怀喉。
[[prototype]]
在 JavaScript 中书妻,原型也是一個(gè)對(duì)象,通過(guò)原型可以實(shí)現(xiàn)對(duì)象的屬性繼承躬拢,JavaScript 的對(duì)象中都包含了一個(gè)" [[Prototype]]"內(nèi)部屬性躲履,這個(gè)屬性所對(duì)應(yīng)的就是該對(duì)象的原型。這是每一個(gè) JavaScript對(duì)象(除了 null )都具有的一個(gè)屬性.
內(nèi)部屬性聊闯,是不能被直接訪問(wèn)的工猜。所以為了方便查看一個(gè)對(duì)象的原型,F(xiàn)irefox和Chrome中提供了__proto__
這個(gè)非標(biāo)準(zhǔn)(不是所有瀏覽器都支持)的訪問(wèn)器(ES5引入了標(biāo)準(zhǔn)對(duì)象原型訪問(wèn)器Object.getPrototype(object)
)菱蔬。
function Person() {
}
var person = new Person();
console.log(person.__proto__) ;// Person {}
// console.log(Object.getPrototypeOf(person)); // Person {}
console.log(person.__proto__.__proto__); // Object {}
通過(guò)輸出結(jié)果可以看到篷帅,Person {}
作為一個(gè)原型對(duì)象,也有__proto__
屬性(對(duì)應(yīng)原型的原型)拴泌。
console.log(Object.prototype.__proto__ === null); // true
null 表示“沒(méi)有對(duì)象”犹褒,即該處不應(yīng)該有值。
所以Object.prototype.__proto__
的值為 null 跟 Object.prototype 沒(méi)有原型弛针,其實(shí)表達(dá)了一個(gè)意思叠骑。
所以查找屬性的時(shí)候查到 Object.prototype 就可以停止查找了。
prototype
在 JavaScript 中削茁,每個(gè)函數(shù)都有一個(gè) prototype 屬性宙枷。當(dāng)一個(gè)函數(shù)被用作構(gòu)造函數(shù)來(lái)創(chuàng)建實(shí)例時(shí),該函數(shù)的 prototype 屬性值將被作為原型賦值給所有對(duì)象實(shí)例(也就是該實(shí)例的__proto__
屬性)茧跋,即所有實(shí)例的原型引用的是函數(shù)的 prototype 屬性慰丛。
注:prototype 屬性是函數(shù)對(duì)象特有的。(函數(shù)才會(huì)有的屬性)
function Person() {
}
var person = new Person();
console.log(Person.prototype); // Person {}
console.log(person.__proto__ === Person.prototype) ;// true
// console.log(Object.getPrototypeOf(person) === Person.prototype) // true
console.log(Person.prototype.__proto__) // Object {}
// Person.prototype.__proto__ === Object.prototype // true
console.log(Object.prototype); // Object {}
當(dāng)通過(guò)Person.prototype.__proto__
語(yǔ)句獲取實(shí)例 person 對(duì)象原型的原型時(shí)候瘾杭,將得到Object {}
對(duì)象诅病,可以看到所有對(duì)象的原型都將追溯到Object {}
對(duì)象。
- 查看函數(shù)對(duì)象 Function 的原型
function Person() {}
console.log(Person.__proto__ === Function.prototype); // true
console.log(Person.constructor === Function); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Function.prototype.constructor === Function); // true
在 JavaScript 中有個(gè) Function 對(duì)象(類似Object),這個(gè)對(duì)象本身是個(gè)函數(shù)贤笆;所有的函數(shù)(包括Function蝇棉,Object)的原型(__proto__
)都是Function.prototype
。
Function 對(duì)象作為一個(gè)函數(shù)芥永,就會(huì)有prototype屬性篡殷,該屬性將對(duì)應(yīng) function () {}
對(duì)象。
Function 對(duì)象作為一個(gè)對(duì)象埋涧,就有__proto__
屬性板辽,該屬性對(duì)應(yīng)"Function.prototype",也就是說(shuō)棘催,Function.__proto__ === Function.prototype
對(duì)于 Function 的原型對(duì)象Function.prototype
劲弦,該原型對(duì)象的__proto__
屬性將對(duì)應(yīng)Object {}
- 如何理解原型:每一個(gè)JavaScript對(duì)象(null除外)在創(chuàng)建的時(shí)候就會(huì)與之關(guān)聯(lián)另一個(gè)對(duì)象,這個(gè)對(duì)象就是我們所說(shuō)的原型醇坝,每一個(gè)對(duì)象都會(huì)從原型"繼承"屬性瓶您。
上面說(shuō)的都是通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象,當(dāng)使用對(duì)象字面量的形式創(chuàng)建對(duì)象時(shí)纲仍,該對(duì)象的原型就是Object.prototype
。
let obj = {};
obj.__proto__ === Object.prototype;
construcor
每個(gè)原型對(duì)象都有一個(gè) constructor 屬性指向關(guān)聯(lián)的構(gòu)造函數(shù)贸毕。
實(shí)例與原型(原型鏈的屬性查找):當(dāng)讀取實(shí)例的屬性時(shí)郑叠,如果找不到,就會(huì)查找該實(shí)例對(duì)象關(guān)聯(lián)的原型中的屬性明棍,如果還查不到乡革,就去找原型的原型,一直找到最頂層 Object 為止摊腋。如果仍然沒(méi)有找到指定的屬性沸版,就會(huì)返回 undefined。
即當(dāng)通過(guò)原型鏈查找一個(gè)屬性的時(shí)候兴蒸,首先查找的是對(duì)象本身的屬性视粮,如果找不到才會(huì)繼續(xù)按照原型鏈進(jìn)行查找。
function Person() {
}
var person = new Person();
console.log(Person === Person.prototype.constructor); // true
console.log(person.constructor == Person); // true
當(dāng)獲取 person.constructor 時(shí)橙凳,其實(shí) person 中并沒(méi)有 constructor 屬性,當(dāng)不能讀取到constructor 屬性時(shí)蕾殴,會(huì)從 person 的原型也就是 Person.prototype 中讀取。
構(gòu)造函數(shù) Person 利用其原型對(duì)象上的 constructor 引用了自身岛啸,當(dāng)構(gòu)造函數(shù) Person 作為構(gòu)造函數(shù)來(lái)創(chuàng)建對(duì)象時(shí)钓觉,原型上的 constructor 就被遺傳到了新創(chuàng)建的實(shí)例對(duì)象上,從原型鏈角度講坚踩,構(gòu)造函數(shù) Person 就是新對(duì)象的類型荡灾。這樣做的意義是,讓新對(duì)象在誕生以后,就具有可追溯的數(shù)據(jù)類型批幌。
由此础锐,可以通過(guò)constructor
這個(gè)屬性,來(lái)判斷一個(gè)對(duì)象類型逼裆。尤其用來(lái)判斷 js 內(nèi)置對(duì)象的類型郁稍。
- null 和 undefined 是無(wú)效的對(duì)象,因此是不會(huì)有 constructor 存在的胜宇,這兩種類型的數(shù)據(jù)需要通過(guò)其他方式來(lái)判斷耀怜。
- 函數(shù)的 constructor 是不穩(wěn)定的,這個(gè)主要體現(xiàn)在自定義對(duì)象上桐愉,當(dāng)開(kāi)發(fā)者重寫 prototype 后财破,原有的 constructor 引用會(huì)丟失,constructor 會(huì)默認(rèn)為 Object
function Person () {}
Person.prototype = {'name': 'xql'};
var person = new Person();
console.log(person.constructor === Person); // false
console.log(person.constructor === Object); // true
prototype 被重新賦值的是一個(gè) { }从诲, { } 是 new Object() 的字面量左痢,因此 new Object() 會(huì)將 Object 原型上的 constructor 傳遞給 { },也就是 Object 本身系洛。
通常俊性,為了規(guī)范開(kāi)發(fā),在重寫對(duì)象原型時(shí)一般都需要重新給 constructor 賦值描扯,以保證對(duì)象實(shí)例的類型不被篡改定页。
hasOwnProperty()
function Person () {}
var person = new Person();
person.hasOwnProperty('constructor'); // false
hasOwnProperty()
是Object.prototype
的一個(gè)方法,該方法能判斷一個(gè)對(duì)象是否包含自定義屬性而不是原型鏈上的屬性绽诚,因?yàn)?code>hasOwnProperty()"是 JavaScript 中唯一一個(gè)處理屬性但是不查找原型鏈的函數(shù)典徊。
hasOwnProperty()
還有一個(gè)重要的使用場(chǎng)景,就是用來(lái)遍歷對(duì)象的屬性恩够。
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.getInfo = function(){
console.log(this.name + " is " + this.age + " years old");
};
var person = new Person("Will", 28);
for(var attr in person){
console.log(attr);
}
// name
// age
// getInfo
for(var attr in person){
if(person.hasOwnProperty(attr)){
console.log(attr);
}
}
// name
// age
References
JavaScript深入之從原型到原型鏈
徹底理解JavaScript原型
判斷JS數(shù)據(jù)類型的四種方法