你的 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新闻滚动插件 jquery.roller.js
Jun 27 Javascript
jQuery JSON实现无刷新三级联动实例探讨
May 28 Javascript
js中复制行和删除行的操作实例
Jun 25 Javascript
wap手机图片滑动切换特效无css3元素js脚本编写
Jul 28 Javascript
js判断某个方法是否存在实例代码
Jan 10 Javascript
javascript实现时间格式输出FormatDate函数
Jan 13 Javascript
EasyUi中的Combogrid 实现分页和动态搜索远程数据
Apr 01 Javascript
JavaScript自定义浏览器滚动条兼容IE、 火狐和chrome
Jan 05 Javascript
JS中SetTimeout和SetInterval使用初探
Mar 23 Javascript
基于javascript的异步编程实例详解
Apr 10 Javascript
优化Vue项目编译文件大小的方法步骤
May 27 Javascript
Layui 动态禁止select下拉的例子
Sep 03 Javascript
谈谈关于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
高分R级DC动画剧《哈莉·奎茵》第二季正式预告首发
2020/04/09 欧美动漫
php checkbox 取值详细说明
2010/08/19 PHP
php截取字符串并保留完整xml标签的函数代码
2013/02/06 PHP
Laravel 5.0 发布 新版本特性详解
2015/02/10 PHP
PHP嵌套输出缓冲代码实例
2015/05/12 PHP
用js实现的抽象CSS圆角效果!!
2007/05/03 Javascript
js调用css属性写法
2013/09/21 Javascript
jQuery下的动画处理总结
2013/10/10 Javascript
jquery进行数组遍历如何跳出当前的each循环
2014/06/05 Javascript
JS实现带缓冲效果打开、关闭、移动一个层的方法
2015/05/09 Javascript
jQuery实现复选框批量选择与反选的方法
2015/06/17 Javascript
js实现点击按钮弹出上传文件的窗口
2016/12/23 Javascript
Vue2.0实现1.0的搜索过滤器功能实例代码
2017/03/20 Javascript
JS设计模式之单例模式(一)
2017/09/29 Javascript
详解js类型判断
2018/05/22 Javascript
JS实现的Object数组去重功能示例【数组成员为Object对象】
2019/02/01 Javascript
vue界面发送表情的实现代码
2020/09/11 Javascript
Python socket C/S结构的聊天室应用实现
2014/11/30 Python
浅谈Python Opencv中gamma变换的使用详解
2018/04/02 Python
关于django 数据库迁移(migrate)应该知道的一些事
2018/05/27 Python
解决使用PyCharm时无法启动控制台的问题
2019/01/19 Python
python生成大写32位uuid代码
2020/03/03 Python
浅谈Python 命令行参数argparse写入图片路径操作
2020/07/12 Python
分享一个python的aes加密代码
2020/12/22 Python
Canvas图片分割效果的实现
2019/07/29 HTML / CSS
加拿大最大的五金、家居装修和园艺产品商店:RONA
2017/01/27 全球购物
台湾前三大B2C购物网站:MOMO购物网
2017/04/27 全球购物
澳大利亚著名的纺织品品牌:Canningvale
2020/05/05 全球购物
销售演讲稿范文
2014/01/08 职场文书
党校学习自我鉴定
2014/02/24 职场文书
小学生家长寄语
2014/04/02 职场文书
销售人员工作自我评价
2014/09/21 职场文书
2014年军人思想汇报范文
2014/10/12 职场文书
国家助学金感谢信
2015/01/21 职场文书
用Python仅20行代码编写一个简单的端口扫描器
2022/04/08 Python
python热力图实现的完整实例
2022/06/25 Python