Home > Article > Web Front-end > An in-depth analysis of object literals in JavaScript
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.
Before ECMAScript 2015, object literals (also called object initializers) in Javascript were quite simple. They could define two types of properties. :
{ name1: value1 }
{ get name() {..} }
and setters { 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 language based on prototypes, 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 use object.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
Call In 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 of myObject
.
Please note that it is not recommended to use object.__ proto__
as a getter/setter
. Alternatives should be considered using Object.getPrototypeOf()
and Object.setPrototypeOf()
.
The good news is that ES2015 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 is myProto
. This object is created with a simple declaration, without using additional functions like Object.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'
。
<span style="font-size: 18px;">super</span>
调用
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.1 super
的使用限制
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.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 *() { }
定义一个属性,该属性用于迭代对象的自有属性。展开操作符 [...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!