ECMAScript 2015之前,Javascript中的对象字面量(也称为对象初始化器)是非常基础的。能够定义两种类型的属性:

  • 成对出现的名称以及相应的值{ name1: value1 }

  • Getters { get name(){..} } 以及setters { set name(val){..} } 可以用于动态的属性值。

遗憾的是,这个对象字面量可能会出现下面这样的情况:

var myObject = {
myString: 'value 1',
get myNumber() {
return this._myNumber;
},
set myNumber(value) {
this._myNumber = Number(value);
}
};
myObject.myString; // => 'value 1'
myObject.myNumber = '15';
myObject.myNumber; // => 15

Javascript一个基于原型的语言,所以其中所有的皆是对象。所以必须在创建对象,配置以及访问原型的时候必须提供一个便利的构建方式。

通常都会涉及到对象的定义和对象原型的设置。我经常觉得对于原型的设置应该允许直接在对象字面量进行,用一条语句即可。

不幸的是,对象字面量的限制不允许通过使用一个直接的方法来达到这个目的。你必须通过结合使用Object.create()以及对象字面量来设置原型:

var myProto = {
propertyExists: function(name) {
return name in this;
}
};
var myNumbers = Object.create(myProto);
myNumbers['array'] = [1, 6, 7];
myNumbers.propertyExists('array'); // => true
myNumbers.propertyExists('collection'); // => false

我觉得这是一个让人很不爽的解决方案。Javascript既然是一个基于原型的语言,为什么还要花这么大力气从一个原型中创建对象。

幸运的是,这个语言每天都在变化。Javascript很多令人沮丧的地方也都在一步步地被解决。

这篇文章解释了ES2015是如何解决上述问题并通过以下额外的好处来提高对象字面量:

  • 在对象构造函数中设置原型
  • 简单函数声明
  • 利用super来调用
  • 动态的属性名称

我们也可展望下未来可以下心的提议在(第二部分):通过使用对象中的rest以及spread属性

1.在对象构造函数中设置原型

你已经知道可以通过使用这个getter 属性__proto__来访问一个对象的原型:

var myObject = {
name: 'Hello World!'
};
myObject.__proto__; // => {}
myObject.__proto__.isPrototypeOf(myObject); // => true

myObject.__proto__返回myObject的原型对象。

好消息是ES2015允许使用对象字面量__proto__作为属性名在对象字面量{ __proto__: protoObject }中来设置原型。

让我们来使用__proto__来初始化一个对象从而改善上述我们提到的糟糕的情形:

var myProto = {
propertyExists: function(name) {
return name in this;
}
};
var myNumbers = {
__proto__: myProto,
array: [1, 6, 7]
};
myNumbers.propertyExists('array'); // => true
myNumbers.propertyExists('collection'); // => false

As you know already, one option to access the prototype of an existing object is using the getter property __proto__:

var myObject = {
name: 'Hello World!'
};
myObject.__proto__; // => {}
myObject.__proto__.isPrototypeOf(myObject); // => true

myNumbers通过一个特别的属性名称__proto__来使用myProto的原型。这个对象通过一句话就可以声明,不需要额外的函数比如Object.create()

很显然,使用__proto__十分简单。我往往更喜欢这种简单并且有效的解决方案。

有点偏离主题了。我觉得奇怪的是一个简单并且灵活的解决方案往往需要大量的工作和设计。如果一个解决方案是简单的,你可能觉得它很容易设计。然而恰恰相反:

  • 让它简明直接是十分复杂的
  • 让它复杂并且难以理解是很简单的

如果有的东西看起来很复杂或者用起来很麻烦,那么它可能在设计的时候考虑的不是很充分。所以你对于简便的观点是什么呢?

2.1__proto__使用的一些特别案例

即使__proto__看起来十分简单,但是还是又一些特殊的情形需要注意:

只允许在对象字面量中使用一次__proto__。一旦重复使用就会出现下面的错误:

var object = {
__proto__: {
toString: function() {
return '[object Numbers]'
}
},
numbers: [1, 5, 89],
__proto__: {
toString: function() {
return '[object ArrayOfNumbers]'
}
}
};

在这个例子中,对象中使用了2次__proto__属性,这个是不被允许的。在这种情形下出现报错SyntaxError: Duplicate __proto__ fields are not allowed in object literals

Javascript只允许对象或者null来使用__proto__属性。任何尝试通过基本类型(strings,numbers,booleans)或者undefined来使用只会被忽略掉并不会改变对象的原型。

让我们在看一下例子吧:

var objUndefined = {
__proto__: undefined
};
Object.getPrototypeOf(objUndefined); // => {}
var objNumber = {
__proto__: 15
};
Object.getPrototypeOf(objNumber); // => {}

这个对象字面量通过使用undefined以及数字15来设置__proto__值。因为只有对象或者null才允许成为原型,objUndefined以及objNumber依然有他们自己默认的原型:普通的Javascript对象。__proto属性会被忽略。

