原型模式(prototype):是指用原型例項指向建立物件的種類,並且通過拷貝這些原型建立新的物件。
真正的原型繼承是作為最新版的ECMAScript5標準提出的,使用Object.create方法來建立這樣的物件,該方法建立指定的物件,其物件的prototype有指定的物件(也就是該方法傳進的第一個引數物件),也可以包含其他可選的指定屬性。例如Object.create(proto, [propertiesObject])
過程
Prototype => new ProtoExam => clone to new Object;
建立一個原型=>new一個原型例項=>拷貝原型例項到新的物件
// 建立一個原型並且新增一些屬性 this.XXX 新增
function Person () {
this.name = 'Uzi'
this.sex = '21'
this.age = '男'
this.getName=function () {
return `姓名:${this.name}`
}
}
// 通過 .prototype.XXX 方式 新增也可
Person.prototype.getMsg = function () {
return `姓名: ${this.name}, 年齡: ${this.age}, 性別:${this.sex}`
}
// new一個原型例項
const proto = new Person();
// 通過 Object.create 拷貝原型例項
const person1 = Object.create(proto);
const person2 = Object.create(proto);
person2.name = 'Ming'
person2.sex = '20'
person2.age = '男'
console.log(person1.getName()) // 姓名: Uzi
console.log(person2.getName()) // 姓名: Ming
console.log(person1.getMsg()) // 姓名: Uzi, 年齡: 男, 性別:21
console.log(person2.getMsg()) // 姓名: Ming, 年齡: 男, 性別:20
複製程式碼
此段程式碼也證明了:原型掛在例項的原型鏈上,修改例項不影響原型,即為深拷貝
原型模式,一般用於抽象結構複雜,但內容組成差不多,抽象內容可定製,新建立只需在原建立物件上稍微修改即可達到需求的情況。
優點
方法不會重新建立(只要 new 一個原型例項)
缺點:
- 所有的屬性和方法都共享
- 不能初始化引數
Object.create
定義
建立一個具有指定原型且可選擇性地包含指定屬性的物件
Object.create(proto, [propertiesObject])
proto
:新建立物件的原型物件
propertiesObject
:可選。如果沒有指定為 undefined,則是要新增到新建立物件的可列舉屬性(即其自身定義的屬性,而不是其原型鏈上的列舉屬性)物件的屬性描述符以及相應的屬性名稱。這些屬性對應 Object.defineProperties() 的第二個引數。
使用
1.定義原型物件
// 新增屬性方法 this.XXX方式
function Person () {
this.name = 'Uzi'
this.sex = '21'
this.age = '男'
this.getName=function () {
return `姓名:${this.name}`
}
}
// 新增屬性方法 .prototype.XXX 方式
Person.prototype.getMsg = function () {
return `姓名: ${this.name}, 年齡: ${this.age}, 性別:${this.sex}`
}
複製程式碼
2.使用 Object.create
拷貝 原型
1.以 Person.prototype
建立物件
const person1 = Object.create(Person.prototype, {
position:{value: 'ADC', writable: true}
})
console.log(person1.position) // ADC
console.log(person1.getMsg()) // 姓名: undefined, 年齡: undefined, 性別:undefined
console.log(person1.getName()) // Uncaught TypeError: person1.getName is not a function
複製程式碼
這說明 person1
就具有 Person
原型方法 getMsg
的方法,但是不具有 Person
下成員屬性 name、sex、age、getName
2. 以例項化的 Person
做原型
const proto = new Person(); // 例項化的Person
const person2 = Object.create(proto, {
position: { value: 'SUP', writable: true }
});
person2.name = 'Ming'
person2.sex = '20'
person2.age = '男'
console.log(person2.position) // SUP
console.log(person2.getMsg()) // 姓名: Ming, 年齡: 男, 性別:20
console.log(person2.getName()) // 姓名: Ming
複製程式碼
這說明,這樣建立的 person2
就具有 Person
的所有的成員屬性 name、sex、age、getName
以及 getMsg 原型方法; 並新增了一個 position
只讀 資料屬性;
使用 Object.create
實現子類繼承
1.以 Person.prototype
物件,做為 Son
的原型
function Son () {
Person.call(this); // 不然無法 Person 成員屬性
}
Son.prototype = Object.create(Person.prototype, {
position: { value: '兒子' }
});
Son.prototype.sonMethod = function() {
return this.name || this.position;
}
const son1= new Son()
console.log(son1.position) // 兒子
console.log(son1.getMsg()) // 姓名: Uzi, 年齡: 男, 性別:21
console.log(son1.sonMethod()) // Uzi
console.log(son1.getName()) // 姓名: Uzi
複製程式碼
2.以例項化的 Person
物件,做為 Son
的原型
const proto = new Person();
function Son () {
}
Son.prototype = Object.create(proto, {
position: { value: '兒子' }
});
const son1= new Son()
console.log(son1.position) // 兒子
console.log(son1.getMsg()) // 姓名: Uzi, 年齡: 男, 性別:21
console.log(son1.getName()) // 姓名: Uzi
複製程式碼
這樣 Son
例項化後,就可以獲取到 Person
所有的屬性及原型方法,以及建立一個只讀資料屬性 position
;
Object.create
的實現
Object.prototype.create = function (obj) {
if (Object.prototype.create) {
return Object.prototype.create
} else {
function F () {
F.prototype = obj; //以傳入引數為原型構造物件
return new F();
}
}
}
複製程式碼
由此也能看出返回了一個新物件,深拷貝
Object.create、{}、new Object() 對比
Object.create 指定了拷貝的原型物件,Object.create(null)的時候物件不具備任何原型屬性,{}、new Object()、具備物件的原型屬性