博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript 继承
阅读量:5157 次
发布时间:2019-06-13

本文共 6837 字,大约阅读时间需要 22 分钟。

原型链

  JavaScript中实现继承最简单的方式就是使用原型链,将子类型的原型指向父类型的实例即可,即 “子类型.prototype = new 父类型();” 。

 

1 // 为父类型创建构造函数 2 function SuperType() { 3   this.name = ['wuyuchang', 'Jack', 'Tim']; 4   this.property = true; 5 } 6  7 // 为父类型添加方法 8 SuperType.prototype.getSuerperValue = function() { 9   return this.property;10 }11 12 // 为子类型创建构造函数13 function SubType() {14   this.test = ['h1', 'h2', 'h3', 'h4'];15   this.subproperty = false;16 }17 18 // 实现继承的关键步骤,子类型的原型指向父类型的实例19 SubType.prototype = new SuperType();20 21 // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空22 SubType.prototype.getSubValue = function() {23   return this.subproperty;24 }25 26 27 /* 以下为测试代码示例 */28 var instance1 = new SubType();29 instance1.name.push('wyc');30 instance1.test.push('h5');31 alert(instance1.getSuerperValue());    // true32 alert(instance1.getSubValue());      // false33 alert(instance1.name);          // wuyuchang,Jack,Tim,wyc34 alert(instance1.test);          // h1,h2,h3,h4,h535 36 37 var instance2 = new SubType();38 alert(instance2.name);          // wuyuchang,Jack,Tim,wyc39 alert(instance2.test);          // h1,h2,h3,h4

 

原型链继承存在两个问题:

第一,由于子类型的原型是父类型的实例,也就是子类型的原型中包含的父类型的属性,从而导致引用类型值的原型属性会被所有实例所共享。以上代码的 “instance1.name.push('wyc');" 就说明此问题。

第二,在创建子类型的实例时,不能向超类型的构造函数中传递参数。因此我们在实际开发中,很少单独使用原型链。 

 

借用构造函数

  为了解决原型链中存在的两个问题,开发人员开始使用一种叫做 “借用构造函数” 的技术来解决原型链中存在的问题。这种技术的实现思路也挺简单,只需要在子类型的构造函数内调用父类型的构造函数即可。别忘了,函数只不过是在特定环境中执行代码的对象,因此可以通过apply()或call()方法执行构造函数。

 

1 // 为父类型创建构造函数 2 function SuperType(name) { 3   this.name = name; 4   this.color = ['pink', 'yellow']; 5   this.property = true; 6  7   this.testFun = function() { 8     alert('http://tools.jb51.net/'); 9   }10 }11 12 // 为父类型添加方法13 SuperType.prototype.getSuerperValue = function() {14   return this.property;15 }16 17 // 为子类型创建构造函数18 function SubType(name) {19   SuperType.call(this, name);20   this.test = ['h1', 'h2', 'h3', 'h4'];21   this.subproperty = false;22 }23 24 // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空25 SubType.prototype.getSubValue = function() {26   return this.subproperty;27 }28 29 30 /* 以下为测试代码示例 */31 var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);32 instance1.name.push('hello');33 instance1.test.push('h5');34 instance1.color.push('blue');35 instance1.testFun();            // http://tools.jb51.net/36 alert(instance1.name);            // wuyuchang,Jack,Nick,hello37 // alert(instance1.getSuerperValue());    // error 报错38 alert(instance1.test);            // h1,h2,h3,h4,h5    39 alert(instance1.getSubValue());        // false    40 alert(instance1.color);            // pink,yellow,blue41 42 var instance2 = new SubType('wyc');43 instance2.testFun();            // http://tools.jb51.net/44 alert(instance2.name);            // wyc    45 // alert(instance2.getSuerperValue());    // error 报错46 alert(instance2.test);            // h1,h2,h3,h447 alert(instance2.getSubValue());        // false48 alert(instance2.color);            // pink,yellow

 

 

可以看到以上代码中子类型SubType的构造函数内通过调用父类型 "SuperType.call(this, name);" 实现了属性的继承,也可以在子类型创建实例的时候为父类型传递参数了。但新的问题又来了。可以看到我在父类型的构造函数中定义了一个方法:testFun,在父类型的原型中定义了一个方法:getSuperValue。可是在实例化子类型后仍然是无法调用父类型的原型中定义的方法getSuperValue,只能调用父类型中构造函数的方法testFun。这就同创建对象中只使用构造函数模式一样,使得函数没有复用性可言。考虑到这些问题,借用构造函数的技术也是很少单独使用的。

 

组合继承(原型链+借用构造函数)

  组合继承就是结合使用原型链与借用构造函数的优点,组合而成的一个模式。实现也很简单,既然是结合,那当然结合了两方的优点,即原型链继承方法,而在构造函数继承属性。

