JavaScript继承
一、ES5通过修改原型链实现继承
https://wangdoc.com/javascript/oop/prototype.html
1.构造函数的继承——子类整体继承父类
让一个构造函数继承另一个构造函数。
- 第一步,子类继承父类的实例
- 第二步,子类继承父类的原型
第一步:在子类的构造函数中,调用父类的构造函数
1 function Sub(value) {
2 Super.call(this);
3 this.prop = value;
4 }
上面代码中,Sub
是子类的构造函数,this
是子类的实例。在实例上调用父类的构造函数Super
,就会让子类实例具有父类实例的属性。
第二步:让子类的原型指向父类的原型(这样子类就可以继承父类的原型)
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.method = '...';
上面代码中,Sub.prototype
是子类的原型,要将它赋值为Object.create(Super.prototype)
,而不是直接等于Super.prototype
。否则后面两行对Sub.prototype
的操作,会连父类的原型Super.prototype
一起修改掉。
举例来说,下面是一个Shape
构造函数。
function Shape() { this.x = 0; this.y = 0; } Shape.prototype.move = function (x, y) { this.x += x; this.y += y; console.info('Shape moved.'); };
我们需要让Rectangle
构造函数继承Shape
。
// 第一步,子类继承父类的实例 function Rectangle() { Shape.call(this); // 调用父类构造函数 } // 另一种写法 function Rectangle() { this.base = Shape; this.base(); } // 第二步,子类继承父类的原型 Rectangle.prototype = Object.create(Shape.prototype); Rectangle.prototype.constructor = Rectangle;
采用这样的写法以后,instanceof
运算符会对子类和父类的构造函数,都返回true
。
var rect = new Rectangle(); rect instanceof Rectangle // true rect instanceof Shape // true
2.单个方法的继承
上面代码中,子类是整体继承父类。有时只需要单个方法的继承,这时可以采用下面的写法。
ClassB.prototype.print = function() { ClassA.prototype.print.call(this); // some code }
上面代码中,子类B
的print
方法先调用父类A
的print
方法,再部署自己的代码。这就等于继承了父类A
的print
方法。
3.多重继承
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。
1 function M1() { 2 this.hello = 'hello'; 3 } 4 5 function M2() { 6 this.world = 'world'; 7 } 8 9 function S() { 10 M1.call(this); 11 M2.call(this); 12 } 13 14 // 继承 M1 15 S.prototype = Object.create(M1.prototype); 16 // 继承链上加入 M2 17 Object.assign(S.prototype, M2.prototype); 18 19 // 指定构造函数 20 S.prototype.constructor = S; 21 22 var s = new S(); 23 s.hello // 'hello' 24 s.world // 'world'
上面代码中,子类S
同时继承了父类M1
和M2
。这种模式又称为 Mixin(混入)。
Object.assign 会把 M2 原型上的函数拷贝到 S 原型上,使 S 的所有实例都可用 M2的方法。
Object.create() 创建实例对象实现继承父类的属性和方法,但还没有继承父类原型
https://wangdoc.com/javascript/oop/new.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()
方法。
1 var person1 = { 2 name: '张三', 3 age: 38, 4 greeting: function() { 5 console.log('Hi! I\'m ' + this.name + '.'); 6 } 7 }; 8 9 var person2 = Object.create(person1); 10 11 person2.name // 张三 12 person2.greeting() // Hi! I'm 张三.
上面代码中,对象person1
是person2
的模板,后者继承了前者的属性和方法。
super关键字
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/super
super关键字用于访问和调用一个对象的父对象上的函数。
语法
super([arguments]); // 调用 父对象/父类 的构造函数 super.functionOnParent([arguments]); // 调用 父对象/父类 上的方法
描述:在构造函数中使用时,super
关键字将单独出现,并且必须在使用this
关键字之前使用。super
关键字也可以用来调用父对象上的函数。
call()
方法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
call()
方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数。
返回值:使用调用者提供的 this
值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined
。
使用 call
方法调用父构造函数
在一个子构造函数中,你可以通过调用父构造函数的 call
方法来实现继承,下例中,使用 Food
和 Toy
构造函数创建的对象实例都会拥有在 Product
构造函数中添加的 name
属性和 price
属性,但 category
属性是在各自的构造函数中定义的。
1 function Product(name, price) { 2 this.name = name; 3 this.price = price; 4 } 5 6 function Food(name, price) { 7 Product.call(this, name, price); 8 this.category = 'food'; 9 } 10 11 function Toy(name, price) { 12 Product.call(this, name, price); 13 this.category = 'toy'; 14 } 15 16 var cheese = new Food('feta', 5); 17 var fun = new Toy('robot', 40);
二、ES6中Class 可以通过extends
关键字实现继承
http://es6.ruanyifeng.com/#docs/class-extends
Class 可以通过extends
关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
class Point {
}
class ColorPoint extends Point {
}
上面代码定义了一个ColorPoint
类,该类通过extends
关键字,继承了Point
类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point
类。下面,我们在ColorPoint
内部加上代码。
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toString() } }
上面代码中,constructor
方法和toString
方法之中,都出现了super
关键字,它在这里表示父类的构造函数,用来新建父类的this
对象。
子类必须在constructor
方法中调用super
方法,否则新建实例时会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象。