Object.create(..)和new(..)的內部實現

Object.create()

常常會有這樣的疑問?Object.create()到底作了什麼工做? 像這樣兩行代碼有什麼不一樣?數組

var obj ={a: 1}
var b = obj
var c = Object.create(obj)
複製代碼

咱們來作一點事情,bash

var obj ={a: 1}
var b = obj
console.log(obj.a) // 1
console.log(b.a) // 1
b.a = 2
console.log(obj.a) //2
複製代碼
var obj ={a: 1}
var b = Object.create(obj)
console.log(obj.a) // 1
console.log(b.a) // 1
b.a = 2
console.log(obj.a) //1
複製代碼

因此咱們立馬能夠想到Object.create貌似建立了一個新的對象,這個對象繼承(關聯)了obj的屬性,改變新對象的同名屬性並不會影響原對象。app

若是直接用「=」來賦值,只是一個對象的引用。函數

那麼,爲何會這樣呢?是由於Object.create()複製了一個新對象麼?實際上並非,只是Object.create()返回了一個新的空對象,而且這個空對象的構造函數的原型(prototype)是指向obj的。因此當咱們訪問新對象b.a的時候其實是經過原型鏈訪問的obj中的a。ui

當咱們試圖修改b.a的時候,這裏有一個知識點(對象的遮蔽效應,若是修改對象的一個與原型鏈同名屬性,那麼會在當前對象中新建一個改屬性,這個屬性擁有更高級的訪問優先級,因此就會遮蔽原型鏈中的同名屬性)this

因此Object.create的具體內部實現模擬spa

_create = function (o) {
    let F = function () {}
    F.prototype = o
    return new F()
}
複製代碼

再來看這個例子prototype

var person = {
	friends : ["Van","Louis","Nick"]
};
var anotherPerson = _create(person);
anotherPerson.friends.push("Rob");
var yetAnotherPerson = _create(person);
yetAnotherPerson.friends.push("Style");
alert(person.friends);//"Van,Louis,Nick,Rob,Style"

複製代碼

至關於作了一次淺複製,新建立的各個對象其實是會共享原始對象中的引用類型的值,這意味着person.friends不只屬於person全部,並且也會被anotherPerson以及yetAnotherPerson共享code

實際上真正的Object.create()還能夠傳入第二個參數,這個參數與Object.defineProperties方法的第二個參數格式相同, 經過第二個參數是會在新對象中從新建立一個屬性的,而後經過屬性遮蔽原理避免修改原對象。cdn

var person = {
	name : "Van"
};
var anotherPerson = Object.create(person, {
	name : {
		value : "Louis"
	}
});
alert(anotherPerson.name);//"Louis"
複製代碼

Object.create(null) 會建立一個真正的空對象,並無繼承Object原型鏈上的方法

var a = {} 這並非一個純粹的空對象,它會繼承原型鏈上的不少方法

new()

關於new的內部實現模擬

function _new () {
  // arguments其實是一個類數組對象,須要轉成數組
  let args = [].slice.call(arguments)
  // 第一個參數是構造函數,把它拿出來
  let constructor = args.shift()
  // Object.create()返回一個新對象,這個對象的構造函數的原型指向Foo
  let context = Object.create(constructor.prototype)
  // 在返回的context對象環境中執行構造函數,爲新的context添加屬性
  let result = constructor.apply(context, args)
  // 若是Foo顯示的返回了一個對象,那麼應該直接返回這個對象,而不用理會以上全部的操做,通常不會發生這種狀況,可是new的實現的確是這樣的邏輯
  // 這裏之因此判斷類型是否爲object還要添加 != null 的判斷,是由於null的typeof結果也是‘object’
  // 不一樣的對象在底層都表示爲二進制,在Javascript中二進制前三位都爲0的話會被判斷爲Object類型,null的二進制表示全爲0,天然前三位也是0,因此執行typeof時會返回"object"
  return (typeof result === 'object' && result != null) ? result : context
}

function Foo (name) {
  this.name = name
}

Foo.prototype.getName = function() {
  console.log(this.name)
}

var a = _new(Foo, 'tom')
a.getName()

複製代碼

實際上new操做符, 就是經過Object.ctreate()建立一個新的對象,這個對象的原型指向構造函數,而且在新建對象的上下文環境中執行構造函數,初始化新建對象的屬性。

總結

固然這裏的實現只是一個模擬實現,至於就是內部真正的實現方式必然是複雜得多。好比說這裏的new方法和Object.create()必然不會相互引用,這樣會產生一個無限循環的函數,因此說這裏只是一個大概思路上的引導,對於理解js的對象繼承,原型鏈的概念會有幫助。

相關文章
相關標籤/搜索