你的 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 相关文章推荐
日历查询的算法 如何计算某一天是星期几
Dec 12 Javascript
Js 去掉字符串中的空格(实现代码)
Nov 19 Javascript
jquery中append()与appendto()用法分析
Nov 14 Javascript
js实现浏览本地文件并显示扩展名的方法
Aug 17 Javascript
原生js实现autocomplete插件
Apr 14 Javascript
AngularJS入门教程之 XMLHttpRequest实例讲解
Jul 27 Javascript
微信小程序 触控事件详细介绍
Oct 17 Javascript
bootstrap 模态框(modal)实现水平垂直居中显示
Jan 23 Javascript
详解vue父子组件间传值(props)
Jun 29 Javascript
在vue中给列表中的奇数行添加class的实现方法
Sep 05 Javascript
Element input树型下拉框的实现代码
Dec 21 Javascript
解决vue项目中出现Invalid Host header的问题
Nov 17 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
PHP字符转义相关函数小结(php下的转义字符串)
2007/04/12 PHP
php开发环境配置记录
2011/01/14 PHP
使用Smarty 获取当前日期时间和格式化日期时间的方法详解
2013/06/18 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十三)
2014/06/26 PHP
PHP/HTML混写的四种方式总结
2017/02/27 PHP
Yii2压缩PHP中模板代码的输出问题
2018/08/28 PHP
Mootools 1.2教程 Fx.Morph、Fx选项和Fx事件
2009/09/15 Javascript
js动态给table添加/删除tr的方法
2013/08/02 Javascript
可以用鼠标拖动的DIV实现思路及代码
2013/10/21 Javascript
使用jQuery实现的掷色子游戏动画效果
2014/03/14 Javascript
易操作的jQuery表单提示插件
2015/12/01 Javascript
js实现的星星评分功能函数
2015/12/09 Javascript
一个简单的JavaScript Map实例(分享)
2016/08/03 Javascript
微信小程序  生命周期详解
2016/10/27 Javascript
详解基于React.js和Node.js的SSR实现方案
2019/03/21 Javascript
js实现固定区域内的不重叠随机圆
2019/10/24 Javascript
五句话帮你轻松搞定js原型链
2020/12/09 Javascript
Python中计算三角函数之cos()方法的使用简介
2015/05/15 Python
python引入不同文件夹下的自定义模块方法
2018/10/27 Python
用python求一重积分和二重积分的例子
2019/12/06 Python
Python实现ATM系统
2020/02/17 Python
在python3中实现查找数组中最接近与某值的元素操作
2020/02/29 Python
python标准库OS模块函数列表与实例全解
2020/03/10 Python
Python2.7:使用Pyhook模块监听鼠标键盘事件-获取坐标实例
2020/03/14 Python
计算Python Numpy向量之间的欧氏距离实例
2020/05/22 Python
python unichr函数知识点总结
2020/12/16 Python
css3实现3D文本悬停改变效果的示例代码
2019/01/16 HTML / CSS
技术总监的工作职责
2013/11/13 职场文书
法警的竞聘演讲稿
2014/01/02 职场文书
机关办公室岗位职责
2014/04/16 职场文书
六查六看心得体会
2014/10/14 职场文书
2014年护理部工作总结
2014/11/14 职场文书
停课通知书
2015/04/24 职场文书
风雨哈佛路观后感
2015/06/03 职场文书
薪资证明范本
2015/06/19 职场文书
2015暑期社会实践调查报告
2015/07/14 职场文书