JavaScript中的继承

继承:

方式#1——原型链:

1.1通过原型来实现继承关系链:

JavaScript中的每个函数都有一个指向某一对象的prototype属性,该函数被new操作符调用时会创建并返回一个对象,并且该对象中会有一个指向其原型对象的链接(__proto__)。通过此链接,可以调用相关原型对象的方法和属性。

而原型对象本身也指向了其原型,所以就形成了一条原型链。

原型链的末端是Object对象。

每个对象都能访问其原型链上的属性和方法。

function Animal(){

       this.name = 'animal';

       this.toString = function(){

              return this.name;

       }

}

function Bird(){

       this.name = 'bird';

}

function Parrot(weight,speed){

       this.name = 'parrot';

       this.weight = weight;

       this.speed = speed;

       this.getPoint = function(){

              return this.weight * this.speed;

       }

}

Bird.prototype = new Animal();

Parrot.prototype = new Bird();

Bird.prototype.constructor = Bird;

Parrot.prototype.constructor = Parrot;

 

var thePoint = new Parrot(20,20);

thePoint.getPoint();

thePoint.toString();//沿着作用域往上找该方法

thePoint.constructor === Parrot

true

Parrot.prototype.isPrototypeOf(thePoint)

true

Animal.prototype.isPrototypeOf(thePoint)

true

var otherParrot = new Bird();

otherParrot.constructor === Bird;

 

 

1.2 针对不变共有属性,可归类到原型中去以提高效率,就不用每次用new的时候都创建一个新属性。

function Animal(){}

Animal.prototype.name = 'Animal';

将1.1中的共有属性添加到原型对象中去。

//constructor

function Animal(){}

//augment prototype

Animal.prototype.name = 'Animal';

Animal.prototype.toString = function(){

       return this.name;

}

//another constructor

function Bird(){}

//take care of inheritance

Bird.prototype = new Animal();

Bird.prototype.constructor = Bird;

//augment prototype

Bird.prototype.name = 'Bird';

 

//注意,为了避免新扩展属性被原来的属性覆盖,要先进行相关的继承关系构建。

//对于Parrot的weight,speed属性是会随着不同的Parrot改变的,所以写在构造函数里。

function Parrot(weight,speed){

       this.weight = weight;

       this.speed = speed;

}

//take care of inheritance

Parrot.prototype = new Bird();

Parrot.prototype.constructor = Parrot;

//augment prototype

Parrot.prototype.name = 'Parrot';

Parrot.prototype.getPoint = function(){

       return this.weight * this.speed;

}

//调用

var parrot=new Parrot(33,33);

parrot.getPoint();

parrot.hasOwnProperty('weight');//true

parrot.hasOwnProperty('name');//false

 

 

方式#2:只从原型继承法

改善法:

*不单独为继承关系创建新对象

*尽量减少运行时的方法搜索(例如toString())

//constructor

function Animal(){}

//augment prototype

Animal.prototype.name = 'Animal';

Animal.prototype.toString = function(){

       return this.name;

}

//another constructor

function Bird(){}

//take care of inheritance

Bird.prototype = Animal.prototype;

Bird.prototype.constructor = Bird;

//augment prototype

Bird.prototype.name = 'Bird';

 

function Parrot(weight,speed){

       this.weight = weight;

       this.speed = speed;

}

//take care of inheritance

Parrot.prototype = Bird.prototype;

Parrot.prototype.constructor = Parrot;

//augment prototype

Parrot.prototype.name = 'Parrot';

Parrot.prototype.getPoint = function(){

       return this.weight * this.speed;

}

//调用

var parrot=new Parrot(33,33);

parrot.getPoint();

parrot.hasOwnProperty('weight');//true

parrot.hasOwnProperty('name');//false

 

然而,这样改了一个属性,则父对象也会跟着改:

Parrot.prototype.name='Parrot'

"Parrot"

var s = new Animal();

undefined

s.name;

"Parrot"

 

 

 

方法#3:临时构造器法——new F()

3.1、创建一个空函数F(),将其原型设置为父级构造器。

于是便可以用new F()来创建一些不包含父对象属性的对象,又能从父对象中继承一切了。

//constructor

function Animal(){}

//augment prototype

Animal.prototype.name = 'Animal';

Animal.prototype.toString = function(){

       return this.name;

}

//another constructor

function Bird(){}

//take care of inheritance

var F = function(){};

F.prototype = Animal.prototype;

Bird.prototype = new F();

Bird.prototype.constructor = Bird;

//augment prototype

Bird.prototype.name = 'Bird';

 

function Parrot(weight,speed){

       this.weight = weight;

       this.speed = speed;

}

//take care of inheritance

var F = function(){};

F.prototype = Bird.prototype;

Parrot.prototype = new F();

Parrot.prototype.constructor = Parrot;

//augment prototype

