在Javascipt中实现对象的继承,一共有6种方法。
实现继承,需要一个父类:
// 父类
function Animal(fu_shili_shu) {
// 父类实例属性
this.fu_shili_shu = fu_shili_shu;
// 父类实例方法
this.fu_shili_fun = function () {
console.log(`父类实例方法-fu_shili_fun`);
};
}
// 父类原型属性
Animal.prototype.fu_yuan_shu = "父类原型属性-fu_yuan_shu";
// 父类原型方法
Animal.prototype.fu_yuan_fun = function () {
console.log("父类原型方法-fu_yuan_fun");
};
//一、原型链继承
// 缺点:
// 父类所有的实例和方法都可以被子类的实例访问
// 1.子类构造函数
function Cat(zi_shili_shu) {
// 子类实例属性
this.zi_shili_shu = zi_shili_shu;
// 子类实例方法
this.zi_shili_fun = function () {
console.log("子类实例方法-zi_shili_fun");
};
}
// 2.子类原型赋值为父类实例,且修复子类prototype.constructor指向
Cat.prototype = new Animal("父类实例属性-zi_shili_shu");
Cat.prototype.constructor = Cat;
// 3.定义子类的原型属性和原型方法
Cat.prototype.zi_yuan_shu = "子类原型属性-zi_yuan_shu";
Cat.prototype.zi_yuan_fun = function () {
console.log("子类原型方法-zi_yuan_fun");
};
// 创建子类实例
let cat1 = new Cat("子类实例属性-zi_shili_shu");
// cat1实例拥有子类、父类的所有的属性和方法
// 二、构造继承
// 缺点:
// 不能继承父类的原型属性和原型方法
// 若父类的实例属性和实例方法需要通过传参构建,子类也不能继承
// 1.子类构造函数
function Cat(zi_shili_shu) {
Animal.call(this);
this.zi_shili_shu = zi_shili_shu;
this.zi_shili_fun = function () {
console.log("子类实例方法-zi_shili_fun");
};
}
// 2.子类原型属性和原型方法
Cat.prototype.zi_yuan_shu = "子类原型方法-zi_yuan_shu";
Cat.prototype.zi_yuan_fun = "子类原型方法-zi_yuan_fun";
let cat1 = new Cat("子类实例属性-zi_shili_shu");
console.log(cat1);
// 三、实例继承
// 缺点:
// 不能写子类的原型属性和原型方法,子类上的原型属性和原型方法是无效的
function Cat(zi_shili_shu) {
// 1.实例化父类
let instance = new Animal("fu_shili_shu");
// 子类实例属性
instance.zi_shili_shu = zi_shili_shu;
// 子类实例方法
instance.zi_shili_fun = function () {
console.log("zi_shili_fun");
};
// 3.返回实例的父类
return instance;
}
// 子类原型属性和方法(无效)
Cat.prototype.zi_yuan_shu = "zi_yuan_shu";
Cat.prototype.zi_yuan_fun = function () {
console.log("zi_yuan_fun");
};
let cat1 = new Cat("zi_shili_shu");
console.log(cat1);
// 四、拷贝继承
// 缺点:
// 子类无法继承父类不可枚举的属性
function Cat(zi_shili_shu) {
// 实例化父类
let animal = new Animal("fu_shili_shi");
// 遍历父类实例的属性
for (let p in animal) {
Cat.prototype[p] = animal[p];
}
// 子类实例属性的方法
this.zi_shili_shu = zi_shili_shu;
this.zi_shili_fun = function () {
console.log("zi_shili_fun");
};
}
// 子类原型属性和原型方法
Cat.prototype.zi_yuan_shu = "zi_yuan_shu";
Cat.prototype.zi_yuan_fun = function () {
console.log("zi_yuan_fun");
};
let cat1 = new Cat("zi_shili_shu");
console.log(cat1);
// 五、组合继承(构造继承+原型链继承)
// 缺点:
// 子类不能继承父类通过传参创建的实例属性
function Dog(zi_shili_shu) {
// 调用父类的构造函数
Animal.call(this);
// 子类实例属性和实例方法
this.zi_shili_shu = zi_shili_shu; // (无效)
this.zi_shili_fun = function () {
console.log("zi_shili_fun");
};
}
// 设置子类原型为父类实例,且修复prototype.constructor
Dog.prototype = new Animal("fu_shili_shu");
Dog.prototype.constructor = Dog;
// 子类原型属性和原型方法
Dog.prototype.zi_yuan_shu = "zi_yuan_shu";
Dog.prototype.zi_yuan_fun = function () {
console.log("zi_yuan_fun");
};
let dog1 = new Dog("zi_shili_shu");
console.log(dog1);
// 六、寄生组合继承(组合继承优化)
// 缺点:
// 不能继承父类通过传参创建的实例属性
function Cat(zi_shili_shu) {
// 调用父类构造函数
Animal.call(this);
// 子类实例属性和实例方法
this.zi_shili_shu = zi_shili_shu; // (无效)
this.zi_shili_fun = function () {
console.log("zi_shili_fun");
};
}
(function () {
// 子类原型设置为父类没有实例属性和实例方法的实例
let Super = function () {};
Super.prototype = Animal.prototype;
Cat.prototype = new Super();
// 修复子类prototype.constructor
Cat.prototype.constructor = Cat;
})();
// 子类原型属性和原型方法
Cat.prototype.zi_yuan_shu = "zi_yuan_shu";
Cat.prototype.zi_yuan_fun = function () {
console.log("zi_yuan_fun");
};
let cat1 = new Cat("zi_shili_shu");
console.log(cat1);
综合上面几种方法的对比,可以大概理出Javascript实现对象继承的整体演变过程的思路:
其思路演变大概有2条线:
1.原型链继承 => 构造继承 => 组合继承(原型链+构造继承) => 寄生组合继承(组合继承优化)
2.实例继承 => 拷贝继承
上述几种继承方式,各有优劣,但是只有原型链继承可以完美的实现子类继承父类的所有属性的方法,这也印证了Javascript语言是通过原型来实现对象的思想是吻合的。