构造函数继承
在之前我们有实现过new运算符,在其中也是做了原型的指向。但是,如果,你的构造函数没有定义原型对象的情况下。其实,也相当于没有使用原型链继承。
function A(x,y,z){
this.x=x;
this.y="共有属性";
this.z=()=>{
console.log("共有方法");
};
};
var b = new A(1,2);//这里传了多个参数,但是我们只使用了1个参数
var c = new A(4,5);
b.y="修改了的y";
b.z={a:1,b:2};
console.log(b); // {x: 1, y: "修改了的y", z: {a:1,b:2}}
console.log(c,c.z()); // {x: 4, y: "共有属性", z: z: ()=>{ console.log("共有方法"); }}
在上面的例子中,我们修改了实例对象b中的属性y,实例对象c并没有受到影响。如果我们希望实例对象公有一些同样的属性和方法时。这样是可行的。但是,问题是,我们每new一个实例对象出来,无论是值类型,还是引用类型,都会开辟新的内存空间。
构造函数继承的缺点
- (耗内存) 构造函数中的属性过大的话,实例化时,将占用过多的内存。
- 实例对象的共有属性或方法无法,占用一个内存空间即可。没必要都开辟新的内存空间。(通过上方的例子可看出实例对象b和c不互相影响)
每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。
注意在写构造函数时,其中的this关键字,它就代表了新创建的实例对象。我们在回顾一下
new的实现, 分为四步:
- 创建一个空对象
- 把空对象的隐式原型指向构造函数的显示原型
- 通过call或apply调用构造函数,把构造函数中的this指向新创建的空对象。(记得给构造函数传入参数,这些参数将成为创建的空对象内的私有属性,比如{x:1,y:2})。
- 返回一个对象(当你使用new运算符的时候会隐式返回这个新创建的对象,如果你new构造函数的时候,传递了参数的话,那么这个对象将有这些属性)
new 小结
- 输入是一个构造函数(可有参数可无参数,无参数时,你最终new 构造函数将得到一个空对象)
- 输出是一个对象,比如:{x:1,y:2}。
- 构造函数中不建议有返回值,这样的话,构造函数也就失去了意义。
- 构造函数如果有原型对象的话,创建的实例对象将继承原型对象上的属性和方法。
function _new(/*constr,params*/){//...arg更方便,隐式取参即可
let args=[...arguments];
let constructor=args.shift();
var newObj={};
newObj.__proto__=constructor.prototype;
//let newObj = Object.create(constructor);//issue
let result = constructor.apply(newObj ,args);
return typeof result === "object" ? result: newObj
}
var p = _new(A,1); // {x: 1, y: "共有属性", z: ƒ}
具体可见:new的实现
原型链继承
function A(){};
A.prototype={
y:"共有属性",
z:()=>{}
}
var b = new A; // 没有参数,没必要写括号
var c = new A;
console.log(b); // {}
console.log(c); // {}
console.log(b.__proto__); // {y: "共有属性", z: ƒ}
console.log(c.__proto__); // {y: "共有属性", z: ƒ}
原型链继承缺点
- 无法传参,也就无法实现私有属性和方法
构造函数+原型链继承(组合式继承)
js的作者,就是为了解决上面的耗费内存的问题,从而引入了原型对象。
function A(x){
this.x=x;
};
A.prototype={
y:"共有属性",
z:()=>{
console.log("共有方法");
}
}
var b = new A(1);
var c = new A(2);
// 通过构造函数或者实例对象都可以修改原型对象上的属性/方法,并有都会直接影响到其他实例对象。(方便修改共有属性/方法)
//b.__proto__.y="111";
A.prototype.y="222";
console.log(b); // {x: 1}
console.log(c); // {x: 2}
console.log(b.__proto__); // {y: "222", z: ƒ}
console.log(c.__proto__); // {y: "222", z: ƒ}
组合式继承小结
- 通过构造函数或者实例对象都可以修改原型对象上的属性/方法,并有都会直接影响到其他实例对象。(方便修改共有属性/方法)
- 相比单纯的构造函数继承,或者是原型链继承。结合了两者的优点。既可以拥有私有属性,又可以拥有共有属性。
- 节省系统资源
多重继承
以上通过
- 构造函数继承
- 原型链继承
- 构造函数+原型链的组合继承 只是最简单的继承场景,并没有涉及到多重继承的情况。后续会继续更新~
其他继承方式
- es6的继承 class extends
- call,apply
- Object.create({})
- 浅拷贝
- 深拷贝
总结:
- 继承的实现,既要让实例对象有私有属性,又要拥有公有属性。
- 公有属性,修改一处,所有实例都可生效,比较方便。
- 要节省系统资源。