me)
}
}
这种方式完全重写了 prototype, 包括其原有的 constructor 属性(指向了字面量对象即 Object)
解决办法就是手动指定一下
Person.prototype = {
constructor: Person
}
原型对象的问题:
-
实例无法给构造函数传值
-
共享既是优点也是缺点, 有些属性希望各个实例各自保持自己的, 就无法通过此方法实现
组合模式
看到了吗? 构造函数模式和原型模式实质上是两个极端, 一个是每个实例都是各自为营, 一个是每个实例都步调一致, 所以, 两者的结合就是更好的解决方案.也是现在最常用的方案.
function Person(name) {
// 每个实例各有的
this.name = name
}
// 每个实例共享的
Person.prototype.sayName = function() {
console.log(this.name)
}
还有一种动态原型的变体
function Person(name) {
this.name = name;
// 只会在构造函数初始化时创建一次
if (typeof this.sayName !== 'function') {
Person.prototype.sayName = function() {
console.log(this.name)
}
}
}
寄生构造函数以及稳妥寄生构造函数模式
首先什么叫寄生? 之前我只知道这个模式叫寄生, 但是不知道为什么叫寄生. 现在我的理解是: 寄生是一种相互独立的状态, 就像寄居蟹, 它可以爬到任何一个的壳中生活.看下面的例子
function Person(name) {
const o = new Object();
o.name = name;
o.sayName = function() {
console.log(this.name)
}
return o;
}
const p1 = new Person('lorry')
const p2 = Person('lorry')
// p1和p2所拥有的属性和方法是一样的.
上述代码中, 壳就是 function Person(name){}
这部分, 寄居蟹就是剩余的部分, 调用 new Person()返回的对象跟 Person 没有任何原型上的关系(p1 instanceof Person = false
).
这样有什么好处呢? 私有变量
function Person(name) {
const o = new Object()
o.sayName = function() {
console.log(name)
}
return o;
}
const p1 = new Person('lorry')
p1中就保存了一个稳定对象, 除了调用 sayName 之外没有任何办法可以获取到构造函数中的数据成员.
对象的和继承
OO 的语言通常有两种继承
-
接口的继承, 只继承方法签名
-
实现的继承, 继承实际的方法
ECMA 只支持实现的继承, 也就是具体的方法, 当然一些java script 的超集, 比如 typescript 可以支持接口的继承.
interface A {
name: string
}
interface B extends A {
age: number
}
var b: B = {
name: 'lorry',
age: 26
}
原型链继承
原理就是将 SubType 的[[ prototype ]] 属性指向了 SuperType 的prototype, 本质就是重写了 prototype.
function SuperType() {
this.property = false;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subProperty = true;
}
// 实现了原型继承, 拥有 SuperType 的所有实例属性和方法
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subProperty;
}
const subIns = new SubType();
console.log(subIns.getSuperValue());
描述继承关系: SubType 继承 SuperType, SuperType 继承默认的原型 Object. 所以
console.log(subIns instanceof SubType) // true
console.log(subIns instanceof SuperType) // true
console.log(subIns instanceof Object) // true
console.log(Object.prototype.isPrototypeOf(subIns))//true
console.log(SuperType.prototype.isPrototypeOf(subIns))//true
console.log(SubType.prototype.isPrototypeOf(subIns))//true
问题:
-
引用类型(比如数组)的原型属性会被所有实例共享.但实质上之所以在 SuperType 的构造函数中定义属性就是不希望所有实例共享.
-
创建子类的实例时(上例中的 subIns ), 无法向父类构造函数中传参.因为继承不发生在构造函数中
借用构造函数
为了解决上述的第二个问题, 有了这个构造函数继承方式
function SuperType(name) {
this.name = name
}
function SubType(name) {
SuperType.call(this, name);
}
const subIns = new SubType('lorry')
subIns.name;// lorry
就跟构造函数的问题一样, 无法实现函数的复用.
组合继承
跟组合创建对象模式一样, 将借用构造函数和原型链继承的方式组合起来就形成了组合继承的方式.
function SuperType(name) {
this.name = name
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name) {
// 继承属性
SuperType.call(this, name);
}
// 继承方法
SubType.prototype = new SuperType()
const subIns = new SubType('lorry')
subIns.sayName() // lorry
注意: 其实在继承方法的时候也继承了实例的属性, 但是在查找原型链的时候, 因为实例本身就有其属性了, 不会再向上到超类中查找, 所以相当于只继承了方法. 这两者的结合就形成了最常用的继承方式.
原型式继承
这种方式是临时创建一个对象, 然后使该对象的原