你的 mixin 真的兼容 ECMAScript 5 吗?

我最近在与客户合作的项目中,需要充分利用的 ECMAScript 5,在此我遇到一个非常有趣的问题。 该问题源于一个非常常见的模式: mixin , 也就是在 JavaScript 中把一个对象的属性或者方法 mixin 到另一个。

大多数 mixin 的功能看起来像这样:

代码如下:

function mixin(receiver, supplier) {
    for (var property in supplier) {
        if (supplier.hasOwnProperty(property)) {
            receiver[property] = supplier[property];
        }
    }
}

在此 mixin() 函数中,一个 for 循环遍历 supplier 对象的属性并赋值给 receiver 对象。 几乎所有的 JavaScript 库有某种形式的类似功能,让您可以编写这样的代码:

代码如下:

mixin(object, {
    name: "Nicholas",

sayName: function() {
        console.log(this.name);
    }
});

object.sayName();       // outputs "Nicholas"

在此示例中,object 对象接收了属性 name 和方法 sayName()。 这在 ECMAScript 3 中运行良好,但在 ECMAScript 5 上却没那么乐观。

这是我遇到的问题:

代码如下:

(function() {

// to be filled in later
    var name;

mixin(object, {
        get name() {
            return name;
        }
    });

// let's just say this is later
    name = "Nicholas";

}());

console.log(object.name);       // undefined

这个例子看起来有点做作,但它准确的描述这个问题。 进行 mixin 的属性使用了 ECMAScript 5 的新特性:一个 getter 属性存取器。 getter 引用一个未初始化的局部变量 name,因此这个属性未定义 undefined。

后来,name 被分配了一个值,以便使存取器 getter 可以返回一个有效的值。 不幸的是,object.name(被 mixin 的属性)始终返回 undefined。

这是怎么回事呢?

我们仔细分析 mixin() 函数。 事实上,在循环语句中,并没有把属性从一个对象重新赋值给到另一个对象。 它实际上是创建一个同名的属性,并把 supplier 对象的存取器方法 getter 的返回值赋值给了它。 (译注:目标对象得到的不是 getter 这个方法,而是得到了 getter 方法的返回值。@justjavac)

在这个例子中,mixin() 的过程其实是这样的:

代码如下:

receiver.name = supplier.name;

属性 receiver.name 被创建,并且被赋值为 supplier.name 的值。 当然,supplier.name 有一个 getter 方法用来返回本地变量 name 的值。 此时,name 的值为 undefined,所以 receiver.name 存储的是 值。 并没有为 receiver.name 创建一个 getter 方法,因此它的值永远不会改变。

要解决这个问题,你需要使用属性描述符(译注:descriptor)将属性从一个对象 mixin 到另一个对象。 一个纯粹的 ECMAScript 5 版本的 mixin() 应该这样写:

代码如下:

function mixin(receiver, supplier) {

Object.keys(supplier).forEach(function(value, property) {
        Object.defineProperty(receiver, property, Object.getOwnPropertyDescriptor(supplier, property));
    });
}

在这个新版本函数中,Object.keys() 用来获取一个数组,包含了 supplier 对象的所有枚举属性。 然后,foreach() 方法用来遍历这些属性。 调用 Object.getOwnPropertyDescriptor() 方法获取 supplier 对象的每个属性描述符(descriptor)。

由于描述符(descriptor)包含了所有的属性信息,包括 getter 和 setter 方法, 该描述符(descriptor)可以直接传递给 Object.defineProperty() ,用来在 receiver 对象上创建相同的属性。 使用这个新版本的 mixin() ,可以解决前面遇到的问题,从而得到你所期望的结果。 getter 方法被正确地从 supplier 传递到了 receiver。

当然,如果你仍然需要支持旧的浏览器,那么你就需要一个函数,回落的 ECMAScript 3:

代码如下:

function mixin(receiver, supplier) {
    if (Object.keys) {
        Object.keys(supplier).forEach(function(value, property) {
            Object.defineProperty(receiver, property, Object.getOwnPropertyDescriptor(supplier, property));
        });
    } else {
        for (var property in supplier) {
            if (supplier.hasOwnProperty(property)) {
                receiver[property] = supplier[property];
            }
        }
    }
}

如果您需要使用一个 mixin() 函数,一定要仔细检查它在 ECMAScript 5 可以正常工作,特别是 getter 和 setter 方法。 否则,你会发现自己陷入像我一样的错误。

(0)

