你的 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代码
Oct 30 Javascript
JS的get和set使用示例
Feb 20 Javascript
jquery实现无限分级横向导航菜单的方法
Mar 12 Javascript
几种经典排序算法的JS实现方法
Mar 25 Javascript
微信小程序 教程之WXML
Oct 18 Javascript
Linux CentOS系统下安装node.js与express的方法
Apr 01 Javascript
jQuery使用unlock.js插件实现滑动解锁
Apr 04 jQuery
node.js实现微信开发之获取用户授权
Mar 18 Javascript
详解JWT token心得与使用实例
Aug 02 Javascript
解决三元运算符 报错“SyntaxError: can''t assign to conditional expression”
Feb 12 Javascript
JavaScript 替换所有匹配内容及正则替换方法
Feb 12 Javascript
Ajax实现局部刷新的方法实例
Mar 31 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实现检测客户端是否使用代理服务器及其匿名级别
2015/01/07 PHP
laravel实现Auth认证,登录、注册后的页面回跳方法
2019/09/30 PHP
js 数值项目的格式化函数代码
2010/05/14 Javascript
javascript 防止刷新,后退,关闭
2010/08/07 Javascript
Node.js开发之访问Redis数据库教程
2015/01/14 Javascript
JavaScript简单实现鼠标移动切换图片的方法
2016/02/23 Javascript
原生JS实现瀑布流插件
2018/02/06 Javascript
基于vue-cli vue-router搭建底部导航栏移动前端项目
2018/02/28 Javascript
微信小程序实现无限滚动列表
2020/05/29 Javascript
优雅的elementUI table单元格可编辑实现方法详解
2018/12/23 Javascript
Vue源码学习之关于对Array的数据侦听实现
2019/04/23 Javascript
优雅的使用javascript递归画一棵结构树示例代码
2019/09/22 Javascript
使用JS location实现搜索框历史记录功能
2019/12/23 Javascript
Vue登录拦截 登录后继续跳转指定页面的操作
2020/08/04 Javascript
javascript实现时钟动画
2020/12/03 Javascript
python基础_文件操作实现全文或单行替换的方法
2017/09/04 Python
python 矩阵增加一行或一列的实例
2018/04/04 Python
python实现全盘扫描搜索功能的方法
2019/02/14 Python
详解Python字典的操作
2019/03/04 Python
python仿抖音表白神器
2019/04/08 Python
如何用C代码给Python写扩展库(Cython)
2019/05/17 Python
对tensorflow中tf.nn.conv1d和layers.conv1d的区别详解
2020/02/11 Python
Python的历史与优缺点整理
2020/05/26 Python
使用Keras 实现查看model weights .h5 文件的内容
2020/06/09 Python
python图片验证码识别最新模块muggle_ocr的示例代码
2020/07/03 Python
Python下划线5种含义代码实例解析
2020/07/10 Python
HTML5 Canvas基本线条绘制的实例教程
2016/03/17 HTML / CSS
某公司部分笔试题
2013/11/05 面试题
就业推荐自我鉴定
2013/10/06 职场文书
建筑系毕业生自我鉴定
2014/01/24 职场文书
怎样拟定创业计划书
2014/05/01 职场文书
迟到检讨书范文
2015/01/27 职场文书
2015年班级工作总结范文
2015/04/03 职场文书
css3实现背景图片颜色修改的多种方式
2021/04/13 HTML / CSS
Django基础CBV装饰器和中间件
2022/03/22 Python
浅谈Node的内存泄露问题
2022/05/06 NodeJs