Parrot.prototype.name = 'Parrot';

Parrot.prototype.getPoint = function(){

       return this.weight * this.speed;

}

//此时修改子对象的属性便不会改变到父对象

var s = new Animal();

s.name;

"Animal"

 

 

3.2、uber——子对象访问父对象的方式

子类调用父类的方法,在构建继承关系中引入一个uber属性,并令其指向父级原型对象。

function Animal(){}

//augment prototype

Animal.prototype.name = 'Animal';

Animal.prototype.toString = function(){

return this.constructor.uber

  ? this.constructor.uber.toString() + ', ' + this.name

  : this.name;

};

//another constructor

function Bird(){}

//take care of inheritance

var F = function(){};

F.prototype = Animal.prototype;

Bird.prototype = new F();

Bird.prototype.constructor = Bird;

Bird.uber = Animal.prototype;

//augment prototype

Bird.prototype.name = 'Bird';

function Parrot(weight,speed){

this.weight = weight;

this.speed = speed;

}

//take care of inheritance

var F = function(){};

F.prototype = Bird.prototype;

Parrot.prototype = new F();

Parrot.prototype.constructor = Parrot;

Parrot.uber = Bird.prototype;

//augment prototype

Parrot.prototype.name = 'Parrot';

Parrot.prototype.getPoint = function(){

return this.weight * this.speed;

};

var my = new Parrot(5,20);

my.toString();

"Animal, Bird, Parrot"

 

 

3.3 将继承部分封装成函数

将实现继承关系的代码提炼出来并迁入一个叫做extend()的可重用函数中。

function extend(Child,Parent){

       var F = function(){};

       F.prototype = Parent.prototype;

       Child.prototype = new F();

       Child.prototype.constructor = Child;

       Child.uber = Parent.prototype;

}

//于是便可使用如下实现继承:

extend(Bird,Animal);

extend(Parrot,Bird);

 

完整的一个例子:

//inheritance helper

function extend(Child,Parent){

       var F = function(){};

       F.prototype = Parent.prototype;

       Child.prototype = new F();

       Child.prototype.constructor = Child;

       Child.uber = Parent.prototype;

}

//define - augment

function Animal(){};

Animal.prototype.name = 'Animal';

Animal.prototype.toString = function(){

return this.constructor.uber

  ? this.constructor.uber.toString() + ', ' + this.name

  : this.name;

};

//define - inherit - augment

function Bird(){};

extend(Bird,Animal);

Bird.prototype.name = 'Bird';

 

//define

function Parrot(weight,speed){

       this.weight = weight;

       this.speed = speed;

}

//inherit

extend(Parrot,Bird);

//augment

Parrot.prototype.name = 'Parrot';

Parrot.prototype.getPoint = function(){

       return this.weight * this.speed;

}

new Parrot().toString();

"Animal, Bird, Parrot"

 

 

方法#4:属性拷贝

4.1参照之前的extend()接口,创建一个extend2()函数,将Parent的原型的所有属性全部拷贝给Child的原型,包括方法。

function extend2(Child,Parent){

       var p = Parent.prototype;

       var c = Child.prototype;

       for(var i in p){

              c[i] = p[i];

       }

       c.uber = p;

}

//用extend2()拷贝获得的toString()方法是一个函数引用。

extend2(Bird,Animal);

var smallBird = new Bird();

smallBird.__proto__.hasOwnProperty('name');//true

smallBird.__proto__.hasOwnProperty('toString');//true

smallBird.__proto__.toString === Aniaml.prototype.toString;

 

 

4.2 小心处理引用拷贝

对象类型,如函数与数组,通常都是以引用形式来进行拷贝的。

如下:

//两个构造器函数,在第一个构造器函数中添加一些属性:

function Dad(){};

function Mom(){};

Dad.prototype.name = 'Dad';

Dad.prototype.owns = ['money','apple'];

extend2(Mom,Dad);

//如果改变Mom中的name属性,不会对Dad产生影响:

Mom.prototype.name += 'small';

Dad.prototype.name;

//但如果改变的是Mom中的owns属性,Dad就受到影响了:

Mom.prototype.owns.pop();

Dad.prototype.owns;

 

//当然,如果用另一个对象对Mom的owns属性进行重写(而非修改现有属性),

//则,Dad的owns属性会继续引用原有对象,而Mom的owns属性则指向了新的对象。

Mom.prototype.owns = ['empty','desk'];(指向了新对象)

Dad.prototype.owns.push('bed');

Dad.prototype.owns;

 

 

方法#5:属性全拷贝

function extendCopy(p){

       var c = {};

       for(var i in p){

              c[i] = p[i];

       }

       c.uber = p;

       return c;

}

//创建一个基本对象:

var animal= {

       name:'animal',

       toString:function(){

              return this.name;

       }

}

