你的 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 相关文章推荐
JavaScript根据数据生成百分比图和柱状图的实例代码
Jul 14 Javascript
js setTimeout 参数传递使用介绍
Aug 13 Javascript
a标签click和href执行顺序探讨
Jun 23 Javascript
基于豆瓣API+Angular开发的web App
Jan 02 Javascript
浅析script标签中的defer与async属性
Nov 30 Javascript
vue组件中点击按钮后修改输入框的状态实例代码
Apr 14 Javascript
javaScript中封装的各种写法示例(推荐)
Jul 03 Javascript
微信小程序自定义组件的实现方法及自定义组件与页面间的数据传递问题
Oct 09 Javascript
vue中导出Excel表格的实现代码
Oct 18 Javascript
小程序封装路由文件和路由方法(5种全解析)
May 26 Javascript
vue 扩展现有组件的操作
Aug 14 Javascript
node koa2 ssr项目搭建的方法步骤
Dec 11 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/11/16 PHP
深入解析php之apc
2013/05/15 PHP
PHP获取和操作配置文件php.ini的几个函数介绍
2013/06/24 PHP
PHP用身份证号获取星座和生肖的方法
2013/11/07 PHP
jquery+thinkphp实现跨域抓取数据的方法
2016/10/15 PHP
PHP 实现从数据库导出到.csv文件方法
2017/07/06 PHP
PHP PDOStatement::rowCount讲解
2019/02/01 PHP
用JavaScript调用WebService的示例
2008/04/07 Javascript
IE6不能修改NAME问题的解决方法
2010/09/03 Javascript
JavaScript下利用fso判断文件是否存在的代码
2010/12/11 Javascript
jQuery Mobile页面跳转后未加载外部JS原因分析及解决
2013/03/18 Javascript
PageSwitch插件实现100种不同图片切换效果
2015/07/28 Javascript
基于JS实现横线提示输入验证码随验证码输入消失(js验证码的实现)
2016/10/27 Javascript
详解Angular 中 ngOnInit 和 constructor 使用场景
2017/06/22 Javascript
vue.js评论发布信息可插入QQ表情功能
2017/08/08 Javascript
JavaScript实现音乐自动切换和轮播
2017/11/05 Javascript
AngularJS实时获取并显示密码的方法
2018/02/06 Javascript
vue-router 源码实现前端路由的两种方式
2018/07/02 Javascript
VuePress 快速踩坑小结
2019/02/14 Javascript
webpack常用构建优化策略小结
2019/11/21 Javascript
JS如何实现网站中PC端和手机端自动识别并跳转对应的代码
2020/01/08 Javascript
vue实现简单加法计算器
2020/10/22 Javascript
[05:05]第三天的dota2
2013/07/29 DOTA
Linux下Python获取IP地址的代码
2014/11/30 Python
python内存管理分析
2015/04/08 Python
python爬取淘宝商品详情页数据
2018/02/23 Python
使用Python完成15位18位身份证的互转功能
2019/11/06 Python
python每5分钟从kafka中提取数据的例子
2019/12/23 Python
Python脚本如何在bilibili中查找弹幕发送者
2020/06/04 Python
关于HTML5语义标签的实践(blog页面)
2016/07/12 HTML / CSS
美国购车网站:TrueCar
2016/10/19 全球购物
语文教育专业推荐信范文
2013/11/25 职场文书
普通党员个人剖析材料
2014/10/08 职场文书
体育教师个人总结
2015/02/09 职场文书
公司老总年会致辞
2015/07/30 职场文书
MySQL性能压力基准测试工具sysbench的使用简介
2021/04/21 MySQL