2.03.07 继承
1.简单了解 封装性、继承性、多态性
封装:把客观存在的数据添加到对象中,即,把构造函数的属性和方法添加到实例对象中 继承:B对象可以拥有A对象的属性和方法,在js中就叫做B对象继承A对象 多态:同一构造函数再不同的场景下,可以有不同的表现形式,即,同一构造函数传进的参数不一样,构造出来不同的对象
2.对象之间"继承"的几种方法
1.构造函数绑定
使用call,apply,bind方法,将父对象的构造函数绑定在子对象上
function Animal(){
this.species = "动物";
}
function Cat(name,color){
Animal.apply(this);
Animal.bind(this)();
//Animal.call(this);
this.name = name;
this.color = color;
}
var cat1 = new Cat("大毛","黄色");
console.log(cat1.species); //动物
// 构造函数原型里方法的继承,需要这样:
Animal.prototype.say=function(){
console.log("Woo woo!");
}
console.log(cat1);
// cat1.say(); //控制台报错:Uncaught TypeError: cat1.say is not a function
console.log(cat1.say);//undefined
Cat.prototype.say=Animal.prototype.say;
cat1.say(); //Woo woo!
console.log(Cat.prototype.say===Animal.prototype.say);//true
//证明Cat.prototype.say与Animal.prototype.say指向同一个地址
2.prototype模式
使用prototype属性。如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。
function Animal(){
this.species = "动物";
};
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype = new Animal();
var cat1 = new Cat("大毛","黄色");
Cat.prototype.constructor = Cat;
console.log(cat1.species); // 动物
这种做法必须修改Cat的原型里面的constructor:Cat.prototype.constructor = Cat;这样才不会是的继承连紊乱 若不进行修改,那么实例对象cat1.constructor也指向Animal,这不符合我们的原意,原意是cat1.constructor指向Cat
3.直接继承prototype(不使用)
第三种方法是对第二种方法的改进。由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。
function Animal(){ }
Animal.prototype.species = "动物";
function Animal(){}
Animal.prototype.species = "动物";
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
console.log(cat1.species); // 动物
与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。 Cat.prototype.constructor = Cat会使得Animal.prototype对象的constructor属性也改掉了!
console.log(Animal.prototype.constructor); // Cat
4.利用空对象作为中介(修改点3的缺点)
由于"直接继承prototype"存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。
function Animal(){
this.species = "动物";
};
function Cat(name,color){
this.name = name;
this.color = color;
}
var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
console.log(cat1.species); // 动物
console.log(F.prototype.constructor); //Animal
console.log(Animal.prototype.constructor); // Animal
F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。 我们将上面的方法,封装成一个函数,便于使用:
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
extend(Cat,Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
这个extend函数,就是YUI库如何实现继承的方法。 另外,说明一点,函数体最后一行:Child.uber = Parent.prototype; 意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是"向上"、“上一层”。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
5.拷贝继承
我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?这样我们就有了第五种方法。
function Animal(){
this.species = "动物";
}
Animal.prototype.species = "动物";
function Cat(name,color){
this.name = name;
this.color = color;
}
for (var i in Animal.prototype) {
Cat.prototype[i] = Animal.prototype[i];
}
var cat1 = new Cat("大毛","黄色");
console.log(cat1.constructor); //Cat
console.log(cat1.species); //动物