js基礎(chǔ)之原型(構(gòu)造函數(shù)、原型和實(shí)例之間的那些屬性)

先看一張圖!展示了構(gòu)造函數(shù)、原型和實(shí)例之間的關(guān)系。

由相互關(guān)聯(lián)的原型組成的鏈狀結(jié)構(gòu)就是原型鏈

從圖中可看出,js 中與原型相關(guān)的屬性:對(duì)象有 [[prototype]]屬性(內(nèi)部屬性)、函數(shù)對(duì)象有prototype屬性、原型對(duì)象有constructor屬性。

[[prototype]]

在 JavaScript 中,原型也是一個(gè)對(duì)象,通過原型可以實(shí)現(xiàn)對(duì)象的屬性繼承,JavaScript 的對(duì)象中都包含了一個(gè)" [[Prototype]]"內(nèi)部屬性,這個(gè)屬性所對(duì)應(yīng)的就是該對(duì)象的原型。這是每一個(gè) JavaScript對(duì)象(除了 null )都具有的一個(gè)屬性.

內(nèi)部屬性,是不能被直接訪問的。所以為了方便查看一個(gè)對(duì)象的原型,F(xiàn)irefox和Chrome中提供了__proto__這個(gè)非標(biāo)準(zhǔn)(不是所有瀏覽器都支持)的訪問器(ES5引入了標(biāo)準(zhǔn)對(duì)象原型訪問器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 {}

通過輸出結(jié)果可以看到,Person {}作為一個(gè)原型對(duì)象,也有__proto__屬性(對(duì)應(yīng)原型的原型)。

console.log(Object.prototype.__proto__ === null); // true

null 表示“沒有對(duì)象”,即該處不應(yīng)該有值。
所以 Object.prototype.__proto__的值為 null 跟 Object.prototype 沒有原型,其實(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)通過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",也就是說,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ì)象就是我們所說的原型,每一個(gè)對(duì)象都會(huì)從原型"繼承"屬性。

上面說的都是通過構(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 為止。如果仍然沒有找到指定的屬性,就會(huì)返回 undefined。

即當(dāng)通過原型鏈查找一個(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 中并沒有 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ù)類型。

由此,可以通過constructor這個(gè)屬性,來(lái)判斷一個(gè)對(duì)象類型。尤其用來(lái)判斷 js 內(nèi)置對(duì)象的類型。

判斷內(nèi)置對(duì)象的類型

  1. null 和 undefined 是無(wú)效的對(duì)象,因此是不會(huì)有 constructor 存在的,這兩種類型的數(shù)據(jù)需要通過其他方式來(lái)判斷。
  2. 函數(shù)的 constructor 是不穩(wěn)定的,這個(gè)主要體現(xiàn)在自定義對(duì)象上,當(dāng)開發(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ī)范開發(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ù)類型的四種方法

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容