你的 mixin 真的兼容 ECMAScript 5 吗?


Posted in Javascript onApril 11, 2013

我最近在与客户合作的项目中,需要充分利用的 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 方法。 否则,你会发现自己陷入像我一样的错误。

Javascript 相关文章推荐
Jquery中LigerUi的弹出编辑框(实现方法)
Jul 09 Javascript
JavaScript代码复用模式详解
Nov 07 Javascript
JavaScript解析json格式数据简单示例
Dec 09 Javascript
JavaScript中Cookie操作实例
Jan 09 Javascript
js中setTimeout()与clearTimeout()用法实例浅析
May 12 Javascript
jquery验证手机号是否正确实例讲解
Nov 17 Javascript
vue实现动态数据绑定
Apr 28 Javascript
深究AngularJS中$sce的使用
Jun 12 Javascript
LayerClose弹窗关闭刷新方法
Aug 17 Javascript
解决微信小程序云开发中获取数据库的内容为空的方法
May 15 Javascript
构建Vue大型应用的10个最佳实践(小结)
Nov 07 Javascript
详解Vue2的diff算法
Jan 06 Vue.js
谈谈关于JavaScript 中的 MVC 模式
Apr 11 #Javascript
在JavaScript并非所有的一切都是对象
Apr 11 #Javascript
在JavaScript中typeof的用途介绍
Apr 11 #Javascript
浅谈关于JavaScript的语言特性分析
Apr 11 #Javascript
javascript中的delete使用详解
Apr 11 #Javascript
将字符串转换成gb2312或者utf-8编码的参数(js版)
Apr 10 #Javascript
原生js实现给指定元素的后面追加内容
Apr 10 #Javascript
You might like
Mysql的Root密码忘记,查看或修改的解决方法(图文介绍)
2013/06/14 PHP
PHP中的排序函数sort、asort、rsort、krsort、ksort区别分析
2014/08/18 PHP
PHP微信公众号自动发送红包API
2016/06/01 PHP
IE图片缓存document.execCommand("BackgroundImageCache",false,true)
2011/03/01 Javascript
Javascript实现DIV滚动自动滚动到底部的代码
2012/03/01 Javascript
浏览器的JavaScript引擎的识别方法
2013/10/20 Javascript
JavaScript避免代码的重复执行经验技巧分享
2014/04/17 Javascript
JavaScript+canvas实现七色板效果实例
2016/02/18 Javascript
Bootstrap教程JS插件弹出框学习笔记分享
2016/05/17 Javascript
jquery.serialize() 函数语法及简单实例
2016/07/08 Javascript
VUE前端cookie简单操作
2017/10/17 Javascript
vue 添加vux的代码讲解
2017/11/30 Javascript
JS设计模式之状态模式概念与用法分析
2018/02/05 Javascript
JavaScript 判断对象中是否有某属性的常用方法
2018/06/14 Javascript
Vue2.x通用编辑组件的封装及应用详解
2019/05/28 Javascript
python中__call__方法示例分析
2014/10/11 Python
python实现模拟按键,自动翻页看u17漫画
2015/03/17 Python
python中range()与xrange()用法分析
2016/09/21 Python
Python解惑之True和False详解
2017/04/24 Python
浅谈python 线程池threadpool之实现
2017/11/17 Python
python正则表达式去除两个特殊字符间的内容方法
2018/12/24 Python
python 利用pandas将arff文件转csv文件的方法
2019/02/12 Python
对python周期性定时器的示例详解
2019/02/19 Python
Python二进制文件读取并转换为浮点数详解
2019/06/25 Python
Python3内置模块之base64编解码方法详解
2019/07/13 Python
python实现递归查找某个路径下所有文件中的中文字符
2019/08/31 Python
python 监测内存和cpu的使用率实例
2019/11/28 Python
详解Python yaml模块
2020/09/23 Python
幼儿园校车司机的岗位职责
2014/01/30 职场文书
党的群众路线教育实践方案
2014/05/11 职场文书
幼儿园大班开学寄语
2014/08/02 职场文书
民主评议党员个人总结
2015/02/13 职场文书
公司捐书倡议书
2015/04/27 职场文书
拥有这5个特征人,“命”都不会太差
2019/08/16 职场文书
springcloud之Feign超时问题的解决
2021/06/24 Java/Android
MySQL中order by的使用详情
2021/11/17 MySQL