你的 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 相关文章推荐
Web Inspector:关于在 Sublime Text 中调试Js的介绍
Apr 18 Javascript
jQuery客户端分页实例代码
Nov 18 Javascript
初识Node.js
Mar 20 Javascript
纯Javascript实现ping功能的方法
Mar 20 Javascript
js中获取时间new Date()的全面介绍
Jun 20 Javascript
基于JavaScript实现轮播图原理及示例
Apr 10 Javascript
基于JavaScript实现活动倒计时效果
Apr 20 Javascript
VueJs 将接口用webpack代理到本地的方法
Nov 27 Javascript
详解JavaScript中的函数、对象
Apr 01 Javascript
vue自定义js图片碎片轮播图切换效果的实现代码
Apr 28 Javascript
浅谈一个webpack构建速度优化误区
Jun 24 Javascript
微信小程序云函数使用mysql数据库过程详解
Aug 07 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链接MySQL的常用扩展函数
2014/10/23 PHP
支持png透明图片的php生成缩略图类分享
2015/02/08 PHP
PHP+Ajax实时自动检测是否联网的方法
2015/07/01 PHP
smarty高级特性之过滤器的使用方法
2015/12/25 PHP
php注册登录系统简化版
2020/12/28 PHP
用dom+xhtml+css制作的一个相册效果代码打包下载
2008/01/24 Javascript
js给selected添加options的方法
2015/05/06 Javascript
javascript中mouseover、mouseout使用详解
2015/07/19 Javascript
javascript中SetInterval与setTimeout的定时器用法
2015/08/24 Javascript
Knockoutjs 学习系列(二)花式捆绑
2016/06/07 Javascript
javascript实现数据双向绑定的三种方式小结
2017/03/09 Javascript
vue弹窗组件使用方法
2018/04/28 Javascript
Vue项目数据动态过滤实践及实现思路
2018/09/11 Javascript
详解vue中localStorage的使用方法
2018/11/22 Javascript
python打开url并按指定块读取网页内容的方法
2015/04/29 Python
Python捕捉和模拟鼠标事件的方法
2015/06/03 Python
Python获取CPU、内存使用率以及网络使用状态代码
2018/02/08 Python
python实现本地图片转存并重命名的示例代码
2018/10/27 Python
Python解决两个整数相除只得到整数部分的实例
2018/11/10 Python
Windows下Anaconda和PyCharm的安装与使用详解
2020/04/23 Python
Python+OpenCV图像处理——打印图片属性、设置存储路径、调用摄像头
2020/10/22 Python
python通过cython加密代码
2020/12/11 Python
意大利高端时尚买手店:Stefania Mode
2018/03/01 全球购物
法国票务网站:Ticketmaster法国
2018/07/09 全球购物
如何在C# winform中异步调用web services
2015/09/21 面试题
环境科学专业研究生求职信
2013/10/02 职场文书
医学院学生的自我评价分享
2013/11/19 职场文书
女儿十岁生日答谢词
2014/01/27 职场文书
财务部经理岗位职责
2014/02/03 职场文书
小学中秋节活动方案
2014/02/06 职场文书
阅兵口号
2014/06/19 职场文书
不服从上级领导安排的检讨书
2014/09/14 职场文书
收银员岗位职责
2015/02/03 职场文书
中班上学期个人总结
2015/02/12 职场文书
丧事主持词
2015/07/02 职场文书
2015年幼儿园国庆节活动总结
2015/07/30 职场文书