This article will take you to understand the object literals in JavaScript and analyze why object literals are cool. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to everyone.
BeforeECMAScript 2015, object literals (also called object initializers) in Javascript were quite simple. They could define two types of properties. :
{ name1: value1 }
{ get name() {..} }
andsetters{ set name(val){..} }
Defined dynamically calculated property valuesUnfortunately, a simple example can represent all the possibilities of object literals:
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
JS is a languagebased onprototypes, so everything is an object. It is necessary to provide an easily constructible language for object creation, configuration, and access to prototypes.
Defining an object and setting its prototype is a common task. The best way is to set the prototype directly in the object literal using a statement.
Unfortunately, the limitations of literals do not allow a simple solution to achieve this. You must useobject.create()
with an object literal to set the prototype.
var myProto = { propertyExists: function(name) { return name in this; } }; var myNumbers = Object.create(myProto); myNumbers['arrat'] = [1, 6, 7]; myNumbers.propertyExists('array'); // => true myNumbers.propertyExists('collection'); // => false
I think this solution is not flexible enough. JS is based on prototypes. Why is it so troublesome to create objects using prototypes?
Fortunately, JavaScript is also slowly improving. Many of the rather uncomfortable features of JS are being addressed step by step.
This article demonstrates how ES2015 solves the problems described above and adds features to improve the capabilities of object literals:
super
CallIn addition, we can Looking ahead, take a look at the new proposals in (Draft 2): Collectable and expandable properties.
As you already know, there is a way to access the prototype of a created object The way is to reference the__proto__
getter attribute:
var myObject = { name: 'Hello World!', }; myObject.__proto__; // => {} myObject.__proto__.isPrototypeOf(myObject); // => true
myObject.__proto__
Returns the prototype object ofmyObject
.
Please note that it is not recommended to useobject.__ proto__
as agetter/setter
. Alternatives should be considered usingObject.getPrototypeOf()
andObject.setPrototypeOf()
.
The good news is thatES2015 allows you to use__proto__
as a property name in an object literal{ __proto__: protoObject }
to set the prototype.
Let's initialize the object with__proto__
properties to see how it improves on the unintuitive solution described in the introduction:
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
myNumbers
is using The object is created with the special attribute name__proto__
, and its prototype ismyProto
. This object is created with a simple declaration, without using additional functions likeObject.create()
.
As you can see, coding using__proto__
is simple. I usually recommend simple and intuitive solutions.
As an aside, I think it’s a bit strange that simple and scalable solutions rely on a lot of design and work. If a solution is simple, you might think it is easy to design. However, the truth is exactly the opposite:
If some things look If it seems complicated or difficult to use, it may not have been fully thought out. What do you think about returning to nature? (Feel free to leave a comment)
1.1 The user manual of__proto__
under special circumstances
Even though__proto__
looks very concise , there are some specific scenarios you need to pay attention to.
对象字面量中__proto__
只允许使用一次。重复使用 JS 会抛出异常:
var object = { __proto__: { toString: function() { return '[object Numbers]' } }, numbers: [1, 5, 89], __proto__: { toString: function() { return '[object ArrayOfNumbers]' } } };
上面示例中的对象字面量使用了两次__proto__
属性,这是不允许的。在这种情况下,将在会抛出SyntaxError: Duplicate __proto__ fields are not allowed in object literals
的语法错误。
JS 约束只能用一个对象或null
作为__proto__
属性值。任何使用原始类型(字符串,数字,布尔值)或undefined
类型都将被忽略,并且不会更改对象的原型。
让我们看看这个限制的例子:
var objUndefined = { __proto__: undefined, }; Object.getPrototypeOf(objUndefined); // => {} var objNumber = { __proto__: 15, }; Object.getPrototypeOf(objNumber); // => {}
这个对象字面量使用了undefined
和数字15
来设置__proto__
的值。因为只有对象或null
允许被当做原型,objUndefined
和objNumber
仍然拥有他们默认的原型: JavaScript 空对象{}
。__proto__
的值被忽略了。
当然,尝试用原始类型去设置对象的原型会挺奇怪。这里的约束符合预期。
可以使用较短的语法在对象常量中声明方法,以省略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()
和get()
是collection
里使用这个缩写形式定义的方法。
这个方法声明的方式还一个好处是它们都是非匿名函数,这在调试的时候会很方便。 上个例子执行collection.add.name
返回函数名'add'
。
super
调用JS 一个有趣的改进是可以使用super
关键字来访问原型链中父类的属性。看下面的例子:
var calc = { numbers: null, sumElements() { return this.numbers.reduce(function(a, b) { return a + b; }); }, }; var numbers = { __proto__: calc, numbers: [4, 6, 7], sumElements() { if (this.numbers == null || this.numbers.length === 0) { return 0; } return super.sumElements(); }, }; numbers.sumElements(); // => 17
calc
是numbers
对象的原型。在numbers
的sumElements
方法中,可以通过super
关键字调用原型的super.sumArray()
方法。
最终,super
是从对象原型链访问继承的属性的快捷方式。
在前面的示例中,可以尝试直接执行calc.sumElements()
来调用原型。 然而,super.sumElements()
可以正确调用,因为它访问对象的原型链。并确保原型中的sumElements()
方法使用this.numbers
正确访问数组。
super
存在清楚地表明继承的属性将被使用。
3.1super
的使用限制
super
在对象字面量中只能在速写式方法声明里使用。
如果尝试从普通方法声明{ name: function() {} }
访问它,JS 将抛出一个错误:
var calc = { numbers: null, sumElements() { return this.numbers.reduce(function(a, b) { return a + b; }); }, }; var numbers = { __proto__: calc, numbers: [4, 6, 7], sumElements: function() { if (this.numbers == null || this.numbers.length === 0) { return 0; } return super.sumElements(); }, }; // Throws SyntaxError: 'super' keyword unexpected here numbers.sumElements();
这个sumElements
方法被定义为一个属性:sumElements: function() {...}
, 因为super
只能在速写式方法声明中使用。所以,在这种情况下调用它会抛出SyntaxError: 'super' keyword unexpected here
的语法错误。
此限制在很大程度上不影响对象字面量的声明方式。 多数情况下因为语法更简洁,使用速写式方法声明会更好。
在 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')
表达式设置了'number_pi'
这个属性名。
相应地,[prefix('bool', 'false')]
将第二个属性名称设置为'bool_false'
。
4.1Symbol
作为属性名
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 *() { }
定义一个属性,该属性用于迭代对象的自有属性。展开操作符[...object]
使用了迭代器来返回自有属性的数组。
对象字面量的可收集可展开的属性目前是草案第二阶段 (stage 2) 中的一个提议,它将被选入下一个 Javascript 版本。
它们等价于 ECMAScript 2015 中已可用于数组的展开和收集操作符。
可收集的属性允许收集一个对象在解构赋值后剩下的属性们。
下面这个例子收集了object
解构后留下的属性:
var object = { propA: 1, propB: 2, propC: 3, }; let { propA, ...restObject } = object; propA; // => 1 restObject; // => { propB: 2, propC: 3 }
可展开的属性允许从一个源对象拷贝它的自有属性到另一个对象字面量中。这个例子中对象字面量的其它属性合集是从source
对象中展开的:
var source = { propB: 2, propC: 3, }; var object = { propA: 1, ...source, }; object; // => { propA: 1, propB: 2, propC: 3 }
JavaScript 正在迈出重要的一步。
在ECMAScript 2015中,即使是作为对象字面量的相对较小的结构也得到了相当大的改进。提案草案中还包含了许多新功能。
你可以在对象初始化时直接通过__proto__
属性名设置其原型。比用Object.create()
简单很多。
请注意,__proto__
是 ES2015 标准附件B的一部分,不鼓励使用。 该附件实现对于浏览器是必需的,但对于其他环境是可选的。NodeJS 4、5和6支持此功能。
现在方法声明有个更简洁的模式,所以你不必输入function
关键字。而且在速写式声明里,你可以使用super
关键字,它允许你十分容易得通过对象的原型链访问父类属性。
如果属性名需要在运行时计算,现在你可以用可计算的属性名[expression]
来初始化对象。
对象字面量现在确实很酷!
英文原文地址:https://dmitripavlutin.com/why-object-literals-in-javascript-are-cool/
作者:Dmitri Pavlutin
译文地址:https://segmentfault.com/a/1190000020669949
更多编程相关知识,请访问:编程视频!!
The above is the detailed content of An in-depth analysis of object literals in JavaScript. For more information, please follow other related articles on the PHP Chinese website!