构造函数、原型对象和实例之间的关系
详细了解JS中的原型以及原型链可见 juejin.cn/post/733711…
还是以一个简单的例子复习一下 原型和原型链
JS中的原型关系图
借助的几个工具
1.TS Playground 将TS转义成JS www.typescriptlang.org/play
2.Babel 将高等的JS转成低等的JS babeljs.io/repl
3.JS代码在线格式化 www.bejson.com/jshtml_form…
手写继承
原型链实现
// 父类型
function Super(){
this.flag = 'super';
}
// 父类型原型上的方法
Super.prototype.getFlag = function(){
return this.flag;
}
// 子类型
function Sub(){
this.subFlag = 'sub';
}
// 实现继承。将子类的prototype设置为父类的一个实例
Sub.prototype = new Super();
// 子类型原型上的方法
Sub.prototype.getSubFlag = function(){
return this.subFlag;
}
const instance = new Sub();
console.log(instance.subFlag); // sub
console.log(instance.flag); // super
使用这种方式实现继承,存在一些问题
一、原型对象上的属性 会被所有实例共享
在通过原型链来实现继承时,原型对象上的属性被会所有实例共享,一旦一个实例修改了原型对象的属性值,会立刻反应到其他实例上
Sub.prototype.flag = 'hahahahah';
这里修改了原型对象Sub.prototype上的flag属性后,所有实例的flag都变成了这个值
二、创建子类型的实例时,不能向父类型的构造函数传递参数
实际上,应该说是 没有办法在不影响所有对象实例的情况下,给父类型的构造函数传递参数,我们传递的参数会成为所有实例的属性
借用构造函数
基本思想:apply() 或 call() 方法,在子类型构造函数的内部调用父类型的构造函数,使得子类型拥有父类型的属性和方法
// 父类型 构造函数,可以传递参数
function Super(properties){
this.properties = [].concat(properties);
this.colors = ['red', 'blue', 'green'];
}
function Sub(properties){
// 继承了 Super,传递参数,互不影响
Super.apply(this, properties);
}
var instance1 = new Sub(['instance1']);
instance1.colors.push('black');
console.log(instance1.colors); // 'red, blue, green, black'
console.log(instance1.properties[0]); // 'instance1'
var instance2 = new Sub();
console.log(instance2.colors); // 'red, blue, green'
console.log(instance2.properties[0]); // 'undefined'
1.借用构造函数解决了上面提到的两个问题:现在 实例间不会共享属性了,也可以向父类型传递参数了
2.但是这种方法仍然存在一些问题:子类型无法继承父类型原型中的属性。我们只是在子类型的构造函数中调用了父类型的构造函数,没有做其他的,子类型和父类型的原型也就没有任何联系
组合继承
基本思想:使用原型链实现对原型属性和方法的继承,借用构造函数来实现对实例属性的继承。
这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性,从而发挥二者之长
function Super(properties){
this.properties = [].concat(properties);
this.colors = ['red', 'blue', 'green'];
}
Super.prototype.log = function() {
console.log(this.properties[0]);
}
function Sub(properties){
// 继承了 Super,传递参数,互不影响
Super.apply(this, properties);
}
// 继承了父类型的原型
Sub.prototype = new Super();
// isPrototypeOf() 和 instance 能正常使用
Sub.prototype.constructor = Sub;
var instance1 = new Sub(['instance1']);
instance1.colors.push('black');
console.log(instance1.colors); // 'red,blue,green,black'
instance1.log(); // 'instance1'
var instance2 = new Sub();
console.log(instance2.colors); // 'red,blue,green'
instance2.log(); // 'undefined'
组合继承看起来很不错,但是也有它的缺点:无论什么情况下,组合继承都会调用两次父类型的构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部
寄生组合式继承
基本思想:不必为了指定子类型的原型而调用父类型的构造函数,我们所需要的无非就是父类型原型的一个副本而已
通过借用构造函数来继承属性,通过借用临时构造函数来继承原型
// 用于继承的函数
function inheritPrototype(child, parent) {
var F = function () {}
F.prototype = parent.prototype;
child.prototype = new F();
child.prototype.constructor = child;
}
// 父类型
function Su