你的 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 相关文章推荐
Domino中运用jQuery读取视图内容的方法
Oct 21 Javascript
javascript 最常用的10个自定义函数[推荐]
Dec 26 Javascript
Jquery命名冲突解决的五种方案分享
Mar 16 Javascript
JavaScript基础知识学习笔记
Dec 02 Javascript
node.js中的fs.fstatSync方法使用说明
Dec 15 Javascript
如何解决手机浏览器页面点击不跳转浏览器双击放大网页
Jul 01 Javascript
js+css3实现旋转效果
Jan 20 Javascript
常用jQuery选择器汇总
Feb 02 Javascript
jQuery Datatable 多个查询条件自定义提交事件(推荐)
Aug 24 jQuery
layui 上传图片 返回图片地址的方法
Sep 26 Javascript
JavaScript实现省市联动效果
Nov 22 Javascript
vue实现简易音乐播放器
Aug 14 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
php下检测字符串是否是utf8编码的代码
2008/06/28 PHP
php实现多维数组中每个单元值(数字)翻倍的方法
2015/02/16 PHP
Laravel中的Auth模块详解
2017/08/17 PHP
jQuery 过滤not()与filter()实例代码
2012/05/10 Javascript
javascript获取作用在元素上面的样式属性代码
2012/09/20 Javascript
javascript限制用户只能输汉字中文的方法
2014/11/20 Javascript
nodejs教程之异步I/O
2014/11/21 NodeJs
jQuery中removeAttr()方法用法实例
2015/01/05 Javascript
javascript制作网页图片上实现下雨效果
2015/02/26 Javascript
JavaScript中的Math.E属性使用详解
2015/06/12 Javascript
JS获取文件大小方法小结
2015/12/08 Javascript
JS禁止查看网页源代码的实现方法
2016/10/12 Javascript
借助node实战JSONP跨域实例
2017/03/30 Javascript
微信小程序 支付后台java实现实例
2017/05/09 Javascript
three.js中3D视野的缩放实现代码
2017/11/16 Javascript
用最少的JS代码写出贪吃蛇游戏
2018/01/12 Javascript
jQuery实现点击自身以外区域关闭弹出层功能完整示例【改进版】
2018/07/31 jQuery
JS实现电话号码的字母组合算法示例
2019/02/26 Javascript
浅析JavaScript 函数防抖和节流
2020/07/13 Javascript
js在HTML的三种引用方式详解
2020/08/29 Javascript
小程序点餐界面添加购物车左右摆动动画
2020/09/23 Javascript
[43:48]Ti4正赛第一天 VG vs NEWBEE 2
2014/07/19 DOTA
Python函数式编程指南(三):迭代器详解
2015/06/24 Python
Python3.7实现中控考勤机自动连接
2018/08/28 Python
在Python中pandas.DataFrame重置索引名称的实例
2018/11/06 Python
python爬取淘宝商品销量信息
2018/11/16 Python
Python绘制堆叠柱状图的实例
2019/07/09 Python
HTML5本地存储和本地数据库实例详解
2017/09/05 HTML / CSS
团工委书记自荐书范文
2013/12/17 职场文书
咖啡厅创业计划书范本
2014/01/22 职场文书
大四毕业生自荐书
2014/07/05 职场文书
暖春观后感
2015/06/08 职场文书
银行柜员工作心得体会
2016/01/23 职场文书
导游词之天津盘山
2019/11/01 职场文书
Netty分布式客户端处理接入事件handle源码解析
2022/03/25 Java/Android
Redis keys命令的具体使用
2022/06/05 Redis