梳理 JavaScript 中的继承问题,则不得不先理解 Js 中的原型链,因为 ECMAScript 主要是基于原型链实现继承的。
在 Js 中,每个函数都有一个 prototype 属性,其指向该函数的原型对象。而函数的原型对象中,有一个 constructor 属性,指回向该函数。当函数被当作构造函数,使用 new 运算符生成实例时,在生成的实例对象中有一个内部属性 __proto__ 属性,该属性也指向函数的原型对象。在原型对象上有 __proto__ 指向原型对象的原型对象,依次传递,直到指向 Object.prototype 对象为止,即构成了原型链。如下图所示:
function Animal(name) {
this.name = name;
this.colors = ['black', 'red', 'pink'];
}
Animal.prototype.run = function (){
console.log('running');
}
function Cat(age) {
this.age = age;
}
Cat.prototype = new Animal('cat'); // 实现原型链继承
var cat1 = new Cat(1);
console.log(cat1); // Cat {
// age: 1,
// __proto__: Animal {
// colors: (4) ["black", "red", "pink", "blue"],
// name: "cat",
// __proto__: Object
// }}
console.log(cat1 instanceof Cat); // true
console.log(cat1 instanceof Animal); // true
cat1.run(); // running
console.log(cat1.colors); // ['black', 'red', 'pink']
cat1.colors.push('blue');
var cat2 = new Cat(2);
console.log(cat2.colors); // ['black', 'red', 'pink','blue']
// Cat的所有实例会共享 原型对象上的属性,其中一个实例的修改,会影响到其他实例。
function Animal(name) {
this.name = name;
this.colors = ['black', 'red', 'pink'];
}
Animal.prototype.run = function (){
console.log('running');
}
function Cat(age) {
Animal.apply(this, ['cat']); // 借助构造函数实现继承
this.age = age;
}
var cat1 = new Cat(1);
console.log(cat1); // Cat {
// age: 1,
// colors:(4) ["black", "red", "pink", "blue"],
// name: "cat",
// __proto__: Object
// }
console.log(cat1 instanceof Cat); // true
console.log(cat1 instanceof Animal); // false
cat1.colors.push('blue');
console.log(cat1.colors); // ['black', 'red', 'pink','blue']
var cat2 = new Cat(2);
console.log(cat2.colors); // ['black', 'red', 'pink']
cat1.run(); // Uncaught TypeError: cat1.run is not a function
// 无法使用在原型对象上的函数,即无法复用函数
function Animal(name) {
this.name = name;
this.colors = ['black', 'red', 'pink'];
}
Animal.prototype.run = function (){
console.log('running');
}
function Cat(age) {
Animal.apply(this); // 借助构造函数实现 实例属性的继承
this.age = age;
}
Cat.prototype = new Animal('cat'); // 借助原型链实现 原型属性和方法的继承
Cat.prototype.constructor = Cat; // 设置原型对象的 constructor 指向
var cat1 = new Cat(1);
console.log(cat1); // Cat {
// name: undefined,
// colors: ["black", "red", "pink"],
// age: 1,
// __proto__: Animal{
// colors: (3) ["black", "red", "pink"],
// name: "cat",
// __proto__: {
// run: ? ()
// ...
// }
// }
// }
console.log(cat1 instanceof Cat); // true
console.log(cat1 instanceof Animal); // true
cat1.run(); // running
// 无论什么情况下,都会调用两次超类型构造函数
// Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
// 即借助原型基于已有的对象创建新对象,同时不必因此创建自定义类型。
// 从本质上讲,object 函数对传入的对象进行了一次浅复制。
Object.create()
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
// 优化组合继承
function Animal(name) {
this.name = name;
this.colors = ['black', 'red', 'pink'];
}
Animal.prototype.run = function (){
console.log('running');
}
function Cat(age) {
Animal.apply(this); // 借助构造函数实现 实例属性的继承
this.age = age;
}
Cat.prototype = Object.create(Animal.prototype); // 浅拷贝 原型属性方法
Cat.prototype.constructor = Cat;
var cat1 = new Cat(1);
console.log(cat1);
console.log(cat1 instanceof Cat); // true
console.log(cat1 instanceof Animal); // true
cat1.run(); // running
console.log(cat1.colors); // ['black', 'red', 'pink']
cat1.colors.push('blue');
console.log(cat1.colors); // ['black', 'red', 'pink', 'blue']
var cat2 = new Cat(2);
console.log(cat2.colors); // ['black', 'red', 'pink']
class Animal {
constructor (name) {
this.name = name;
}
run() {
console.log('running');
}
}
class Cat extends Animal{
constructor (name, age) {
super(name);
this.age = age;
}
}
let cat1 = new Cat('cat', 1);
cat1.run(); // running
console.log(cat1);
原文:https://www.cnblogs.com/horizon-jens/p/11937290.html