原型模式(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()、具备对象的原型属性