fucntion Person(name,sex){    //父类

  this.name=name;

  this.sex=sex;

}

Person.prototype.showName=function(){

  alert(this.name);

}

function Star(){}    //子类

现在有一个新的对象 Star,去继承Person,并给Star添加自己的新属性job,和新方法 sing。

 

对象的属性继承和方法继承。

属性继承:通过父类的构造函数继承(使用call函数);

//属性继承:调用父类的call

fucntion Star(name,sex,job){

    // ❌   Person(name,sex)    //如果直接用Person(),因为函数Person()直接被调用,所以Person函数中的this指向的是window。

  Person.call(this,name,sex)    //继承Person的属性,使用call。使用call函数,将this指向为新创建的对象p2。 

  this.job=job;

}

但是方法继承的时候我们不能直接使用原型赋值

//❌ Star.prototype=Person.prototype;  //为对象赋给对象,为对象的引用,所以这两个对象指向的是同一存储空间,任意一个对象的修改都会引起另一个的变化。因此,我们要将对象的赋值改变为基本类型的赋值。

继承:拷贝继承、类式继承、原型继承

1、拷贝继承(for in)  ( jquery的extend函数也是拷贝继承。$.extend(),当把第一个参数设置为true时,为深拷贝)

封装extend函数:(将obj2拷贝给obj1)

function extend(obj1,obj2){
  for(var attr in obj2){
    obj1[attr]=obj2[attr];
  }
}

 

使用方法:extend(Child.prototype,Parent.prototype);   

这种拷贝写法适合所有属性都是基本类型的对象。当某个属性为一个数组或是一个对象时,这样的拷贝无法实现数组、对象的真正拷贝。也就是这种写法实际上是浅拷贝。

若想实现深拷贝,只要递归调用浅拷贝方法即可:

function extend(obj1,obj2){
  for(var attr in obj2){
       if (typeof obj2[i] === 'object') {  //当某个属性的值为一个对象时,需要递归调用extend
      obj1[i] = (obj2[i].constructor ===  Array) ? [] : {};  //创建空的数组或对象
      extend(obj1[i], obj2[i]);
    } else {
      obj1[attr]=obj2[attr];
    }  
  }
}    

 

在这个例子中:extend(Star.prototype,Person.prototype);

Star.prototype.sing=function(){

  alert('sing a song');

}

2、类式继承(构造函数)

JS中没有类,但是我们可以把JS中的构造函数看作类。

Star.prototype=new Person();   //用父类的实例,赋值给子类的原型。它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。

//这时,Star.Prototype.constructor=Person,这显然会导致继承链的紊乱,因此我们必须手动纠正,将Star.prototype对象的constructor值改为Star。

Star.Prototype.constructor=Star;  

另外一个问题:当我们创建两个实例:

s1=new Star();

s2=new Star(); //测试会发现s1和s2之间的属性不是独立的,因为它们的继承都来自Person。当s1的属性变化时,s2也会受到影响。为了避免这个问题,我们需要属性和方法分开继承。

我们需要一个空对象,作为中间桥梁:

var F=funtion(){};

F.prototype=Person.prototype;

所以最终的类式继承,需要4句话:

var F=funtion(){};
F.prototype=Person.prototype;  //只会复制方法,属性并没有复制。
Star.prototype=new F(); //所以这时候Star只是接收了父类的方法,而没有接收父类的属性。要想继承父类的属性,仍然使用Person.call(this)的方法。this的指向不同,因此s1和s2的属性不会相互干扰。
Star.prototype.constructor=Star;

 

3、原型继承(借助prototype)

var A={

  name:'a';

}

var B=cloneObj(A);

function cloneObj(obj){

  var F=function(){};

  F.prototype=obj;

  return new F();

}

B继承了A。且B和A的属性相互独立。

 

总结:

(1)拷贝继承:通用

(2)类式继承:适合通过new构造函数

(3)原型继承:适合无new的对象