new,create,class,extends,{}創建對象的原理及原型鏈查找規則

首發:https://zhuanlan.zhihu.com/p/91439580

原型,原型鏈屬於js語言的基礎,近期面試了很多同學,發現有很大一部分並不清楚原型,隱式原型,原型鏈之間的關係,加上自己在業務中分析源碼遇到的一些問題,秉着知識回顧和總結的初心,詳細總結下js原型相關的知識,有不足之處,還望大家補充。

幾個概念:

原型:prototype

隱式原型:__proto__

本文將從創建對象的不同方法中引出原型鏈查找規則

創建對象的5個方法

在講創建方法之前先來看下Object例子

const object_shadow = Object;
console.log(object_shadow)
console.log(typeof object_shadow) // function
console.log(typeof new object_shadow()) //object

調試上面的代碼,可以得到如下結果:

object_shadow沒有constructor,沒有prototype

object_shadow的__proto__等於function (){}

=> function(){}的constructor等於 function Function(){}

=> function Function(){}的prototype 等於function() {}

可以得到:object_shadow._proto_ = object_shadow.constructor.prototype

說明:object_shadow在查找constructor沒找到時,會通過__proto__向上查找,由於object_shadow並沒有constructor,本質object_shadow會通過__proto__向上查找

另外Object是一個函數類型,所以可以使用new關鍵字

接下來看5中創建對象的方法

1,字面量創建對象

const test = {
  title:'字面量創建對象'
}

調試test:

從結果可以看出直接創建,_proto_指向Object

2,構造函數創建

function a(){
  this.title = "構造函數創建對象"
}

const A = new a()

執行過程:

創建一個字面量對象var obj = {}

設置新對象的constructor屬性爲構造函數的名稱

指定obj的隱式原型等於a的prototype obj._proto_ = a.prototype

執行構造函數中的代碼,構造函數中的this指向new出對象 a.call(obj)

將初始化完畢的新對象地址,保存到等號左邊的變量中, 即賦值給A

調試A

可以看出:A._proto_ = A.constructor.prototype

3,Object.create()創建對象

const create_test = Object.create(test)
const create_a = Object.create(a)

create_test不滿足 create_test._proto_ = create_test.constructor.prototype

create_a不滿足 create_a._proto_ = create_a.constructor.prototype

console.log(create_a.title)//會得到undefined

Object.create(o)執行過程

    // 創造一個新匿名函數
    var F = function () {};
    // 給該匿名函數的原型指向o
    F.prototype = o;
    // 返回該匿名函數的實例
    return new F();

Object.create創建對象的過程包括了new

通過create創建的對象的_proto_指向構造函數F的prototype,通過上面的打印結果也可以看出。

4,class創建對象

class b {
  title(){
    return 'class創建對象'
  }
}

class創建的對象默認有prototype屬性,不需要通過_proto_向上查找

5,extends創建對象

class extends_b extends b{}
//下面三個均報錯
class extends_A extends A{}//TypeError: Class extends value #<Object> is not a constructor or null
class extends_test extends test{}/TypeError: Class extends value #<Object> is not a constructor or null
class extends_create_test extends create_test{}//TypeError: Class extends value #<Object> is not a constructor or null

extends創建對象 的 _proto_和prototype均指向被繼承對象

由上面5種創建方法可以看出

1,class創建對象的方式,對象默認有prototype屬性

2,Object.create的創建的對象沒有prototype屬性,只有_proto_屬性指向傳入的對象

3,new a()創建方式 _proto_屬性指向constructor爲a的對象,也沒有prototype屬性

4,字面量創建對象_proto_屬性指向Object,也沒有沒有prototype屬性

原型鏈查找規則

來看一個例子:

const test = {
  title:'字面量創建對象'
}
test.constructor.prototype.happy = 'constructor.prototype.happy'
test.constructor.prototype.pro_l = 'constructor.prototype.pro_l'
test.__proto__.pro_l = '_l'
console.log(test.pro_l)//'_l'

console.log(test.happy)//'constructor.prototype.pro_l'
create_a.constructor.prototype.__proto__.constructor.prototype.index = 3;
console.log(create_a.index)//3

test對象上沒有happy,pro_l屬性,默認向__proto_查找,此時如果_proto_上有屬性,則獲取,如果沒有__proto_會指向_proto_的constructor的prototype,也就是會查找其構造函數的prototype,如果沒有,繼續查找當前_proto_的_proto_

再看一個例子

//A是上面的對象
A.__proto__.constructor.prototype.test_a = 'pro_test_a'
A.__proto__.__proto__.test_a = 'test_a'
console.log(A.test_a)//pro_test_a

如果A.__proto__.constructor.prototype.test_a不存在,則取A.__proto__.__proto__.test_a

//A是上面的對象
//A.__proto__.constructor.prototype.test_a = 'pro_test_a'
A.__proto__.__proto__.test_a = 'test_a'
console.log(A.test_a)//test_a 

constructor的prototype上沒有屬性時,繼續查找當前__proto__的_proto_

 

如圖:

總結:

1,對象的_proto_和A.constructor.prototype不一定相等,比如Object.create()就不是

2,{}創建對象效率是最高的,不需要執行構造函數,_proto_直接指向Object

3,new 一個對象的過程,新對象的_proto_指向該構造函數的prototype

4,Object.create()對象內部包含了new 運算符,新對象的_proto_指向傳入的對象

5,new和Object.create()的區別,_proto_是否指向傳入的對象

6,原型鏈查找

對象的prototype>_proto_>對象的constructor.prototype>_proto_._proto_

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章