day19_JS_继承进阶
1.JS中的继承
继承是面向对象中一个比较核心的概念。
其他正统面向对象语言都会有两种方式实现继承:一个是接口实现,一个是继承。
而JS只是继承,不支持接口实现,而实现继承的方式依靠原型链完成。
在js里,被继承的函数称为超类型(父类,基类也行,其他语言叫法)
继承的函数称为子类型(子类,派生类)。
继承也有之前问题,比如字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。
为了解决引用共享和超类型无法传递参数的问题,我们采用一种借用构造函数的技术或者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题。
-
冒充式继承
function Box(_a) { this.a = _a; this.play(); } // 添加一个实例化属性和方法 Box.prototype.a = 10; Box.prototype.play = function () { } // 添加一个静态属性和方法 Box.b = 20; Box.run = function () { } function Ball(_a) { //此处会出错,this.play is not a function Box.call(this, _a); } var b = new Ball(10); console.loe(b);
-
组合式继承
function Box(_a) { console.log("aaa");//打印两次aaa this.a = _a; this.play(); } // 添加一个实例化属性和方法 Box.prototype.a = 10; Box.prototype.play = function () { } // 添加一个静态属性和方法 Box.b = 20; Box.run = function () { } function Ball(_a) { // Box.call(this,_a); this.a = _a; this.play(); } Ball.prototype = new Box(); // 将原来constructor中的Box替换为Ball Ball.prototype.constructor = Ball; var b = new Ball(10); console.log(b); // 缺点:原型中a的属性值没有继承到
-
原型式继承
function Box(_a) { console.log("aaa");//打印两次aaa this.a = _a; this.play(); } // 添加一个实例化属性和方法 Box.prototype.a = 10; Box.prototype.play = function () { } // 添加一个静态属性和方法 Box.b = 20; Box.run = function () { } function Ball(_a) { Box.call(this, _a); } function F() { } F.prototype = Box.prototype; Ball.prototype = new F(); Ball.prototype.constructor = Ball; let b = new Ball(10); console.log(b);
-
寄生式继承
- 版本一(相对容易理解)
function Box(_a) { this.a = _a; this.play(); } // 添加一个实例化属性和方法 Box.prototype.a = 10; Box.prototype.play = function () { } // 添加一个静态属性和方法 Box.b = 20; Box.run = function () { } function extend(subClass, supClass) { // 创建一个中间替代类,防止多次执行父类(超类)的构造函数 function F() { } // 将父类的原型赋值给这个中间替代类 F.prototype = supClass.prototype; // 将原子类的原型保存 var proto = subClass.prototype; // 将子类的原型设置为中间替代类的实例对象 subClass.prototype = new F(); // 将原子类的原型复制到子类原型上,合并超类原型和子类原型的属性方法 var names = Object.getOwnPropertyNames(proto); for (var i = 0; i < names.length; i++) { var desc = Object.getOwnPropertyDescriptor(proto, names[i]); Object.defineProperty(subClass.prototype, names[i], desc); } // 设置子类的构造函数时自身的构造函数,以防止因为设置原型而覆盖构造函数(防止) subClass.prototype.constructor = subClass; // 给子类的原型中添加一个属性,可以快捷的调用到父类的原型方法 subClass.prototype.superClass = supClass.prototype; // 如果父类的原型构造函数指向的不是父类构造函数,重新指向(防止) if (supClass.prototype.constructor !== supClass) { supClass.prototype.constructor = supClass; } } function Ball(_a) { this.superClass.constructor.call(this, _a); } Ball.prototype.play = function () { //执行超类的play方法 this.superClass.play.call(this); } Object.defineProperty(Ball.prototype, "d", { value: 20 }) extend(Ball, Box); var b = new Ball(10); console.log(b);
- 版本二(ES5)
function Box(_a) { this.a = _a; this.play(); } // 添加一个实例化属性和方法 Box.prototype.a = 10; Box.prototype.play = function () { } // 添加一个静态属性和方法 Box.b = 20; Box.run = function () { } Function.prototype.extend = function (supClass) { function F() { } F.prototype = supClass.prototype; var proto = this.prototype; this.prototype = new F(); var names = Object.getOwnPropertyNames(proto); for (var i = 0; i < names.length; i++) { var desc = Object.getOwnPropertyDescriptor(proto, names[i]); Object.defineProperty(this.prototype, names[i], desc); } this.prototype.constructor = this; this.prototype.superClass = supClass.prototype; if (supClass.prototype.constructor !== supClass) { supClass.prototype.constructor = supClass; } } function Ball(_a) { this.superClass.constructor.call(this, _a); } Ball.prototype.play = function () { this.superClass.play.call(this); } Object.defineProperty(Ball.prototype, "d", { value: 20 }) Ball.extend(Box); var b = new Ball(10); console.log(b);
还有其他混入式继承等,博主就不一一罗列了~~~