constructor和instanceof的區別與作用


一、constructor

我們創建的每個函數都有一個prototype(原型)對象,這個屬性是一個指針,指向一個對象。在默認情況下,所有原型對象都會自動獲得一個constructor(構造函數)屬性,這個屬性是一個指向prototype屬性所在函數的指針。

1 function Person () {}
2 
3 console.log(Person.prototype)

打印結果如下:

當調用構造函數創建一個新實例后,該實例的內部將包含一個指針,指向構造函數的原型對象。ECMA-262第5版中管這個指針叫[[Prototype]],雙中括號表示該屬性為內部屬性,在JavaScript中不能直接訪問。雖然在腳本中沒有標准的方式訪問[[Prototype]],但Firefox、Safari和Chrome在每個對象上都支持一個屬性__proto__來訪問[[Prototype]],但__proto__的[[Enumerable]]值為false,不可以通過for-in或Object.keys()枚舉出來。

 1 function Person (name, age) {
 2   this.name = name
 3   this.age = age
 4 }
 5 
 6 var person = new Person('Jim', 21)
 7 
 8 console.log(person)
 9 
10 console.log(Object.keys(person))
11 
12 console.log(person.__proto__)
13 
14 console.log(person.__proto__ === Person.prototype)

打印結果如下:

因為constructor屬性在構造函數的原型里,並且指向構造函數,那我們就可以利用constructor屬性來判斷一個實例對象是由哪個構造函數構造出來的,也可以說判斷它屬於哪個類。

 1 function Person (name, age) {
 2   this.name = name
 3   this.age = age
 4 }
 5 
 6 function Car () {}
 7 
 8 var person = new Person('Jim', 21)
 9 
10 console.log(person.constructor)   //Person
11 
12 console.log(person.constructor === Person)    //true
13 
14 console.log(person.constructor === Car)    //  false

但有一點我們是要注意的,當我們將Person.prototype設置為等於一個以對象字面量形式創建的新對象時,constructor屬性不再指向Person。因為上述做法把Person默認的prototype覆蓋掉,指向Person的constructor就不復存在。

 1 function Person () {}
 2 
 3 var person1 = new Person()
 4 
 5 Person.prototype = {
 6   name: 'Jim',
 7   age: '21'
 8 }
 9 
10 var person2 = new Person()
11 
12 console.log(person1)
13 
14 console.log(person2)
15 
16 console.log(person1.constructor)
17 
18 console.log(person2.constructor)

 打印結果如下:

訪問person2時,Person.prototype里已經沒有了constructor屬性,所以會繼續沿着原型鏈往上找到Person.prototype.prototype中的constructor屬性,它是指向Object的,因此person2.constructor指向Object。

 

那如果我們對 Person.prototype重新賦值后希望constructor仍指向Person的話,我們可以在字面對象里加一個constructor屬性讓它指向Person

 1 function Person () {}
 2 
 3 var person1 = new Person()
 4 
 5 Person.prototype = {
 6   constructor: Person,
 7   name: 'Jim',
 8   age: '21'
 9 }
10 
11 var person2 = new Person()
12 
13 console.log(person1)
14 
15 console.log(person2)
16 
17 console.log(person1.constructor)
18 
19 console.log(person2.constructor)
20 
21 console.log(Object.keys(Person.prototype))

打印結果如下:

我們發現person2.constructor重新指向Person,但同時我們也發現constructor變成了可枚舉屬性,上文說到constructor屬性默認是不可枚舉的,即[[Enumerable]]的值為false

我們可以通過Object.defineProperty()把constructor定義為不可枚舉屬性

 1 function Person () {}
 2 
 3 var person1 = new Person()
 4 
 5 Person.prototype = {
 6   name: 'Jim',
 7   age: '21'
 8 }
 9 // 重設構造函數,只使用與ECMAScript5兼容的瀏覽器
10 Object.defineProperty(Person.prototype, 'constructor', {
11   enumerable: false,
12   value: Person
13 })
14 
15 var person2 = new Person()
16 
17 console.log(person1)
18 
19 console.log(person2)
20 
21 console.log(person1.constructor)
22 
23 console.log(person2.constructor)
24 
25 console.log(Object.keys(Person.prototype))

打印結果如下: 

我們可以看到constructor已經變成了不可枚舉屬性

 

二、instanceof

instanceof 運算符用來測試一個對象在其原型鏈中是否存在一個構造函數的 prototype 屬性。通俗來將就是判斷一個實例對象是否由某個構造函數構造而來。

1 function Person () {}
2 
3 function Car () {}
4 
5 var person = new Person()
6 
7 console.log(person instanceof Person)  //true
8 
9 console.log(person instanceof Car)   //false

這一點與constructor有着相同的作用,但instanceof相對於constructor更為可靠

 1 function Person () {}
 2 
 3 Person.prototype = {
 4   name: 'Jim',
 5   age: '21'
 6 }
 7 
 8 var person = new Person()
 9 
10 console.log(person instanceof Person)    //true
11 
12 console.log(person.constructor === Person)    //false

可見Person.prototype對象被重寫並不影響instanceof的判斷,因為instanceof是根據原型鏈來判斷構造函數的,只要對象實例的原型鏈不發生變化,instanceof便可以正確判斷

 1 function Person () {}
 2 
 3 var person = new Person()
 4 
 5 console.log(person instanceof Person)  //true
 6 
 7 console.log(person instanceof Object)  //true
 8 
 9 person.__proto__ = {}
10 
11 console.log(person instanceof Person)  //false
12 
13 console.log(person instanceof Object)   //true

instanceof不僅可以判斷實例對象直接的構造函數,而且還能判斷原型鏈上所有的構造函數,上面代碼中Object在person的原型鏈中,所以返回true。

當我們把空字面對象{}賦值給person.__proto__后,相當於切斷了person原來的原型鏈(person -> Person -> Object),所以person instanceof Person返回false,那為什么person instanceof Object會返回true呢,因為空字面對象是Object的實例,即原型鏈修改為(person -> Object)。這里順帶說一下我面試時被問到的一道面試題,就是Object的原型是什么?答案是null,因為Object是萬物之源,所以對象都是Object的實例,處於原型鏈的末端,而它沒有原型。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM