你的 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 相关文章推荐
让AJAX不依赖后端接口实现方案
Dec 03 Javascript
JavaScript创建类/对象的几种方式概述及实例
May 06 Javascript
javascript数组详解
Oct 22 Javascript
简介JavaScript中Math.LOG10E属性的使用
Jun 14 Javascript
浅谈JavaScript 的执行顺序
Aug 07 Javascript
JQuery中解决重复动画的方法
Oct 17 Javascript
js实现键盘自动打字效果
Dec 23 Javascript
微信小程序时间标签和时间范围的联动效果
Feb 15 Javascript
vue组件间的参数传递实例详解
Apr 26 Javascript
vue中axios的二次封装实例讲解
Oct 14 Javascript
在antd4.0中Form使用initialValue操作
Nov 02 Javascript
vue二选一tab栏切换新做法实现
Jan 19 Vue.js
谈谈关于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类
2006/10/09 PHP
用PHP实现ODBC数据分页显示一例
2006/10/09 PHP
mysql 的 like 问题,超强毕杀记!!!
2007/01/18 PHP
详解ThinkPHP3.2.3验证码显示、刷新、校验
2016/12/29 PHP
详谈PHP中的密码安全性Password Hashing
2017/02/04 PHP
thinkPHP5.0框架URL访问方法详解
2017/03/18 PHP
浅谈PHP中的Trait使用方法
2019/03/22 PHP
PHP单文件上传原理及上传函数的封装操作示例
2019/09/02 PHP
JS获取dom 对象 ajax操作 读写cookie函数
2009/11/18 Javascript
javascript 触发HTML元素绑定的函数
2010/09/11 Javascript
Notify - 基于jquery的消息通知插件
2011/10/18 Javascript
JS实现图片翻书效果示例代码
2013/09/09 Javascript
jquery用offset()方法获得元素的xy坐标
2014/09/06 Javascript
Javascript中设置默认参数值示例
2014/09/11 Javascript
$("").click与onclick的区别示例介绍
2014/09/25 Javascript
使用node.js 获取客户端信息代码分享
2014/11/26 Javascript
Javascript基础教程之argument 详解
2015/01/18 Javascript
jQuery实现在下拉列表选择时获取json数据的方法
2015/04/16 Javascript
Javascript中prototype属性实现给内置对象添加新的方法
2015/05/14 Javascript
JS实现iframe自适应高度的方法示例
2017/01/07 Javascript
JavaScript表单验证的两种实现方法
2017/02/11 Javascript
vue 登录滑动验证实现代码
2018/08/24 Javascript
vue.js指令v-for使用以及下标索引的获取
2019/01/31 Javascript
jQuery实现判断滚动条滚动到document底部的方法分析
2019/08/27 jQuery
从零开始在vue-cli4配置自适应vw布局的实现
2020/06/08 Javascript
python 获取et和excel的版本号
2009/04/09 Python
基于wxpython开发的简单gui计算器实例
2015/05/30 Python
Python subprocess库的使用详解
2018/10/26 Python
Python 实现取矩阵的部分列,保存为一个新的矩阵方法
2018/11/14 Python
Python配置虚拟环境图文步骤
2019/05/20 Python
tensorflow 保存模型和取出中间权重例子
2020/01/24 Python
解决jupyter notebook import error但是命令提示符import正常的问题
2020/04/15 Python
印度尼西亚最完整和最大的在线药房网站:Farmaku.com
2019/11/23 全球购物
助人为乐好少年事迹材料
2014/08/18 职场文书
培训后的感想
2015/08/07 职场文书
员工旷工检讨书
2015/08/15 职场文书