學習constructor和instanceof的區別

1、constructor

咱們建立的每一個函數都有一個prototype(原型)對象,這個屬性是一個指針,指向一個對象。在默認狀況下,全部原型對象都會自動得到一個constructor(構造函數)屬性,這個屬性是一個指向prototype屬性所在函數的指針。瀏覽器

function Person () {}
 console.log(Person.prototype)
複製代碼

打印結果以下:bash

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

function Person (name, age) {
    this.name = name
    this.age = age
  }
 var person = new Person('Jim', 21)
 console.log(person)
 console.log(Object.keys(person))
 console.log(person.__proto__)
 console.log(person.__proto__ === Person.prototype)
複製代碼

打印結果以下:測試

由於constructor屬性在構造函數的原型裏,而且指向構造函數,那咱們就能夠利用constructor屬性來判斷一個實例對象是由哪一個構造函數構造出來的,也能夠說判斷它屬於哪一個類。this

function Person (name, age) {
    this.name = name
    this.age = age
  }
  
  function Car () {}
  
  var person = new Person('Jim', 21)
  
 console.log(person.constructor)   //Person
 
 console.log(person.constructor === Person)    //true
 
 console.log(person.constructor === Car)    //  false
複製代碼

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

function Person () {}
  
  var person1 = new Person()
  
  Person.prototype = {
    name: 'Jim',
    age: '21'
  }
 var person2 = new Person()
 console.log(person1)

 console.log(person2)

 console.log(person1.constructor)
 console.log(person2.constructor)
複製代碼

打印結果以下:prototype

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

那若是咱們對 Person.prototype從新賦值後但願constructor仍指向Person的話,咱們能夠在字面對象里加一個constructor屬性讓它指向Personcode

function Person () {}
 var person1 = new Person()
 Person.prototype = {
   constructor: Person,
   name: 'Jim',
   age: '21'
 }
 var person2 = new Person()
 console.log(person1)
 console.log(person2)
 console.log(person1.constructor)
 console.log(person2.constructor)
 console.log(Object.keys(Person.prototype))
複製代碼

打印結果以下:對象

咱們發現person2.constructor從新指向Person,但同時咱們也發現constructor變成了可枚舉屬性,上文說到constructor屬性默認是不可枚舉的,即[[Enumerable]]的值爲false 咱們能夠經過Object.defineProperty()把constructor定義爲不可枚舉屬性

function Person () {}
 var person1 = new Person()
 Person.prototype = {
   name: 'Jim',
   age: '21'
 }
 // 重設構造函數,只使用與ECMAScript5兼容的瀏覽器
 Object.defineProperty(Person.prototype, 'constructor', {
  enumerable: false,
  value: Person
 })
 var person2 = new Person()
 console.log(person1)
 console.log(person2)
 console.log(person1.constructor)
 console.log(person2.constructor)
 console.log(Object.keys(Person.prototype))
複製代碼

打印結果以下:

咱們能夠看到constructor已經變成了不可枚舉屬性

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

function Person () {}
function Car () {}
var person = new Person()
console.log(person instanceof Person)  //true
console.log(person instanceof Car)   //false
複製代碼

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

function Person () {}
 Person.prototype = {
   name: 'Jim',
   age: '21'
 }
 var person = new Person()
 console.log(person instanceof Person)    //true
 console.log(person.constructor === Person)    //false
複製代碼

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

function Person () {}
 
var person = new Person()
console.log(person instanceof Person)  //true
console.log(person instanceof Object)  //true
person.__proto__ = {}
console.log(person instanceof Person)  //false]
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的實例,處於原型鏈的末端,而它沒有原型。

相關文章
相關標籤/搜索