摘要:继承和前面两篇文章中的知识非常相关,如果对函数创建原理和原型链不熟悉,请猛戳高级程序设计笔记创建对象高级程序设计笔记原型图解继承,通俗的说,就是将自身不存在的属性或方法,通过某种方式为自己所用文章分别介绍原型链继承继承借用构造函数继承组合继
继承和前面两篇文章中的知识非常相关,如果对函数创建原理和原型链不熟悉,请猛戳:
《javascript高级程序设计》笔记:创建对象
《javascript高级程序设计》笔记:原型图解
继承,通俗的说,就是将自身不存在的属性或方法,通过某种方式为自己所用
文章分别介绍原型链继承、call/apply继承(借用构造函数继承)、组合继承、原型式继承、寄生式继承、寄生组合式继承
1. 原型链继承核心:将父类的实例作为子类的原型
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } // 继承自SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue());//true
简单的原型链分析
在《javascript高级程序设计》笔记:创建对象 的文章中,使用原型创建对象会存在多个实例对引用类型的操作会被篡改的问题,在上面同样存在这个问题,如下:
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){} SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green,black"
两个实例对象instance1和instance2的colors属性指向相同,改变一个会影响另一个实例的属性
缺陷:
(1)原型链继承多个实例的引用类型属性指向相同,存在篡改的可能
(2)不能传递参数
核心:使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类(不使用原型)
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } function SubType(name, age){ // 继承自SuperType SuperType.call(this, name); this.age = age; } var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green" alert(instance1.name); // "Nicholas" alert(instance1.age); // 29
借用构造函数继承的核心就在于SuperType.call(this, name),“借调”了SuperType构造函数,这样,SubType的每个实例都会将SuperType中的属性复制一份
缺陷:
(1)只能继承父类的实例属性和方法,不能继承原型属性/方法
(2)无法实现复用,每个子类都有父类实例函数的副本,影响性能
核心:结合原型链继承和构造函数继承通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承,这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ //继承属性 SuperType.call(this, name); this.age = age; } // 继承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27
图解:
缺陷:
父类中的实例属性和方法既存在于子类的实例中,又存在于子类的原型中,不过仅是内存占用,因此,在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法
这个方法是javascript中最常用的继承模式
4. 原型式继承核心:直接将某个对象直接赋值给构造函数的原型
function object(obj){ function F(){} F.prototype = obj; return new F(); }
object()对传入其中的对象执行了一次浅复制,将F的原型直接指向传入的对象
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
缺点:
(1)原型链继承多个实例的引用类型属性指向相同,存在篡改的可能
(2)无法传递参数
另外,ES5中存在Object.create()的方法,能够代替上面的object方法
5. 寄生式继承核心:在原型式继承的基础上,增强对象,返回构造函数
function createAnother(original){ varclone=object(original); // 过调用函数创建一个新对象 clone.sayHi = function(){ // 以某种方式增强这个对象 alert("hi"); }; return clone; // 返回对象 }
函数的主要作用是为构造函数新增属性和方法,以增强函数
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
缺点:
(1)原型链继承多个实例的引用类型属性指向相同,存在篡改的可能
(2)无法传递参数
核心:结合借用构造函数传递参数和寄生模式实现继承
function inheritPrototype(subType, superType){ var prototype = Object.create(superType.prototype); //创建对象 prototype.constructor = subType; // 增强对象 subType.prototype = prototype; // 指定对象 } // 父类初始化实例属性和原型属性 function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; // 借用构造函数传递增强子类实例属性(支持传参和避免篡改) function SubType(name, age){ SuperType.call(this, name); this.age = age; } // 将父类原型指向子类 inheritPrototype(SubType, SuperType); // 新增子类原型属性 SubType.prototype.sayAge = function(){ alert(this.age); } var instance1 = new SubType("xyc", 23); var instance2 = new SubType("lxy", 23); instance1.colors.push("2"); // ["red", "blue", "green", "2"] instance1.colors.push("3"); // ["red", "blue", "green", "3"]
图解:
寄生组合继承集合了前面几种继承优点,几乎避免了上面继承方式的所有缺陷,是执行效率最高也是应用面最广的,就是实现的过程相对繁琐
参考:
JS继承的实现方式
面向对象的程序设计之继承
MDN——Object.create()
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/89603.html
摘要:上一篇你不知道的笔记写在前面这是年第一篇博客,回顾去年年初列的学习清单,发现仅有部分完成了。当然,这并不影响年是向上的一年在新的城市稳定连续坚持健身三个月早睡早起游戏时间大大缩减,学会生活。 上一篇:《你不知道的javascript》笔记_this 写在前面 这是2019年第一篇博客,回顾去年年初列的学习清单,发现仅有部分完成了。当然,这并不影响2018年是向上的一年:在新的城市稳定、...
摘要:继承传统的面向对象语言,继承是类与类之间的关系。原型继承原型定义原型就是指构造函数的属性所引用的对象。创建构造函数创建的实例对象张三李四就是对象的原型也是的原型在原型上创建一个属性运行和,并对比是否为同一个方法。 原文链接:http://www.hansmkiii.com/2018/07/06/javascript-node-1/ 面向对象、原型、继承 1、面向对象 1.1 什么...
摘要:思路是,使用原型链对原型属性和方法进行继承,借用构造函数实现对实例属性的继承。注意使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。无论什么情况下都会调用两次超类型的构造函数。 说明: 此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看的顺序来的...
摘要:继承的是超类型中构造函数中的属性,如上继承了属性,但没有继承原型中的方法。上述造成的结果是子类型实例中有两组超类型的构造函数中定义的属性,一组在子类型的实例中,一组在子类型实例的原型中。 ECMAScript只支持实现继承,主要依靠原型链来实现。与实现继承对应的是接口继承,由于script中函数没有签名,所以无法实现接口继承。 一、原型链 基本思想:利用原型让一个引用类型继承另一个引用...
摘要:实现原型链的方式如下让原型对象称为另一个构造函数的实例这个实例继承了的属性上述代码继承是通过来实现,创建的实例,并将该实例赋给。无疑,集两者之大成,这才是最常用的继承模式。 原型链 JavaScript的继承主要依靠原型链来实现的。我们知道,构造函数,原型,和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个原型对象的指针。 实现原型链...
阅读 2273·2021-11-24 09:39
阅读 2967·2021-10-15 09:39
阅读 3066·2021-07-26 23:38
阅读 2260·2019-08-30 11:14
阅读 3389·2019-08-29 16:39
阅读 1693·2019-08-29 15:23
阅读 737·2019-08-29 13:01
阅读 2633·2019-08-29 12:29