你的 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中暂停功能的实现代码
Mar 04 Javascript
用js生产批量批处理执行命令
Jul 28 Javascript
使用javascript做的一个随机点名程序
Feb 13 Javascript
关于Jquery中的事件绑定总结
Oct 26 Javascript
新手学习前端之js模仿淘宝主页网站
Oct 31 Javascript
vue打包后显示空白正确处理方法
Nov 01 Javascript
Angular4实现图片上传预览路径不安全的问题解决
Dec 25 Javascript
使用 Vue cli 3.0 构建自定义组件库的方法
Apr 30 Javascript
javascript自定义日期比较函数用法示例
Jul 22 Javascript
vue draggable resizable gorkys与v-chart使用与总结
Sep 05 Javascript
VUEX-action可以修改state吗
Nov 19 Javascript
js用正则表达式筛选年月日的实例方法
Jan 04 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
thinkphp微信开之安全模式消息加密解密不成功的解决办法
2015/12/02 PHP
PHP函数nl2br()与自定义函数nl2p()换行用法分析
2016/04/02 PHP
php微信公众平台开发(三)订阅事件处理
2016/12/06 PHP
php输出反斜杠的实例方法
2019/09/19 PHP
prototype 中文参数乱码解决方案
2009/11/09 Javascript
JavaScript 申明函数的三种方法 每个函数就是一个对象(一)
2009/12/04 Javascript
限制textbox或textarea输入字符长度的JS代码
2013/10/16 Javascript
jquery map方法使用示例
2014/04/23 Javascript
FF(火狐)浏览器无法执行window.close()解决方案
2014/11/13 Javascript
jQuery如何防止这种冒泡事件发生
2015/02/27 Javascript
Bootstrap每天必学之基础排版
2015/11/20 Javascript
ionic js 模型 $ionicModal 可以遮住用户主界面的内容框
2016/06/06 Javascript
jQuery双向列表选择器DIV模拟版
2016/11/01 Javascript
js, jQuery实现全选、反选功能
2017/03/08 Javascript
Vue创建头部组件示例代码详解
2018/10/23 Javascript
小程序实现多选框功能
2018/10/30 Javascript
js基于canvas实现时钟组件
2021/02/07 Javascript
为python设置socket代理的方法
2015/01/14 Python
详解python中的json的基本使用方法
2016/12/21 Python
Selenium 模拟浏览器动态加载页面的实现方法
2018/05/16 Python
python实现对服务器脚本敏感信息的加密解密功能
2019/08/13 Python
基于Python+Appium实现京东双十一自动领金币功能
2019/10/31 Python
信号生成及DFT的python实现方式
2020/02/25 Python
Python发送邮件封装实现过程详解
2020/05/09 Python
Python基于smtplib协议实现发送邮件
2020/06/03 Python
python 实现两个npy档案合并
2020/07/01 Python
Python Sqlalchemy如何实现select for update
2020/10/12 Python
python实现图片,视频人脸识别(dlib版)
2020/11/18 Python
python+openCV对视频进行截取的实现
2020/11/27 Python
学期自我鉴定范文
2013/10/01 职场文书
电子商务自荐书范文
2014/01/04 职场文书
给男朋友的道歉短信
2015/05/12 职场文书
2016年精神文明建设先进个人事迹材料
2016/02/29 职场文书
MySQL如何构建数据表索引
2021/05/13 MySQL
Java 获取Word中所有的插入和删除修订的方法
2022/04/06 Java/Android
vue实现拖拽交换位置
2022/04/07 Vue.js