构造函数、原型、实例、原型链之间的联系
描述:每个构造函数都有一个原型对象;
每个原型对象都有一个指针,指向构造函数;
每个实例对象都有一个内部指针,指向原型对象;
若此时的原型对象是另一个类型的实例,此时的原型对象将包含一个指针,指向另一个原型对象,
相应的另一个原型对象中也包含一个指针,指向另一个构造函数;若此时另一个原型对象又是另另一个
类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条,俗称原型链。
继承
实现:继承主要通过原型链来实现
基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法
本质:重写原型对象
继承的实现方式:
1.借用构造函数实现继承
基本思想:在子类中调用父类的构造函数,这样在创建子类实例对象时就会执行父类的构造函数中定义的所有属性的初始化,结果每个子类实例对象都会具有父类属性的一个副本。
function Animal () {
this.name = "animal";
this.colors = ["blue", "green"];
this.sayName = function () {
console.log(this.name);
}
}
Animal.prototype.call = function () {
console.log("Animal call...");
}
function Cat() {
Animal.call(this);
}
var cat1 = new Cat();
var cat2 = new Cat();
cat1.colors.push("black");
console.log(cat1.colors); //"blue", "green", "black"
console.log(cat2.colors); //"blue", "green"
cat1.sayName(); //"animal"
cat1.call(); //cat1.call is not a function.
缺点:方法都在构造函数中定义,函数的复用就无从谈起;
父类原型中的方法对于子类而言是不可见的,结果导致所有属性的继承只能使用构造函数模式。
2.借助原型链实现继承
function Animal () {
this.name = "animal";
this.colors = ["blue", "green"];
}
Animal.prototype.call = function () {
console.log("Animal call...");
}
function Cat() {
}
Cat.prototype = new Animal (); //inherit
Cat.prototype.jump = function () {
console.log("cat jump...");
}
var cat1 = new Cat();
var cat2 = new Cat();
cat1.colors.push("black");
console.log(cat1.colors); //"blue", "green", "black"
console.log(cat2.colors); //"blue", "green","black"
cat1.call(); //"Animal call..."
cat1.jump(); //"cat jump..."
缺点:最主要的问题是包含引用类型值的原型,包含引用类型的原型属性会被所有的实例对象共享,这就是为什么要在构造函数而不是原型对象中定义属性的原因;
在通过原型实现继承时,原型会变成另一个类型的实例,于是原先的实例属性就变成了现在的原型属性了。
3.构造函数和原型链组合方式实现继承
基本思想:使用构造函数模式实现对实例属性的继承,保证了每个实例对象都有自己的属性;
使用原型链模式实现对原型属性和方法的继承,实现了函数的复用。
function Animal () {
this.name = "animal";
this.colors = ["blue", "green"];
}
Animal.prototype.call = function () {
console.log("Animal call...");
}
function Cat() {
Animal.call(this);
}
Cat.prototype = new Animal (); //inherit
Cat.prototype.jump = function () {
console.log("cat jump...");
}
var cat1 = new Cat();
var cat2 = new Cat();
cat1.colors.push("black");
console.log(cat1.colors); //"blue", "green", "black"
console.log(cat2.colors); //"blue", "green"
console.log(cat1 instanceof Cat, cat1 instanceof Animal); //true true
console.log(cat1.constructor); //Animal
缺点:任何情况下,父类的构造函数执行了两次,不必为了指定子类型的原型而调用父类的构造函数,我们所需要的无非就是父类原型的一个副本而已;
无法区分一个实例对象是子类实例还是父类实例。
4.构造函数和原型链组合方式实现继承优化(1)
function Animal () {
this.name = "animal";
this.colors = ["blue", "green"];
}
Animal.prototype.call = function () {
console.log("Animal call...");
}
function Cat() {
Animal.call(this);
}
Cat.prototype = new Animal (); //inherit
Cat.prototype.jump = function () {
console.log("cat jump...");
}
var cat1 = new Cat();
var cat2 = new Cat();
cat1.colors.push("black");
console.log(cat1.colors); //"blue", "green", "black"
console.log(cat2.colors); //"blue", "green"
console.log(cat1 instanceof Cat, cat1 instanceof Animal); //true true
console.log(cat1.constructor); //Animal
描述:解决了两次调用父类构造函数的问题,但是仍然无法区分一个实例对象是子类实例还是父类实例。
5.构造函数和原型链组合方式实现继承优化(2)
function Animal () {
this.name = "animal";
this.colors = ["blue", "green"];
this.sayName = function () {
console.log(this.name);
}
}
Animal.prototype.call = function () {
console.log("Animal call...");
}
function Cat() {
Animal.call(this);
}
Cat.prototype = Object.create(Animal.prototype); //inherit
Cat.prototype.constructor = Cat;
Cat.prototype.jump = function () {
console.log("cat jump...");
}
var cat1 = new Cat();
var cat2 = new Cat();
cat1.colors.push("black");
console.log(cat1.colors); //"blue", "green", "black"
console.log(cat2.colors); //"blue", "green"
console.log(cat1 instanceof Cat, cat1 instanceof Animal); //true true
console.log(cat1.constructor); // Cat
描述:目前最完美的继承实现方式;
-
继承
实现:继承主要通过原型链来实现
基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法
本质:重写原型对象