当然,通过使用基本类型来设置对象的原型也很奇怪,所以这里的限制也是理所当然的了。

2. Shorthand method definition

简单函数声明

在对象字面量中可以通过使用一个简短的表达式来声明方法,通过这种方式关键字function以及符号:可以省略。这个就叫做简单函数声明。

让我们使用新的简短的形式来定义函数:

var collection = {
items: [],
add(item) {
this.items.push(item);
},
get(index) {
return this.items[index];
}
};
collection.add(15);
collection.add(3);
collection.get(0); // => 15

add() and get() are methods defined in collection using a short form.

collection通过一种简短的形式来定义add()get() 方法。

这样做的好处之一是声明的方法就是命名的函数,这对于调试来说很有利。通过执行collection.add.name就会返回上述例子中的方法add的名称。

3.通过super来调用

一个有趣的提升是通过关键字super就可以访问从原型链中继承的属性,如下所示:

var calc = {
sumArray (items) {
return items.reduce(function(a, b) {
return a + b;
});
}
};
var numbers = {
__proto__: calc,
numbers: [4, 6, 7],
sumElements() {
return super.sumArray(this.numbers);
}
};
numbers.sumElements(); // => 17

calc就是numbers对象的原型。在numbers对象中的sumElements方法中,可以通过利用关键字super:super.sumArray()来访问原型中的方法。

super是反问对象原型链中继承属性的快捷方式。

3.1 super使用限制

super只能被用于对象字面量中简单方法的定义。

如果常识通过一个普通的函数声明来访问{ name: function() {} },Javascript就会抛出错误:

var calc = {
sumArray (items) {
return items.reduce(function(a, b) {
return a + b;
});
}
};
var numbers = {
__proto__: calc,
numbers: [4, 6, 7],
sumElements: function() {
return super.sumArray(this.numbers);
}
};
// Throws SyntaxError: 'super' keyword unexpected here
numbers.sumElements();

方法sumElements是以属性的方式来定义:sumElements: function() {…}。因为super只能在简单函数中使用,在这种情形调用的话就回抛错SyntaxError: 'super' keyword unexpected here

这一限制并太会影响对象字面量声明的方式。通常在对象字面量中更多的会使用简单函数定义。

4.动态的属性名称

在ES2015之间,对象初始化器中的属性名称只是字面上的,大多数都是静态字符串。为了创建一个具有动态名称的属性,你必须使用属性访问器:

function prefix(prefStr, name) {
return prefStr + '_' + name;
}
var object = {};
object[prefix('number', 'pi')] = 3.14;
object[prefix('bool', 'false')] = false;
object; // => { number_pi: 3.14, bool_false: false }

很显然,这种定义属性的方式远远不能让人满意。动态属性命名以优雅的方式解决了这个问题。

当通过一个表达式来获得属性的名称的时候,把代码放在一个中括号里面{[expression]:value}。这个表达式最终的结果就会成为这个属性的名称。

我很喜欢这种方式:简短。

我们再升级一下:

function prefix(prefStr, name) {
return prefStr + '_' + name;
}
var object = {
[prefix('number', 'pi')]: 3.14,
[prefix('bool', 'false')]: false
};
object; // => { number_pi: 3.14, bool_false: false }

[prefix('number', 'pi')]通过计算表达式prefix('number', 'pi')的值来获得属性的名称。

相应的 [prefix('bool', 'false')]将第二个属性名称命名为'bool_false'

4.1 将Symbol作为属性名称

Symbols也能够被用于动态属性名称。只要确保在中括号中包含它们:{ [Symbol('name')]: 'Prop value' }

比如,让我们使用一个特别的属性Symbol.iterator 来遍历对象中的属性。示例如下:

var object = {
number1: 14,
number2: 15,
string1: 'hello',
string2: 'world',
[Symbol.iterator]: function *() {
var own = Object.getOwnPropertyNames(this),
prop;
while(prop = own.pop()) {
yield prop;
}
}
}
[...object]; // => ['number1', 'number2', 'string1', 'string2']

[Symbol.iterator]: function *() { } 定义了一个属性来用于遍历对象中的属性。这个spread操作符[...object] 用迭代器访问并返回属性列表。

5. 展望未来:rest以及spread属性

对象字面量中rest以及spread属性是接下来的草案,这将可能作为新版本javascript的新特性。

在ECMAScript 2015中的数组意境存在一个替代物。

Rest属性 允许收集解构赋值后遗留的对象中的属性。

下面这个例子就是在解构赋值object之后收集剩余的属性:

var object = {
propA: 1,
propB: 2,
propC: 3
};
let {propA, ...restObject} = object;
propA; // => 1
restObject; // => { propB: 2, propC: 3 }

Spread属性 允许从源对象中拷贝属性到另一个对象字面量中。在下面的例子中对象字面量中的额外属性来自于源对象。

var source = {
propB: 2,
propC: 3
};
var object = {
propA: 1,
...source
}
object; // => { propA: 1, propB: 2, propC: 3 }

