本文最后更新于 2024-03-23T16:32:43+00:00
前置知识:14、原型与原型链 - 掘金
继承:子可以访问父的属性/方法
普通继承
原理:将父实例赋值到子构造函数的原型上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| function Parent(args = {}) { this.like = args.like || ["游泳", "足球"];
this.sayHello = function () { console.log("你好呀~"); }; }
Parent.prototype.sayName = function () { console.log(`my name is ${this.name}`); };
function Child(name, address) { this.name = name; this.address = address; }
Child.prototype = new Parent(); Child.prototype.constructor = Child;
const c1 = new Child("张三", "四川", { like: ["吃饭", "睡觉"] }); const c2 = new Child("李四", "深圳");
console.log(JSON.stringify(c1)); console.log(JSON.stringify(c2));
c1.like.push("喝酒"); c2.like.push("跑步");
console.log(JSON.stringify(c1.like)); console.log(JSON.stringify(c2.like));
c1.sayHello(); c2.sayHello();
c1.sayName(); c2.sayName();
|
优点:没啥优点,最基本的继承而已
缺点:父类是共享的;父类实例创建没法传参
经典继承
原理:在子构造函数内调用父构造函数,将值绑到子实例上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| function Parent(args = {}) { this.like = args.like || ["游泳", "足球"];
this.sayHello = function () { console.log("你好呀~"); }; }
Parent.prototype.sayName = function () { console.log(`my name is ${this.name}`); };
function Child(name, address, parentArgs) { Parent.call(this, parentArgs);
this.name = name; this.address = address; }
const c1 = new Child("张三", "四川", { like: ["吃饭", "睡觉"] }); const c2 = new Child("李四", "深圳");
c1.like.push("喝酒"); c2.like.push("跑步");
console.log(JSON.stringify(c1)); console.log(JSON.stringify(c2));
c1.sayHello(); c2.sayHello();
c1.sayName(); c2.sayName();
|
优点:父的属性/方法将直属于子实例,并且支持父构造函数的传参
缺点:没法继承父构造函数原型链上的属性Parent.prototype.*
组合继承
结合普通继承,补齐经典继承的缺点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| function Parent(args = {}) { this.like = args.like || ["游泳", "足球"];
this.sayHello = function () { console.log("你好呀~"); }; }
Parent.prototype.sayName = function () { console.log(`my name is ${this.name}`); };
function Child(name, address, parentArgs) { Parent.call(this, parentArgs);
this.name = name; this.address = address; }
Child.prototype = new Parent(); Child.prototype.constructor = Child;
const c1 = new Child("张三", "四川", { like: ["吃饭", "睡觉"] }); const c2 = new Child("李四", "深圳");
c1.like.push("喝酒"); c2.like.push("跑步");
console.log(JSON.stringify(c1)); console.log(JSON.stringify(c2));
c1.sayHello(); c2.sayHello();
c1.sayName(); c2.sayName();
console.log(JSON.stringify(Object.getPrototypeOf(c1)));
|
优点:补齐了普通、经典继承的缺点
缺点:父构造函数调用了两次
寄生组合继承
原理:解决组合继承的第二次父构造函数的调用,不让它调用呗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| function Parent(args = {}) { this.like = args.like || ["游泳", "足球"];
this.sayHello = function () { console.log("你好呀~"); }; }
Parent.prototype.sayName = function () { console.log(`my name is ${this.name}`); };
function Child(name, address, parentArgs) { Parent.call(this, parentArgs);
this.name = name; this.address = address; }
Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;
const c1 = new Child("张三", "四川", { like: ["吃饭", "睡觉"] }); const c2 = new Child("李四", "深圳");
c1.like.push("喝酒"); c2.like.push("跑步");
console.log(JSON.stringify(c1)); console.log(JSON.stringify(c2));
c1.sayHello(); c2.sayHello();
c1.sayName(); c2.sayName();
console.log(JSON.stringify(Object.getPrototypeOf(c1)));
|
特殊说明:Parent.call(this, parentArgs)
这段代码其实就是 ES6 里面,子类继承时调用的super(args)
的实现逻辑
额外补充:现在有了 ES6 的class
后,继承就很简单了,一个关键词extends
就解决了
多重继承
功能:一子可以访问多父的属性/方法
原理:基于寄生组件继承,多次调用父构造函数即可
1 2 3 4 5 6 7 8
| function Child() { Parent1.call(this) Parent2.call(this) }
Child.prototype = Object.create(Object.assgin({}, Parent1.prototype, Parent2.prototype)) Child.prototype.constructor = Child;
|