你的 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 1.4 15个你应该知道的新特性(译)
Jan 24 Javascript
jQuery效果 slideToggle() 方法(在隐藏和显示之间切换)
Jun 28 Javascript
使用jQuery UI的tooltip函数修饰title属性的气泡悬浮框
Jun 24 Javascript
js读取被点击次数的简单实例(从数据库中读取)
Mar 07 Javascript
微信小程序 自定义Toast实例代码
Jun 12 Javascript
Bootstrap按钮组实例详解
Jul 03 Javascript
javascript 产生随机数的几种方法总结
Sep 26 Javascript
React Native实现地址挑选器功能
Oct 24 Javascript
vue使用中的内存泄漏【推荐】
Jul 10 Javascript
bootstrap-table formatter 使用vue组件的方法
May 09 Javascript
使用apifm-wxapi模块中的问题及解决方法
Aug 05 Javascript
javascript实现拖拽碰撞检测
Mar 12 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
多文件上传的例子
2006/10/09 PHP
PHP PDO函数库详解
2010/04/27 PHP
php入门学习知识点三 PHP上传
2011/07/14 PHP
PHP乱码问题,UTF-8乱码常见问题小结
2012/04/09 PHP
解析如何在PHP下载文件名中解决乱码的问题
2013/06/20 PHP
ThinkPHP3.2.2的插件控制器功能
2015/03/05 PHP
php json中文编码为null的解决办法
2016/12/14 PHP
防止动态加载JavaScript引起的内存泄漏问题
2009/10/08 Javascript
javascript计算当月剩余天数(天数计算器)示例代码
2014/01/09 Javascript
改变状态栏文字的js代码
2014/06/13 Javascript
js和jquery设置disabled属性为true使按钮失效
2014/08/07 Javascript
JavaScript中的toLocaleDateString()方法使用简介
2015/06/12 Javascript
原生javascript实现解析XML文档与字符串
2016/03/01 Javascript
javascript设计模式Constructor(构造器)模式
2016/08/19 Javascript
JavaScript代码执行的先后顺序问题
2017/10/29 Javascript
weui中的picker使用js进行动态绑定数据问题
2019/11/06 Javascript
原生js实现点击轮播切换图片
2020/02/11 Javascript
一些常用的Python爬虫技巧汇总
2016/09/28 Python
用Python登录好友QQ空间点赞的示例代码
2017/11/04 Python
python PyTorch参数初始化和Finetune
2018/02/11 Python
更换Django默认的模板引擎为jinja2的实现方法
2018/05/28 Python
Python中函数参数调用方式分析
2018/08/09 Python
python单例模式实例解析
2018/08/28 Python
Python拆分大型CSV文件代码实例
2019/10/07 Python
Python实现将蓝底照片转化为白底照片功能完整实例
2019/12/13 Python
Python timer定时器两种常用方法解析
2020/01/20 Python
4行Python代码生成图像验证码(2种)
2020/04/07 Python
印度购物网站:TATA CLiQ
2017/11/23 全球购物
李维斯牛仔裤英国官方网站:Levi’s英国
2019/10/10 全球购物
这76道Java面试题及答案,祝你能成功通过面试
2016/04/16 面试题
计算机网络专业求职信
2014/06/05 职场文书
法院干警四风问题自我剖析材料
2014/09/29 职场文书
清明节扫墓活动总结
2015/02/09 职场文书
如何书写邀请函?
2019/06/24 职场文书
餐厅开业活动方案
2019/07/08 职场文书
Python上下文管理器Content Manager
2021/06/26 Python