你的 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 相关文章推荐
[原创]保存的js无法执行的解决办法
Feb 25 Javascript
javascript 兼容所有浏览器的DOM扩展功能
Aug 01 Javascript
ajax提交表单实现网页无刷新注册示例
May 08 Javascript
JavaScript中统计Textarea字数并提示还能输入的字符
Jun 10 Javascript
由ReactJS的Hello world说开来
Jul 02 Javascript
Angularjs注入拦截器实现Loading效果
Dec 28 Javascript
Javascript实现跑马灯效果的简单实例
May 31 Javascript
原生js封装二级城市下拉列表的实现代码
Jun 16 Javascript
JS设计模式之观察者模式实现实时改变页面中金额数的方法
Feb 05 Javascript
Vue.set() this.$set()引发的视图更新思考及注意事项
Aug 30 Javascript
JS实现简单移动端鼠标拖拽
Jul 23 Javascript
详解如何在Javascript中使用Object.freeze()
Oct 18 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
收音机另类DIY - 纸巾盒做外壳
2021/03/02 无线电
windows下PHP APACHE MYSQ完整配置
2007/01/02 PHP
php入门学习知识点一 PHP与MYSql连接与查询
2011/07/14 PHP
PHP中使用json数据格式定义字面量对象的方法
2014/08/20 PHP
php版微信公众账号第三方管理工具开发简明教程
2016/09/23 PHP
thinkPHP5框架自定义验证器实现方法分析
2018/06/11 PHP
利用PHP扩展Xhprof分析项目性能实践教程
2018/09/05 PHP
centos7上编译安装php7以php-fpm方式连接apache
2018/11/08 PHP
Javascript的构造函数和constructor属性
2010/01/09 Javascript
防止页面被iframe(兼容IE,Firefox火狐)
2010/07/04 Javascript
jquery计算鼠标和指定元素之间距离的方法
2015/06/26 Javascript
jQuery实现类似老虎机滚动抽奖效果
2015/08/06 Javascript
js获取及判断键盘按键的方法
2015/12/01 Javascript
Vue实现双向绑定的方法
2016/12/22 Javascript
jquery事件与绑定事件
2017/03/16 Javascript
详解nodejs微信公众号开发——1.接入微信公众号
2017/04/10 NodeJs
利用npm 安装删除模块的方法
2018/05/15 Javascript
Three.JS实现三维场景
2018/12/30 Javascript
Echarts动态加载多条折线图的实现代码
2019/05/24 Javascript
在VUE中实现文件下载并判断状态的方法
2019/11/08 Javascript
vue通过过滤器实现数据格式化
2020/07/20 Javascript
Python实现将绝对URL替换成相对URL的方法
2015/06/28 Python
【Python】Python的urllib模块、urllib2模块批量进行网页下载文件
2016/11/19 Python
python使用Apriori算法进行关联性解析
2017/12/21 Python
浅谈Python的条件判断语句if/else语句
2019/03/21 Python
python 的topk算法实例
2020/04/02 Python
分享30个新鲜的CSS3打造的精美绚丽效果(附演示下载)
2012/12/28 HTML / CSS
印度第一网上礼品店:IGP.com
2020/02/06 全球购物
国际贸易个人求职信范文
2014/01/04 职场文书
2015社区六五普法工作总结
2015/04/21 职场文书
学校后勤工作总结2015
2015/05/15 职场文书
七年级上册生物的课件
2019/08/07 职场文书
2019年英语版感谢信(8篇)
2019/09/29 职场文书
Oracle更换为MySQL遇到的问题及解决
2021/05/21 Oracle
CSS巧用渐变实现高级感背景光动画
2021/12/06 HTML / CSS
Win11 Dev 预览版25174.1000发布 (附更新修复内容汇总)
2022/08/05 数码科技