常常會有這樣的疑問?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的內部實現模擬
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的對象繼承,原型鏈的概念會有幫助。