JavaScript继承
1.设计思想
js没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承。
1.1 历史
设计者仿效java使用new来从原型对象中生成实例对象,但是区别是new后面的不是类,而是构造函数。
说明
js中的构造函数:
- 函数体内部使用了this关键字,代表要生成的对象实例
- 生成对象的时候,需要用new,调用构造函数
1.2 new 运算符的缺点
用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。即同一个属性在一个实例对象中修改了其他对象实例不会因此而改变。
1.3 prototype属性的引入
这个属性包含一个对象,简称prototype对象,所有实例对象需要共享的属性和方法在此对象中。
2. 封装
2.1 生成对象的原始模式
var cat1 = {};
cat1.name = "大毛";
cat1.color = "黄色";
2.2 原始模式的改进
function Cat(name, color){
return {
name:name,
color:color
}
}
//实例对象
var cat1 = Cat("一毛","黄色");
var cat2 = Cat("二毛","黑色");
cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。
2.3 构造函数模式
function Cat(name,color){
this.name = name;
this.color = color;
}
var cat1 = new Cat("一毛","黄色");
var cat2 = new Cat("二毛","黑色");
2.4 构造函数模式的问题
若是存在一些不变的属性,则当new多个实例对象时则会造成内存的浪费,所以出现了prototype模式。
2.5 Prototype模式
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。
3. 构造函数的继承
3.1 构造函数绑定
使用call或者apply方法,将父对象的构造函数绑定在子对象上
function Animal(){ //父对象
this.species = "动物";
}
function Cat(name,color){
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
3.2 prototype模式
使用prototype属性,如果prototype对象,指向一个Animal实例,那么所有实例就都继承了Animal.
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("一毛","黄色");
3.3 直接继承prototype
function Animal(){ }
Animal.prototype.species = "动物";
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
3.4 利用空对象做中介
var F = fucntion(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
则可以将此类型的方法封装为一个函数
function extend(Child, Parent){
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是"向上"、"上一层"。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
3.5 拷贝继承
function extend2(child, Parent){
var p = Parent.prototype;
var c = Child.prototype;
for(var i in p){
c[i] = p[i];
}
c.uber = p;
}
4. 非构造函数的继承
4.1 何为非构造函数的继承
即普通对象,不是构造函数的继承
var Chinese = {
nation:"中国"
};
var Doctor = {
career:"医生"
}
4.2 object()方法
function object(o){
function F(){}
F.prototype = o;
return new F();
}
然后使用此object方法
var Doctor = object(Chniese);
Doctor.career = "医生";
4.3 浅拷贝
把父对象的属性,全部拷贝给子对象,也能实现继承。
function extendCope(p){
var c ={};
for(var i in p){
c[i] = p[i];
}
c.uber =p;
return c;
}
4.4 深拷贝
所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。
function deepCope(p,c){
var c = c || {};
for(var i in p){
if(typeof p[i] === "object"){
c[i] = (p[i].contructor === Array)?[]:{};
deepCope(p[i],c[i]);
}else{
c[i]= p[i];
}
}
return c;
}
jQuery就是使用这种继承方法。