Day41:混入

【书名】:你不知道的JavaScript(上卷)

【作者】:Kyle Simpson

【本书总页码】:213

【已读页码】:157

显式混入

由于 JavaScript 不会自动实现 Vehicle到 Car 的复制行为,所以需要手动实现复制功能。这个功能在许多库和框架中被称为extend(..),但是为了方便理解我们称之为 mixin(..)。

现在 Car 中就有了一份 Vehicle 属性和函数的副本了。从技术角度来说,函数实际上没有被复制,复制的是函数引用。所以,Car 中的属性 ignition 只是从 Vehicle 中复制过来的对于 ignition() 函数的引用。相反,属性 engines 就是直接从 Vehicle 中复制了值 1。

Car 已经有了 drive 属性(函数),所以这个属性引用并没有被 mixin 重写,从而保留了Car 中定义的同名属性,实现了“子类”对“父类”属性的重写。

1. 再谈多态

显式多态:Vehicle.drive.call( this )。

相对多态:inherited:drive()。

JavaScript(在 ES6 之前)并没有相对多态的机制。所以,由于 Car 和Vehicle 中都有 drive() 函数,为了指明调用对象,必须使用绝对(而不是相对)引用。通过名称显式指定 Vehicle 对象并调用它的 drive() 函数。

但是如果直接执行 Vehicle.drive(),函数调用中的 this 会被绑定到Vehicle 对象而不是Car 对象。因此,使用 .call(this)来确保 drive() 在 Car 对象的上下文中执行。

如果函数 Car.drive() 的名称标识符并没有和 Vehicle.drive() 重叠(或者说“屏蔽”)的话,就不需要实现方法多态,因为调用mixin(..) 时会把函数 Vehicle.drive() 的引用复制到 Car 中,因此可以直接访问this.drive()。正是由于存在标识符重叠,所以必须使用更加复杂的显式伪多态方法。

在支持相对多态的面向类的语言中,Car 和 Vehicle 之间的联系只在类定义的开头被创建,从而只需要在这一个地方维护两个类的联系。

但是在 JavaScript 中(由于屏蔽)使用显式伪多态会在所有需要使用(伪)多态引用的地方创建一个函数关联,这会极大地增加维护成本。此外,由于显式伪多态可以模拟多重继承,所以它会进一步增加代码的复杂度和维护难度。

使用伪多态通常会导致代码变得更加复杂、难以阅读并且难以维护,因此应当尽量避免使用显式伪多态,因为这样做往往得不偿失。

2. 混合复制

分析一下 mixin(..) 的工作原理。它会遍历 sourceObj的属性,如果在 targetObj没有这个属性就会进行复制。由于是在目标对象初始化之后才进行复制,因此一定要小心不要覆盖目标对象的原有属性。

如果是先进行复制然后对 Car 进行特殊化的话,就可以跳过存在性检查。不过这种方法并不好用并且效率更低,所以不如第一种方法常用:

这两种方法都可以把不重叠的内容从 Vehicle 中显性复制到 Car 中。

复制操作完成后,Car 就和 Vehicle 分离了,向 Car 中添加属性不会影响 Vehicle,反之亦然。

由于两个对象引用的是同一个函数,因此这种复制(或者说混入)实际上并不能完全模拟面向类的语言中的复制。

JavaScript 中的函数无法(用标准、可靠的方法)真正地复制,所以只能复制对共享函数对象的引用。如果修改了共享的函数对象(比如ignition()),比如添加了一个属性,那 Vehicle 和 Car 都会受到影响。

如果你目标对象中显式混入超过一个对象,就可以部分模仿多重继承行为,但是仍没有直接的方式来处理函数和属性的同名问题。

一定要注意,只在能够提高代码可读性的前提下使用显式混入,避免使用增加代码理解难度或者让对象关系更加复杂的模式。

3. 寄生继承

显式混入模式的一种变体被称为“寄生继承”,它既是显式的又是隐式的,主要推广者是Douglas Crockford。

下面是它的工作原理:

调用 new Car() 时会创建一个新对象并绑定到 Car 的 this 上。但是因为没有使用这个对象而是返回了我们自己的 car 对象,所以最初被创建的这个对象会被丢弃,因此可以不使用 new 关键字调用 Car()。

这样做得到的结果是一样的,但是可以避免创建并丢弃多余的对象。

隐式引入

通过在构造函数调用或者方法调用中使用 Something.cool.call( this ),实际上“借用”了函数 Something.cool() 并在 Another 的上下文中调用了它。最终的结果是 Something.cool() 中的赋值操作都会应用在 Another 对象上而不是Something 对象上。

因此,把 Something 的行为“混入”到了 Another 中。

虽然这类技术利用了 this 的重新绑定功能,但是 Something.cool.call( this ) 仍然无法变成相对(而且更灵活的)引用,所以使用时千万要小心。通常来说,尽量避免使用这样的结构,以保证代码的整洁和可维护性。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容