Javascript继承
在 JavaScript 中,继承是一种通过一个对象(子类)来获取另一个对象(父类)属性和方法的方式。JavaScript 中的继承主要有两种方式:原型继承和构造函数继承。
原型继承
function Parent (name) {
this.arr = [1, 2, 3];
this.name = name;
}
Parent.prototype.getName = function () {
console.log(this.arr);
}
function Child () {}
Child.prototype = new Parent('Tom'); // 改变Child的原型
var child1 = new Child();
var child2 = new Child();
child1.arr.push(4); // 改变引用属性值
child1.getName(); // [ 1, 2, 3, 4 ]
child2.getName(); // [ 1, 2, 3, 4 ]
通过改变Child的prototype指向,使得Child继承了Parent的实例属性。但是:
- 引用类型的属性被所有实例共享,即一改接改。
- 创建Child的实例时,不能向Parent传参,因为在修改Chil原型时Parent的属性值就已经确定。
构造函数继承(经典继承)
function Parent(name) {
this.arr = [1, 2, 3, 4];
this.name = name;
}
Parent.prototype.sayName = function() {
console.log(this.name);
}
function Child(name) {
Parent.call(this, name); // 在Child内调用构造函数Parent
}
const child1 = new Child('Tom');
const child2 = new Child('Sam');
child1.arr.push(5);
console.log(child2); // { arr: [ 1, 2, 3, 4, 5 ], name: 'Tom' }
console.log(child1); // { arr: [ 1, 2, 3, 4 ], name: 'Sam' }
child1.sayName(); // sayName is undefined
在Child构造函数内调用Parent构造函数,将Parent的实例属性指向Child实例(需要了解new关键字原理和构造函数执行原理)。
构造函数原理
function Person(name, age) {
// 1.函数体最前端隐式创建空对象this={}
this.name = name;
// this = {
// name : name
// }
this.age = age;
// this = {
// name : name,
// age : age
// }
// 2.将name属性添加进去
return this;
// 3.隐式抛出this,可以自行return其它结果
}
new执行原理
function myNew(Func, ...args) {
// 创建原型为Func.prototype的对象
let obj = Object.create(Func.prototype);
// 改变构造函数this指向obj,将属性都挂载到obj上
const result= Func.apply(obj, args);
// 构造函数可自定义返回结果,但必须是对象
return typeof result === 'object' ? result : obj;
}
解决了原型继承的缺点
- 避免了引用类型的属性被所有实例共享
- 可以在 Child 中向 Parent 传参
但是并不能继承Parent的方法,方法都在Child构造函数中定义。
组合继承
原型链继承和经典继承双剑合璧,结合了两种方法的优点。
function Parent (name) {
this.name = name;
this.arr = [1, 2, 3];
}
Parent.prototype.getArr = function () {
console.log(this.arr)
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const child1 = new Child('Tom', 25);
const child2 = new Child('Sam', 22);
child1.arr.push(4);
child1.getArr(); // [ 1, 2, 3, 4 ]
child2.getArr(); // [ 1, 2, 3 ]
console.log(child1.name); // 'Tom'
console.log(child2.name); // 'Sam'
圣杯模式
const inherit = (function () {
const F = function() {};
return function (target, origin) {
F.prototype = origin.prototype;
target.prototype = new F();
target.prototype.constructor = target;
}
}());
利用闭包私有化标量F,通过F引用被继承者的原型,通过实例化生成新的对象作为继承者的原型,并将原型的构造函数指向继承者本身。
类继承
通过es6提供class类实现的继承。
class Parent {
constructor(name) {
this.arr = [1, 2, 3];
this.name = name;
}
getArr() {
console.log(this.arr);
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
sayName() {
console.log(this.age);
}
}
const child1 = new Child('Tom', 25);
const child2 = new Child('Sam', 22);
child1.arr.push(4);
child1.getArr(); // [ 1, 2, 3, 4 ]
child2.getArr(); // [ 1, 2, 3 ]
console.log(child1.name); // 'Tom'
console.log(child2.name); // 'Sam'