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. CF1244C

    题目描述 给出n,p,w,d,求(x,y,z)使得 xw+yd=p x+y+z=n 其中d<w<10^5^ 题解 显然扩欧啊( 来自天国的long long y如果大于等于w,则显然可以把 ...

  2. makefile 赋值

    = 是最基本的赋值:= 是覆盖之前的值?= 是如果没有被赋值过就赋予等号后面的值+= 是添加等号后面的值

  3. [luogu]P2680 运输计划[二分答案][树上差分]

    [luogu]P2680 [NOIP2015]运输计划 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n ...

  4. 【bzoj3343】教主的魔法

    *题目描述: 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的身高一开始 ...

  5. cf 118B

    B. Present from Lena time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  6. ubantu apt-get install安装php及开展

    .安装php apt-get install libapache2-mod-php5 php5 报错 E: Package 'libapache2-mod-php5' has no installat ...

  7. 特征点检测算法——FAST角点

    上面的算法如SIFT.SURF提取到的特征也是非常优秀(有较强的不变性),但是时间消耗依然很大,而在一个系统中,特征提取仅仅是一部分,还要进行诸如配准.提纯.融合等后续算法.这使得实时性不好,降系了统 ...

  8. LOJ504「LibreOJ β Round」ZQC 的手办

    https://loj.ac/problem/504 题解 对于区间取\(\max\),这个比较好办,直接在线段树上打标记就行了. 如果让我们弹出前\(n\)个数,我们可以用类似超级钢琴的思想,队列中 ...

  9. 服务器中常见的RAID

    Standalone 最普遍的单磁盘储存方式. Cluster 集群储存是通过将数据分布到集群中各节点的存储方式,提供单一的使用接口与界面,使用户可以方便地对所有数据进行统一使用与管理. Hot sw ...

  10. 2.Vue子组件给父组件通信

    子组件给父组件通信 如果子组件想要改变数据呢?这在vue中是不允许的,因为vue只允许单向数据传递,这时候我们可以通过触发事件来通知父组件改变数据,从而达到改变子组件数据的目的 子组件: <te ...