1 // 为父类型创建构造函数 2 function SuperType(name) { 3   this.name = name; 4   this.color = ['pink', 'yellow']; 5   this.property = true; 6  7   this.testFun = function() { 8     alert('http://tools.jb51.net/'); 9   }10 }11 12 // 为父类型添加方法13 SuperType.prototype.getSuerperValue = function() {14   return this.property;15 }16 17 // 为子类型创建构造函数18 function SubType(name) {19   SuperType.call(this, name);20   this.test = ['h1', 'h2', 'h3', 'h4'];21   this.subproperty = false;22 }23 24 SubType.prototype = new SuperType();25 26 // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空27 SubType.prototype.getSubValue = function() {28   return this.subproperty;29 }30 31 32 /* 以下为测试代码示例 */33 var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);34 instance1.name.push('hello');35 instance1.test.push('h5');36 instance1.color.push('blue');37 instance1.testFun();            // http://tools.jb51.net/38 alert(instance1.name);            // wuyuchang,Jack,Nick,hello39 alert(instance1.getSuerperValue());      // true40 alert(instance1.test);            // h1,h2,h3,h4,h5    41 alert(instance1.getSubValue());        // false    42 alert(instance1.color);            // pink,yellow,blue43 44 var instance2 = new SubType('wyc');45 instance2.testFun();            // http://tools.jb51.net/46 alert(instance2.name);            // wyc    47 alert(instance2.getSuerperValue());      // true48 alert(instance2.test);            // h1,h2,h3,h449 alert(instance2.getSubValue());        // false50 alert(instance2.color);            // pink,yellow

 

 

以上代码通过 SuperType.call(this, name); 继承父类型的属性,通过 SubType.prototype = new SuperType(); 继承父类型的方法。以上代码很方便的解决了原型链与借用构造函数所遇到的问题,成为了JavaScript中最为常用的实例继承的方法。但混合模式也有缺点,可以看到在以上代码中在继承方法的时候实际已经继承了父类型的属性,只不过此时对于引用类型属于共享的,因此在子类型的构造函数内再次调用父类型的构造函数从而继承了父类型的属性而去覆盖了原型中所继承的属性,这样调用两次构造函数显然没有必要,但有什么方法可以解决呢?在解决此问题时先看以下两个模式。

 

原型式继承:原型式继承的的实现方法与普通继承的实现方法不同,原型式继承并没有使用严格意义上的构造函数,而是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

 

1 /* 原型式继承 */ 2 function object(o) { 3   function F() {} 4   F.prototype = o; 5   return new F(); 6 } 7  8 var person = { 9   name : 'wuyuchang',10   friends : ['wyc', 'Nicholas', 'Tim']11 }12 13 var anotherPerson = object(person);14 anotherPerson.name = 'Greg';15 anotherPerson.friends.push('Bob');16 17 var anotherPerson2 = object(person);18 anotherPerson2.name = 'Jack';19 anotherPerson2.friends.push('Rose');20 21 alert(person.friends);  // wyc,Nicholas,Tim,Bob,Rose

 

寄生式继承

 

1 /* 原型式继承 */ 2 function object(o) { 3   function F() {} 4   F.prototype = o; 5   return new F(); 6 } 7     8 /* 寄生式继承 */ 9 function createAnother(original) {10   var clone = object(original);11   clone.sayHi = function() {12     alert('hi');13   }14   return clone;15 }16 17 var person = {18   name : 'wuyuchang',19   friends : ['wyc', 'Nicholas', 'Rose']20 }21 var anotherPerson = createAnother(person);22 anotherPerson.sayHi();

 

寄生组合式继承:前面说过了JavaScrip中组合模式实现继承的缺点,现在我们就来解决它的缺点,实现思路是,对于构造函数继承属性,而原型链的混成形式继承方法,即不用在继承方法的时候实例化父类型的构造函数

 

1 function object(o) { 2   function F() {} 3   F.prototype = o; 4   return new F(); 5 } 6  7 /* 寄生组合式继承 */ 8 function inheritPrototype(subType, superType) { 9   var prototype = object(superType.prototype);10   prototype.constructor = subType;11   subType.prototype = prototype;12 }

 

而在使用时只需要将组合模式中的 “SubType.prototype = new SuperType();” 这行代码替换成 inheritPrototype(subType, superType);  即可。寄生组合式继承的高效率体现在它只调用了一次父类型构造函数,避免了创建不必要的或多余的属性。与此同时,原型链还能保持不变。因此,还能够正常使用instanceof和isPrototypeof()。这也是目前来说最理想的继承方式了,目前也在向这种模式转型。

 

文章学习自有删改。

 

 

 

最终参考是来自于《JavaScript高级程序设计》一书。

 

转载于:https://www.cnblogs.com/xiaochechang/p/5735414.html

你可能感兴趣的文章
Cocos2d-x动作学习笔记 分类: cocos2d代码编写 ...
查看>>
第4章例4-12 源程序2
查看>>
Bootstrap 3 How-To #1 下载与配置
查看>>
java并发编程(10)Fork/Join
查看>>
#程序员健康 如何解救你的鼠标手
查看>>
linux下maven的安装
查看>>
win10安装express遇到的问题。
查看>>
Corosync 配置描述
查看>>
JAVA中类、实例与Class对象
查看>>
svn_linux + apache 实现网页访问svn
查看>>
leetcode 58. length of last word
查看>>
C++随笔(2)
查看>>
周浩晖 - 鬼望坡(2014年11月25日)
查看>>
servlet+jdbc+fckeditor网络留言板
查看>>
JavaScript 对象
查看>>
matlab 非平稳变化时域分析
查看>>
6、Cocos2dx 3.0游戏开发的基本概念找个小三场比赛
查看>>
Android读取JSON格式数据
查看>>
SAP HANA开发中常见问题- 基于SAP HANA平台的多团队产品研发
查看>>
内部元素一一相应的集合的算法优化,从list到hashmap
查看>>