我們可以從最初的混合繼承模式,慢慢說起Object.create()
的前因後果。
1.混合模式實現原型式繼承
一般來說,使用建構函式結合原型的混合模式,可以為特定的例項共享屬性和方法。
比較常見的是下面的形式,也是最基本的使用:
function Person(name){
this.name = name;
};
Person.prototype.sayName = function(){
alert(this.name);
}
Person.prototype.XXX = XXXXX;
......
var p1 = new Person('mike');
var p2 = XXXXX;
......
複製程式碼
如果只是想達到例項共享屬性,不想噼裡啪啦的建立建構函式,懶得定義一大堆原型屬性,,完全可以用更簡潔的方法去實現。
比如,這麼做:
定義於一個create
函式,來對傳入的obj
物件加工,使obj
物件作為建構函式Person
的原型,最後返回一個臨時的新例項。
function create(obj){
function Person(){};
Person.prototype = obj;
return new Person();
}
var people = {
name:'mike'
}
var p1 = create(people);
console.log(p1.name); //mike
複製程式碼
故事還在繼續。。。
上面方法證明是可行的,不過有一個坑,那就是原型的引用型別屬性會共享相應的值
,舉個例子:
function create(obj){
function Person(){};
Person.prototype = obj;
return new Person();
}
var people = {
name:'mike',
arr:[1,2,3,4,5]
}
var p1 = create(people);
var p2 = create(people);
p1.name = 'jake';
p1.arr.push('hahaha');
console.log(p1.name); //jake
console.log(p2.name); //mike
console.log(p1.arr); //[1, 2, 3, 4, 5, "hahaha"]
console.log(p2.arr); //[1, 2, 3, 4, 5, "hahaha"]
複製程式碼
由於原型中的arr
屬性是引用型別,所以它是被例項所共享,上面可以看到原型中,基本型別屬性和引用型別屬性的差異。
2.Object.create()實現原型式繼承
ES5新增了一個Object.create()
方法,用於規範原型式繼承。
Object.create(prototype, descriptors)
複製程式碼
- prototype:必需。 要用作原型的物件。 可以為 null。
- descriptors:可選。 包含一個或多個屬性描述符的 JavaScript 物件。 “資料屬性”是可獲取且可設定值的屬性。 資料屬性描述符包含 value 特性,以及 writable、enumerable 和 configurable 特性。 如果未指定最後三個特性,則它們預設為 false。【這很重要,意味著如果不顯式宣告屬性的
enumerable=true
,它在某些方法下是不可列舉遍歷的】
在只傳入一個引數的情況下,Object.create()
與上面的create()
用法相同:
var person = {
name:'mike',
arr:[1,2,3,4,5]
}
var p1 = Object.create(person);
var p2 = Object.create(person);
p1.name = 'jake';
p1.arr.push('hahaha');
console.log(p1.name) //jake
console.log(p2.name) //mike
console.log(p1.arr) //[1, 2, 3, 4, 5, "hahaha"]
console.log(p2.arr) //[1, 2, 3, 4, 5, "hahaha"]
複製程式碼
注意了,還是那個坑,包含引用型別的屬性始終會共享相應的值。
Object.create()
接收的第二個引數,是為新物件定義額外的屬性,指定的任何屬性都會覆蓋原型上的同名屬性。
var person = {
name:'mike',
arr:[1,2,3,4,5]
}
var p1 = Object.create(person,{
name:{
value:'jake'
},
arr:{
value:[7,8,9]
}
});
var p2 = Object.create(person);
p1.arr.push('hahaha');
console.log(p1.name) //jake
console.log(p2.name) //mike
console.log(p1.arr) //[7, 8, 9, "hahaha"]
console.log(p2.arr) //[1, 2, 3, 4, 5]
複製程式碼
以下補充於2018-09-12。
**Object.create()**還可以用於完整克隆一個物件,包括它的原型屬性。
Object.create(
Object.getPrototypeOf( obj ),
Object.getOwnPropertyDescriptors( obj )
)
複製程式碼
3.proto
改變物件的原型鏈指標__proto__
也可以實現繼承另一個物件。
不推薦使用這個屬性,__proto__
屬性只有瀏覽器必須部署,在非瀏覽器的環境不一定部署。
function Person(){
this.sex = 'man';
}
Person.prototype.name = 'hello';
var p = new Person();
var obj = {
addr:'china',
__proto__:new Person
}
console.log(obj.name) //hello
console.log(obj.sex) //man
複製程式碼
4.Object.setPrototypeOf()
ES6新增API,Object.setPrototypeOf()
Object.setPrototypeOf 方法的作用與 __proto__
相同,用來設定一個物件的 prototype 物件,返回引數物件本身。
Object.setPrototypeOf(object, prototype)
複製程式碼
function Person(){
this.sex = 'man';
}
Person.prototype.name = 'hello';
var p = new Person();
var obj = {
addr:'china'
}
Object.setPrototypeOf( obj, new Person )
console.log(obj.name) //hello
console.log(obj.sex) //man
複製程式碼