Javascript 继承实现方式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Javascript 继承实现方式相关的知识,希望对你有一定的参考价值。

参考技术A

  面向对象与基于对象

  几乎每个开发人员都有面向对象语言(比如C++ C# Java)的开发经验 在传统面向对象的语言中 有两个非常重要的概念 类和实例 类定义了一类事物公共的行为和方法 而实例则是类的一个具体实现 我们还知道 面向对象编程有三个重要的概念 封装 继承和多态

  但是在javascript的世界中 所有的这一切特性似乎都不存在 因为JavaScript本身不是面向对象的语言 而是基于对象的语言 这里面就有一些有趣的特性 比如JavaScript中所有事物都是对象 包括字符串 数组 日期 数字 甚至是函数 比如下面这个例子

  var doc; //定义了一个doc基类

  doc=(function()

  var _d;

  function doc()

  this _d=window document;

  

  doc prototype echo=function()

  this _d write( hell world )

  return ;

   //定义了一个属性echo

  return doc;

  )()

  使用

  var mydoc=new doc()

  mydoc echo() //在页面显示hell world

  模拟JavaScript中类和继承

  /*

  * 作者 杨贤喜

  * 开发时间

  * 联系方式

  */

  //原自typescript 思路

  var __extends = this __extends || function(a b)

  function __() nstructor=a;

  __ prototype=b prototype;

  a prototype=new __()

  

  这就需要引入另外一个概念 原型(prototype) 我们可以简单的把prototype看做是一个模版 新创建的自定义对象都是这个模版(prototype)的一个拷贝 (实际上不是拷贝而是链接 只不过这种链接是不可见 给人们的感觉好像是拷贝)

  实现类的继承

  //DocMulit 继承doc

  var DocMulit=(function(_doc)

  __extends(DocMulit _doc)

  var txt but;

  function DocMulit()

  _doc call(this)

  txt= this _d createElement( input )

  but=this _d createElement( button )

  but innerText= 单击这里 ;

  but onclick=function()

  if(txt value== )

  txt focus() //console log(txt)

  else

  var doc_p=new DocP(txt value)

  doc_p echo()

  

  

  

  //重写父类的 echo 的方法

  DocMulit prototype echo=function()

  _doc prototype echo call(this) //调用父类的echo方法

  this _d body appendChild(txt)

  this _d body appendChild(but)

  

  return DocMulit;

  )(doc)

  //DocP 继承doc

  var DocP=(function(_doc)

  __extends(DocP _doc)

  var p_tar;

  function DocP(tar)

  _doc call(this)

  this _d=window document;

  this p_tar=this _d createElement( p )

  this p_tar innerText=tar;

  this p_tar setAttribute( title Click here of delete )

  this p_tar setAttribute( style cursor:pointer; )

  this p_tar onclick=function()

  if(confirm( You are delete? ))

  window document body removeChild(this)

  

  

  

  //重写父类的echo方法

  DocP prototype echo=function()

  this _d body appendChild(this p_tar)

  

  return DocP;

  )(doc)

  //实例化doc继承类DocMulit

  var mydoc=new DocMulit()

  mydoc echo()

  在页面上看到的效果

lishixinzhi/Article/program/Java/JSP/201311/19220

javascript实现继承的方式和各自的优缺点

ECMAscript只支持实现继承,主要是依靠原型链来实现的。

JavaScript实现继承的方式:

  • 类式继承
  • 构造函数继承
  • 组合继承
  • 寄生组合式继承

1.类式继承

 1 //类式继承
 2 //声明父类
 3 function Animal(color) {
 4   this.name = ‘animal‘;
 5   this.type = [‘pig‘, ‘cat‘];
 6   this.color = color;
 7 }
 8 // 为父类添加共有方法
 9 Animal.prototype.greet = function(sound) {
10   console.log(sound);
11 }
12 
13 //声明子类
14 function Dog(){
15     this.name = ‘dog‘;
16 }
17 
18 //类式继承父类(父类实例作为子类的原型对象)
19 Dog.prototype = new Animal(‘white‘);
20 
21 //子类实例继承父类的属性和方法
22 var dog1 = new Dog();
23 console.log(dog1.name);     //dog
24 console.log(dog1.type);     //[ ‘pig‘, ‘cat‘ ]
25 dog1.greet(‘wangwangwang‘); //‘wangwangwang‘
26 
27 //缺陷1:修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例
28 dog1.type.push(‘dog‘);
29 var dog2 = new Dog();
30 console.log(dog2.type);   //[ ‘pig‘, ‘cat‘, ‘dog‘ ]
31 
32 //缺陷2:无法为不同的实例初始化继承来的属性,不能向父类的构造函数中传递参数
33 //无法为不同dog赋值不同的颜色,所有dog只能同一种颜色
34 console.log(dog1.color);  //‘white‘ 
35 console.log(dog2.color);  //‘white‘  
  原理说明:在实例化一个类时,新创建的对象复制了父类的构造函数内的属性与方法并且将原型__proto__指向了父类的原型对象,这样就拥有了父类的原型对象上的属性与方法。
 类式继承的两个缺陷
  (1)修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例
  (2)无法为不同的实例初始化继承来的属性,不能向父类的构造函数中传递参数

2.构造函数继承

 1 //构造函数继承
 2 //声明父类
 3 function Animal(color) {
 4   this.name = ‘animal‘;
 5   this.type = [‘pig‘, ‘cat‘];
 6   this.color = color;
 7 }
 8 // 为父类添加共有方法
 9 Animal.prototype.greet = function(sound) {
10   console.log(sound);
11 }
12 
13 //声明子类
14 function Dog(color, age){
15     Animal.apply(this, arguments); //在子类构造函数内部调用父类构造函数
16     this.age = age;
17 }
18 
19 //子类实例继承父类的属性和方法
20 var dog1 = new Dog(‘white‘, 1);
21 var dog2 = new Dog(‘red‘, 2);
22 dog1.type.push(‘dog‘);
23 
24 console.log(dog1.color);  //‘white‘ 
25 console.log(dog1.type);  //[ ‘pig‘, ‘cat‘, ‘dog‘ ]
26 console.log(dog2.color);  //‘red‘ 
27 console.log(dog2.type);  //[ ‘pig‘, ‘cat‘ ]
28 
29 //缺陷:无法获取到父类的共有方法,也就是通过原型prototype绑定的方法
30 dog1.greet(‘wangwangwang‘); //TypeError: dog1.greet is not a function
31 dog2.greet(‘wangwangwang‘); //TypeError: dog2.greet is not a function

  构造函数继承方式可以避免类式继承的缺陷,在子类构造函数内部调用父类构造函数。通过使用apply()和call()方法可以改变函数的作用域,在新创建的对象上执行构造函数。

所以在上面的例子中,我们在Dog子类中调用这个方法也就是将Dog子类的变量在父类中执行一遍,这样子类就拥有了父类中的共有属性和方法。

  构造函数继承也是有缺陷的,那就是我们无法获取到父类的共有方法,也就是通过原型prototype绑定的方法。

//缺陷:无法获取到父类的共有方法,也就是通过原型prototype绑定的方法
dog1.greet(‘wangwangwang‘); //TypeError: dog1.greet is not a function
dog2.greet(‘wangwangwang‘); //TypeError: dog2.greet is not a function

3.组合继承

组合继承其实就是将类式继承和构造函数继承组合在一起:

 1 //类式继承和构造函数继承结合的组合继承
 2 //声明父类
 3 function Animal(color) {
 4   this.name = ‘animal‘;
 5   this.type = [‘pig‘, ‘cat‘];
 6   this.color = color;
 7 }
 8 // 为父类添加共有方法
 9 Animal.prototype.greet = function(sound) {
10   console.log(sound);
11 }
12 
13 //声明子类
14 function Dog(color, age){
15     Animal.apply(this, arguments); //在子类构造函数内部调用父类构造函数
16     this.age = age;
17 }
18 
19 //类式继承
20 Dog.prototype = new Animal();
21 Dog.prototype.constructor = Dog;  
22 
23 //子类实例继承父类的属性和方法
24 var dog1 = new Dog(‘white‘, 1);
25 var dog2 = new Dog(‘red‘, 2);
26 dog1.type.push(‘dog‘);
27 
28 console.log(dog1.color);  //‘white‘ 
29 console.log(dog1.type);  //[ ‘pig‘, ‘cat‘, ‘dog‘ ]
30 console.log(dog2.color);  //‘red‘ 
31 console.log(dog2.type);  //[ ‘pig‘, ‘cat‘ ]
32 
33 //获取到父类的共有方法
34 dog1.greet("wang");  //‘wang‘
35 dog2.greet("miao");  //‘miao‘
36 
37 //缺陷 :调用了两次父类构造函数
38 Dog.prototype = new Animal(); //第一次调用
39 Animal.apply(this, arguments); //第二次调用
  在上面的例子中,我们在子类构造函数中执行父类构造函数,在子类原型上实例化父类,这就是组合继承了,可以看到它综合了类式继承和构造函数继承的优点,同时去除了缺陷。组合继承是JavaScript中最常用的继承方式。 
  可能你会奇怪为什么组合式继承可以去除类式继承中的引用缺陷?其实这是由于原型链来决定的,由于JavaScript引擎在访问对象的属性时,会先在对象本身中查找,如果没有找到,才会去原型链中查找,如果找到,则返回值,如果整个原型链中都没有找到这个属性,则返回undefined。也就是说,我们访问到的引用类型(比如上面的type)其实是通过apply复制到子类中的,所以不会发生共享。
组合继承的缺陷就是它调用了两次父类的构造函数。
//缺陷 :调用了两次父类构造函数
Dog.prototype = new Animal(); //第一次调用
Animal.apply(this, arguments); //第二次调用

4.寄生组合式继承

寄生组合式继承强化的部分就是在组合继承的基础上减少一次多余的调用父类的构造函数。由于现在用的最多的还是组合继承,寄生组合式继承不做深入的探究,有兴趣可以 参考js高程P172 。

总结

  JavaScript主要通过原型链实现继承。原型链的构建是通过将一个类型的的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问父类型的所有属性和方法,这一点与基于类的继承很相似。

  原型链的问题对象实例共享所有继承的方法和属性,修改子类实例对象继承自父类的属性或方法时,会影响新创建的子类实例,因此不适宜单独使用。

  解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用父类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。但是使用这种方式导致的结果就是无法获取到父类的共有方法,也就是通过原型prototype绑定的方法。

  使用最多的集成模式是组合继承方式,这种模式综合了类式继承和构造函数继承的优点,同时去除了缺陷。即使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。

 

参考:JavaScript实现继承的方式

以上是关于Javascript 继承实现方式的主要内容,如果未能解决你的问题,请参考以下文章

javascript实现继承的方式和各自的优缺点

javascript 继承实现方式

JavaScript是如何实现继承的(六种方式)

javascript实现对象的继承的方式

javascript实现继承的几种方式

JavaScript 实现继承的5种方式