//创建新对象,并进行扩展:

var plant = extendCopy(animal);

plant.name = 'plant';

plant.toString = function(){

       return this.uber.toString() + ' , ' + this.name;

}

var smallplant = extendCopy(plant);

smallplant.name = 'smallplant';

smallplant.getFunc = function(){

       return this.weight * this.height;

}

smallplant.weight = 5;

smallplant.height = 10;

smallplant.getFunc();

smallplant.toString();

"animal , plant , smallplant"

 

 

 

方法#6:深拷贝

也是通过遍历对象的属性来进行拷贝操作,只是在遇到一个对象引用的属性时,需要再次对其调用深拷贝函数。

function deepCopy(p,c){

       c = c || {};

       for(var i in p){

              if(p.hasOwnProperty(i)){

                     if(typeof p[i] === 'object'){

                            c[i] = Array.isArray(p[i]) ? [] : {};

                            deepCopy(p[i],c[i]);

                     }else{

                            c[i]=p[i];

                     }

              }

       }

       return c;

}

//创建一个对象,包含数组和子对象:

var parent = {

       numbers:[1,2,3],

       letters:['a','b','c'],

       obj : {

              prop:1

       },

       bool:true

}

var mydeep = deepCopy(parent);

var myextend = extendCopy(parent);

mydeep.numbers.push(4,5,6);

mydeep.numbers;

myextend.numbers.push(10);

4

myextend.numbers;

[1,2,3,10];

//ES5标准中实现了Array.isArray()函数。

//跨浏览器解决方案:

if(Array.isArray !== 'function'){

       Array.isArray = function(num){

              return

              Object.prototype.toString.call(num) === '[Object Array]'

       }

}

 

 

 

方法#7:原型继承法

Douglas Crockford为我们提供了一种方法,级用object()函数来接收父对象,并返回一个以该对象为原型的新对象。

function object(o){

       function F(){};

       F.prototype = o;

       return new F();

}

//如果需要访问uber属性,可以继续object()函数,具体如下:

function object(o){

       var n;

       function F(){};

       F.prototype = o;

       n = new F();

       n.uber = o;

       return n;

}

//使用

var smallplant = object(plant);

smallplant.name = 'smallplant';

smallplant.getFunc = function(){

       return this.weight * this.height;

}

smallplant.toString();

 

//这个函数被ES5采纳,更名为Object.create();

var parrot = Object.create(bird);

 

 

 

方法#8:扩展与增强模式

新建一个对象时,首先要先继承现有对象,然后再为其添加额外的方法和属性。

具体:

*使用原型继承的方式,将一个已有对象设置为新对象的原型

*新建一个对象后,将另一个已有对象的所有属性拷贝过来

function objectPlus(o,stuff){

       var n;

       function F(){};

       F.prototype = o;

       n = new F();

       n.uber = o;

       for(var i in stuff){

              n[i] = stuff[i];

       }

       return n;

}

//首先,需要一个基本对象animal:

var animal = {

       name:'animal',

       toString:function(){

              return this.name;

       }

}

var bird = objectPlus(animal,{

       name:'bird',

       toString:function(){

              return this.uber.toString() + ' , ' + this.name;

       }

})

var parrot = objectPlus(bird,{

       name:'parrot',

       getPoint:function(){

              return this.weight * this.speed;

       },

       weight:0,

       speed:0

});

var my = objectPlus(parrot,{

       weight:4,

       speed:4

});

//注意:parrot的name属性被重复两次。

//是基于parrot对象的,多了一层继承关系,也可以给该实例一个name属性。

objectPlus(parrot,{

       name:'parrot-small'

}).toString();

 

 

 

方法#9:多重继承

外层循环用于遍历对象,内层循环用于拷贝属性

function multi(){

       var n={},stuff,j=0,len=arguments.length;

       for(j=0;j<len;j++){

              stuff =arguments[j];

              for(var i in stuff){

                     if(stuff has Own property(1)){

                            n[i]=stuff[i];

                     }

              }

       }

       return n;

}

//创建3个对象并作为参数传递

var animal = function(){

       name:'animal',

       toString:function(){

              return this.name;

       }

}

var bird = {

       name:'bird',

       dimen:2

}

var parrot = multi(animal,bird,{

       name:'parrot',

       getPoint:function(){

              return this.weight * this.height

       }

});

animal.getPoint();

animal.dimen;

 

 

方法#10:寄生式继承

将父对象克隆进that对象,添加属性。

var bird = {

       name:'bird',

       dimen:2

}

function parrot(w,s){

       var that = object(bird);

       that.name = 'parrat';

       that.getPoint = function(){

              return this.weight * this.speed;

       }

       that.weight = w;

       that.speed = s;

       return that;

}

 

posted @ 2017-02-20 06:44  jsbally  阅读(155)  评论(0编辑  收藏  举报