你的 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 相关文章推荐
jQuery-onload让第一次页面加载时图片是淡入方式显示
May 23 Javascript
JavaScript位移运算符(无符号) >>> 三个大于号 的使用方法详解
Mar 31 Javascript
JS实现HTML标签转义及反转义
Apr 14 Javascript
js实现1,2,3,5数字按照概率生成
Sep 12 Javascript
vue中$refs的用法及作用详解
Apr 24 Javascript
vue.js仿hover效果的实现方法示例
Jan 28 Javascript
Vue递归组件+Vuex开发树形组件Tree--递归组件的简单实现
Apr 01 Javascript
新手快速入门JavaScript装饰者模式与AOP
Jun 24 Javascript
解决vue初始化项目时,一直卡在Project description上的问题
Oct 31 Javascript
vue(2.x,3.0)配置跨域代理
Nov 27 Javascript
详解react组件通讯方式(多种)
May 06 Javascript
在Vue中获取自定义属性方法:data-id的实例
Sep 09 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重定向的三种方法分享
2012/02/22 PHP
php根据日期判断星座的函数分享
2014/02/13 PHP
smarty模板中拼接字符串的方法
2014/02/14 PHP
ThinkPHP3.1之D方法实例详解
2014/06/20 PHP
php中convert_uuencode()与convert_uuencode函数用法实例
2014/11/22 PHP
php表单敏感字符过滤类
2014/12/08 PHP
yii2使用GridView实现数据全选及批量删除按钮示例
2017/03/01 PHP
javascript 火狐(firefox)不显示本地图片问题解决
2008/07/05 Javascript
JQuery 操作Javascript对象和数组的工具函数小结
2010/01/22 Javascript
jquery默认校验规则整理
2014/03/24 Javascript
JS判断客服QQ号在线还是离线状态的方法
2015/01/13 Javascript
JavaScript之iterable_动力节点Java学院整理
2017/06/29 Javascript
JS中call和apply函数用法实例分析
2018/06/20 Javascript
vue init webpack 建vue项目报错的解决方法
2018/09/29 Javascript
微信自定义分享链接信息(标题,图片和内容)实现过程详解
2019/09/04 Javascript
js 下拉菜单点击旁边收起实现(踩坑记)
2019/09/29 Javascript
JavaScript自定义超时API代码实例
2020/04/30 Javascript
Element Breadcrumb 面包屑的使用方法
2020/07/26 Javascript
[04:39]显微镜下的DOTA2第十三期—Pis卡尔个人秀
2014/04/04 DOTA
python del()函数用法
2013/03/24 Python
Python中实现结构相似的函数调用方法
2015/03/10 Python
在Python的循环体中使用else语句的方法
2015/03/30 Python
浅谈dataframe中更改列属性的方法
2018/07/10 Python
解决Python plt.savefig 保存图片时一片空白的问题
2019/01/10 Python
Python更新所有已安装包的操作
2020/02/13 Python
pycharm下配置pyqt5的教程(anaconda虚拟环境下+tensorflow)
2020/03/25 Python
对Pytorch 中的contiguous理解说明
2021/03/03 Python
英国领先的亚洲旅游专家:Wendy Wu Tours
2018/01/21 全球购物
获取邓白氏信用报告:Dun & Bradstreet
2019/01/22 全球购物
美国传奇滑手Paul Rodriguez创办的街头滑板品牌:Primitive Skateboarding
2019/10/29 全球购物
世界上最伟大的马产品:Equiderma
2020/01/07 全球购物
Tessabit美国:集世界奢侈品和设计师品牌的意大利精品买手店
2020/06/29 全球购物
四查四看自我剖析材料
2014/09/19 职场文书
个人道歉信大全
2019/04/11 职场文书
会议承办单位欢迎词
2019/07/09 职场文书
《最后一头战象》读后感:动物也有感情
2020/01/02 职场文书