相关推荐

  • 你的 mixin 真的兼容 ECMAScript 5 吗?

    我最近在与客户合作的项目中,需要充分利用的 ECMAScript 5,在此我遇到一个非常有趣的问题. 该问题源于一个非常常见的模式: mixin , 也就是在 JavaScript 中把一个对象的属性或者方法 mixin 到另一个. 大多数 mixin 的功能看起来像这样: 复制代码 代码如下: function mixin(receiver, supplier) {    for (var property in supplier) {        if (supplier.hasOwnPr

  • Vue+webpack+Element 兼容问题总结(小结)

    项目中用到了Vue.js和Elenment-UI Vue官方文档中给出明确范围:Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性.但它支持所有兼容 ECMAScript 5 的浏览器. 而且Element-UI支持IE10+及大多数浏览器.由此要用到Babel,主要用于将ECMAScript 2015+代码转换为旧浏览器或环境中向后兼容的JavaScript版本.在此之前,需要先搞清楚IE6~11是否支持es5: IE6~IE8:不支持

  • JS中创建自定义类型的常用模式总结【工厂模式,构造函数模式,原型模式,动态原型模式等】

    本文实例讲述了JS中创建自定义类型的常用模式.分享给大家供大家参考,具体如下: 虽然在 ES6 中,已经出了 class 的语法,貌似好像不用了解 ES5 中的这些老东西了,但是越深入学习,你会发现理解这些模式的重要性. 在本文中,我会描述 7 种常用的创建自定义类型的模式:工厂模式.构造函数模式.原型模式.组合使用构造函数模式.动态原型模式.寄生构造函数模式.稳妥构造函数模式.分别给出他们的示例代码,并分析他们的利弊,方便读者选择具体的方式来构建自己的自定义类型. 最后,我会指出 ES6 中的

  • TypeScript具有的几个不同特质

    今天要讨论的话题是TypeScript.之前在转载过一篇<The Rise of TypeScript?>的文章,今天来谈一下我的感想. 很多朋友应该都了解,TypeScript是微软推出的一个开源语言,用于开发大型JS应用程序.其作者是大名鼎鼎的C#之父Anders Hejlsberg.所以从它发布beta之初,我就持续在关注. 最近发生的一件事情--Angular团队打算使用TypeScript来开发Angular 2版本,促使TJ VanToll提出这样一个疑问(观点).而对于他的这个观

  • 正则表达式速查表(ASP.NET)

    出处:RegExLib.com Regular Expression Cheat Sheet (.NET) 元字符 说明 ^ 匹配字符串的开始位置 $ 匹配字符串的结束位置 . 匹配任意单个字符(换行符 \n 除外) | 交替 {-} 指定要限定的数量 [...] 指定要匹配的字符集 (-) 对表达式进行逻辑分组 * 匹配零或多个前面的表达式 + 匹配一或多个前面的表达式 ? 匹配零或一个前面的表达式 \ 放在上面任何一个字符之前,表示匹配该字符本身.放在其他特殊字符后面,表示字符转义(见下面)

  • 深入了解Java 脚本化api编程

    Java脚本化API为谁准备? 脚本语言的一些有用的特性是: 方便:大多数脚本语言都是动态类型的.您通常可以创建新的变量,而不声明变量类型,并且您可以重用变量来存储不同类型的对象.此外,脚本语言往往会自动执行许多类型的转换,例如, 必要时 将数字10转换为"10". 开发快速原型:您可以避免编辑编译运行周期,只使用"编辑运行"! 应用扩展/定制:你可以"具体化"的部分应用程序,例如一些配置脚本,业务逻辑/规则和财务应用中的数学表达式 . 为应用添

  • 详解React Angular Vue三大前端技术

    一.[React] React(也被称为React.js或ReactJS)是一个用于构建用户界面的JavaScript库.它由Facebook和一个由个人开发者和公司组成的社区来维护. React可以作为开发单页或移动应用的基础.然而,React只关注向DOM渲染数据,因此创建React应用通常需要使用额外的库来进行状态管理和路由,Redux和React Router分别是这类库的例子. 基本用法 下面是一个简单的React在HTML中使用JSX和JavaScript的例子. Greeter函数

  • jQuery实现可兼容IE6的滚动监听功能

    本文实例讲述了jQuery实现可兼容IE6的滚动监听功能.分享给大家供大家参考,具体如下: 其实这东西本打算用原生的javascript来写,但是原生的javascript取class与监听滚动条的滚动过于麻烦,因此上了jQuery,没关系,反正兼容IE6就行. 将实现如下的效果: 也就是在网页中有点常见的滚动监听.相应的标题,滚到哪里,则左边的滚动条当前标题前的...就变成>>>,,当然了,左边的标题同样是可以点击,马上滚到该滚的地方. 首先是网页布局部分,代码如下,请忽略大堆的Jav

  • 源码解读jQ中浏览器兼容模块support第1/2页

    前言 jQuery的属性support是判断浏览器之间是否兼容的模块 ,该模块包含了leadingWhitespace,tbody,htmlSerialize,style,hrefNormalized,opacity,cssFloat,checkOn,optSelected,getSetAttribute-.等兼容问题,所有的这些属性也只是jQuery内部会用到,因为jQ内部一些模块需要对这些东西进行判断, 就直接写成了一个support模块, 可以供我们, 但是我们写代码的时基本都没用到啊,

  • 更优雅的事件触发兼容

    问题种种 做底层接口兼容,无非就是利用if,判断客户端支持哪个接口的问题.最著名的例子就是事件: 复制代码 代码如下: var addEvent = function(e, what, how) { if (e.addEventListener) e.addEventListener(what, how, false) else if (e.attachEvent) e.attachEvent('on' + what, how) } 这里考虑了给元素绑定事件时可能遇到的两种状况--标准的W3C

随机推荐