摘要:创建实例时无法向的构造函数传递参数在不影响所有对象实例的情况下易错点添加原型方法的代码要放在替换原型的语句之后。继承属性继承方法原型式继承原型式继承并没有使用严格意义上的构造函数。
许多OO 语言都支持两种继承方式:
接口继承:只继承方法签名
实现继承:继承实际的方法。
由于函数没有签名,在ECMAScript 中无法实现接口继承。ECMAScript 只支持实现继承
原型链继承原型链是Javascript实现继承的主要方法
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(); console.log(instance.getSuperValue())//true //instanceof用于测试实例与原型链中出现过的构造函数的关系 console.log(instance instanceof object) //true console.log(instance instanceof SuperType) //true console.log(instance instanceof SubType) //true console.log(Object.prototype.isPrototypeof(instance)) //true console.log(SuperType.prototype.isPrototypeof(instance)) //true console.log(SubType.prototype.isPrototypeof(instance)) //true
实践中较少多带带使用原型链继承
注意:
instance.constructure现在指向的是SuperType
instance.toString现在指向的是Object.prototype
SuperType中的引用值类型会在所有SubType中共享。
创建SubType实例时,无法向SuperType的构造函数传递参数(在不影响所有对象实例的情况下)
易错点
SubType、SuperType添加原型方法的代码要放在替换原型的语句之后。否则SubType添加的原型方法会失效,因为原型对象被替换了。
不能通过字面量创建SubType的原型对象。因直接对原型赋值字面量对象会替换SubType的原型,打破了原型链。
借用构造函数继承解决了Javascript原型链继承中引用值类型被共享的问题
function SuperType(){ this.colors=["red","blue","green"]; } function SubType(){ SuperType.call(this); } 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"
通过call(this)或apply(this)我们再SubType的实例环境下调用了SuperType的构造函数,因此在SubType对象上执行了SuperType的对象初始化代码,因此每个SubType都具有了自己的colors属性
优势
可以传递在子类构造函数中向超类构造函数传递参数
问题
因为方法都在构造函数中定义的,因此无法实现函数复用,每个对象都重新定义了一次方法。
无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
注意
为了确保SuperType构造函数不会重写子类属性,应在调用SuperType的构造函数之后再添加子类自身的属性。
组合继承将原型链和借用构造函数组合到一块,发挥二者之长的一种继承模式。
原型链实现对属性和方法的复用。
借用构造函数实现对实例属性的继承。
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ console.log(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(){ console.log(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); console.log(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); console.log(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27原型式继承
原型式继承并没有使用严格意义上的构造函数。是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
function object(o){ function F(){} F.prototype = o; return new F(); }
在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。从本质上讲,object()对传入其中的对象执行了一次浅复制。
ECMAScript 5 通过新增Object.create()方法规范化了原型式继承。这个方法接收两个参数:
一个用作新对象原型的对象和(可选的)
一个为新对象定义额外属性的对象。
在传入一个参数的情况下,Object.create()与object()方法的行为相同。
Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属性。
在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象
function createAnother(original){ //通过调用函数创建一个新对象,任何能够返回新对象的函数都适用该模式 var clone = object(original); clone.sayHi = function(){ //以某种方式来增强这个对象 alert("hi"); }; return clone; //返回这个对象 } var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
问题
无法做到函数复用而降低效率
寄生组合式继承由于组合继承两次调用了SuperType的构造函数,SubType就具有了两组SuperType的属性:一组在实例上,一组在SubType 原型中。
寄生组合式继承通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
基本思路:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。
本质:使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function inheritPrototype(subType, superType){ var prototype = object(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); };
优势
高效率:只调用了一次SuperType 构造函数,并且因此避免了在SubType.prototype 上面创建不必要的、多余的属性。
原型链还能保持不变;因此,还能够正常使用instanceof 和isPrototypeOf()
寄生组合式继承是引用类型最理想的继承范式。
YUI 的YAHOO.lang.extend()方法采用了寄生组合继承,从而让这种模式首次出现在了一个应用非常广泛的JavaScript 库中。
node中的util.inherits继承寄生组合式继承与寄生组合式继承中inheritPrototype功能一致,因此如果不希望在子类间共享引用类型值属性,还需组合借用构造函数继承。
exports.inherits = function(ctor,superCtor){ ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype,{ constructor:{ value:ctor, enumerable:false, writable:true, configurable:true } }; };
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/82552.html
摘要:和构造函数前面提到,是个内置隐藏属性,虽然在可以通过访问,但是其设计本意是不可被读取和修改的,那么我们如何利用原型链来建立继承关系提供了关键字。到这儿,思路就清晰了,怎么让对象和对象的相连实现继承只需把的构造函数的连接到就行了。 什么是继承? 大多数人使用继承不外乎是为了获得这两点好处,代码的抽象和代码的复用。代码的抽象就不用说了,交通工具和汽车这类的例子数不胜数,在传统的OO语言中(...
摘要:的继承方式属于原型式继承,非常灵活。当使用关键字执行类的构造函数时,系统首先创建一个新对象,这个对象会继承自构造函数的原型对象新对象的原型就是构造函数的属性。也就是说,构造函数用来对生成的新对象进行一些处理,使这个新对象具有某些特定的属性。 继承这个东西在Javascript中尤其复杂,我掌握得也不好,找工作面试的时候在这个问题上栽过跟头。Javascript的继承方式属于原型式继承,...
摘要:中的继承并不是明确规定的,而是通过模仿实现的。继承中的继承又称模拟类继承。将函数抽离到全局对象中,函数内部直接通过作用域链查找函数。这种范式编程是基于作用域链,与前面讲的继承是基于原型链的本质区别是属性查找方式的不同。 这一节梳理对象的继承。 我们主要使用继承来实现代码的抽象和代码的复用,在应用层实现功能的封装。 javascript 的对象继承方式真的是百花齐放,属性继承、原型继承、...
摘要:我们有了构造函数之后,第二步开始使用它构造一个函数。来个例子这种方式很简单也很直接,你在构造函数的原型上定义方法,那么用该构造函数实例化出来的对象都可以通过原型继承链访问到定义在构造函数原型上的方法。 来源: 个人博客 白话解释 Javascript 原型继承(prototype inheritance) 什么是继承? 学过面向对象的同学们是否还记得,老师整天挂在嘴边的面向对象三大特...
摘要:这正是我们想要的太棒了毫不意外的,这种继承的方式被称为构造函数继承,在中是一种关键的实现的继承方法,相信你已经很好的掌握了。 你应该知道,JavaScript是一门基于原型链的语言,而我们今天的主题 -- 继承就和原型链这一概念息息相关。甚至可以说,所谓的原型链就是一条继承链。有些困惑了吗?接着看下去吧。 一、构造函数,原型属性与实例对象 要搞清楚如何在JavaScript中实现继承,...
阅读 2852·2021-11-22 11:56
阅读 3555·2021-11-15 11:39
阅读 898·2021-09-24 09:48
阅读 761·2021-08-17 10:14
阅读 1325·2019-08-30 15:55
阅读 2754·2019-08-30 15:55
阅读 1312·2019-08-30 15:44
阅读 2776·2019-08-30 10:59