6. 总结

Javascript正在飞速前进。

即使一个相当小的对象字面量的构建在ECMAScript 2015都得到了相当可观的提升。更多的特性在草案议程上。

你可以通过直接设置__proto__这个属性名称来直接设置对象的原型。在简单函数声明中可以通过使用关键字super来轻松地访问对象原型链中所继承的属性。

如果一个属性的名称是实时获得的,现在你可以通过使用动态属性名称[expression]来初始化对象。

诚然,对象字面量真是酷爆了!!!

欢迎评论。

_

javascript中的对象字面量为啥这么酷的更多相关文章

  1. 在JavaScript里的“对象字面量”是什么意思?

    字面量表示如何表达这个值,一般除去表达式,给变量赋值时,等号右边都可以认为是字面量.字面量分为字符串字面量(string literal ).数组字面量(array literal)和对象字面量(ob ...

  2. javascript中对象字面量的理解

    javascript中对象字面量与数组字面量 第一部分 我们知道JavaScript中的数据类型有基本数据类型和引用类型,其中Object类型就是非常常用的类型.那么如果创建一个Object类型的实例 ...

  3. javascript中对象字面量与数组字面量

    第一部分 我们知道JavaScript中的数据类型有基本数据类型和引用类型,其中Object类型就是非常常用的类型.那么如果创建一个Object类型的实例呢?下面我介绍两种方法: 第一:构造函数法. ...

  4. javaScript高级教程(九) ------javascript对象字面量--------困扰已久的问题

    在编程语言中,字面量是一种表示值的记法.例如,"Hello, World!" 在许多语言中都表示一个字符串字面量(string literal ),JavaScript也不例外. ...

  5. JSON字符串和Javascript对象字面量

    JSON字符串和Javascript对象字面量 JSON是基于Javascript语法的一个子集而创建的,特别是对象和数组字面量语法. 正是由于JSON的这种特殊来历,导致很多Javascript程序 ...

  6. JavaScript 对象字面量

    JavaScript 对象字面量   JavaScript 对象字面量 在编程语言中,字面量是一种表示值的记法.例如,"Hello, World!" 在许多语言中都表示一个字符串字 ...

  7. js中对象字面量

    一.对象字面量语法 var person={ name:'小王', age:18, _pri:233 } 成员名称的单引号不是必须的 最后一个成员结尾不要用逗号,不然在某些浏览器中会抛出错误 成员名相 ...

  8. JavaScript对象字面量

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  9. Java常量,变量,对象(字面量)在JVM内存中的存储位置

    Java常量,变量,对象(字面量)在JVM内存中的存储位置 2019-02-26 18:13:09 HD243608836 阅读数 540  收藏 更多 分类专栏: JAVA jvm   苦苦研究了快 ...

随机推荐

  1. HTML中的marquee标签实现滚动效果

    一.标签<marquee>简介 通过开始标签<marquee>和结束标签</marquee>的共同配合而实现滚动效果,<marquee>滚动的内容< ...

  2. bootstrap datetimepicker 位置错误

    bootstrap datetimepicker 位置错误,大致问题跟其他网友描述的一样,页面自动出滚动条,然后datetimepicker飘到页脚,网上的方法都是修改place方法里面的555行左右 ...

  3. UVALive 6858 Frame (模拟)

    Frame 题目链接: http://acm.hust.edu.cn/vjudge/contest/130303#problem/D Description http://7xjob4.com1.z0 ...

  4. 基于Anaconda安装Tensorflow 并实现在Spyder中的应用

    基于Anaconda安装Tensorflow 并实现在Spyder中的应用 Anaconda可隔离管理多个环境,互不影响.这里,在anaconda中安装最新的python3.6.5 版本. 一.安装 ...

  5. 启用和配置 FILESTREAM

    2017/08/23 在开始使用 FILESTREAM 之前,必须在 SQL Server 数据库引擎实例中启用 FILESTREAM. 本主题说明了如何使用 SQL Server 配置管理器来启用 ...

  6. The Linux usage model for device tree data

    Linux and the Device Tree The Linux usage model for device tree data Author: Grant Likely grant.like ...

  7. ora600

    4节点RAC:版本oracle11.2.0.4 22:20——23:40发生ora600 alert日志: Errors in file /u01/app/oracle/diag/rdbms/orcl ...

  8. DAY 3 上午

    状压DP 状态压缩dp 状态压缩是设计dp状态的一种方式. 当普通的dp状态维数很多(或者说维数与输入数据有关),但每一维总量很少时,可以将多维状态压缩为一维来记录. 这种题目最明显的特征就是:都存在 ...

  9. MySQL-default设置

    Both statements insert a value into the phone column, but the first inserts a NULL value and the sec ...

  10. 用Vue来实现音乐播放器(十六):滚动列表的实现

    滚动列表是一个基础组件  他是基于scroll组件实现的 在base文件夹下面创建一个list-view文件夹 里面有list-view.vue组件     <template> < ...