Cet article vous amènera à comprendre les littéraux d'objet en JavaScript et à analyser pourquoi les littéraux d'objet sont cool. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il sera utile à tout le monde.
Avant ECMAScript 2015, les littéraux d'objet (également appelés initialiseurs d'objet) en Javascript étaient assez simples et pouvaient définir deux types de propriétés :
{ name1: value1 }
{ get name(){..} }
et setters { set name(val){..} }
Propriété à calcul dynamique valeursMalheureusement, un exemple simple peut représenter toutes les possibilités des littéraux d'objet :
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 est un langage basé sur le prototype , donc tout est un objet. Il est nécessaire de fournir un langage facilement constructible pour la création d'objets, la configuration et l'accès aux prototypes.
Définir un objet et paramétrer son prototype est une tâche courante. La meilleure façon est de définir le prototype directement dans le littéral objet à l’aide d’une instruction.
Malheureusement, les limitations des littéraux ne permettent pas de trouver une solution simple pour y parvenir. Les prototypes doivent être définis en utilisant object.create()
en combinaison avec des littéraux d'objet.
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
Je pense que cette solution n'est pas assez flexible. JS est basé sur des prototypes. Pourquoi est-il si difficile de créer des objets à l'aide de prototypes ?
Heureusement, JavaScript s'améliore également lentement. De nombreuses fonctionnalités plutôt inconfortables de JS sont abordées étape par étape.
Cet article montre comment ES2015 résout les problèmes décrits ci-dessus et ajoute des fonctionnalités pour améliorer les capacités des littéraux d'objet :
super
AppelDe plus, nous pouvons regarder vers l'avenir, Jetez un œil à la nouvelle proposition dans ( Projet 2) : Propriétés collectables et extensibles.
Comme vous le savez déjà, il existe un moyen d'accéder au prototype de un objet créé La méthode consiste à référencer l'attribut getter de __proto__
:
var myObject = { name: 'Hello World!', }; myObject.__proto__; // => {} myObject.__proto__.isPrototypeOf(myObject); // => true
myObject.__proto__
renvoie l'objet prototype de myObject
.
Veuillez noter qu'il n'est pas recommandé d'utiliser object.__ proto__
comme getter/setter
. Des méthodes alternatives devraient envisager d'utiliser Object.getPrototypeOf()
et Object.setPrototypeOf()
.
La bonne nouvelle est que ES2015 vous permet d'utiliser __proto__
comme nom de propriété dans un littéral d'objet { __proto__: protoObject }
pour définir le prototype.
Initialisons l'objet avec l'attribut __proto__
et voyons comment il améliore la solution peu intuitive décrite dans l'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
utilise le nom d'attribut spécial __proto__
Le objet créé, son prototype est myProto
. Cet objet est créé avec une simple déclaration et aucune fonction supplémentaire comme Object.create()
n'est utilisée.
Comme vous pouvez le constater, l'encodage à l'aide de __proto__
est simple. Je recommande généralement des solutions simples et intuitives.
En passant, je trouve un peu étrange qu'une solution simple et évolutive repose sur beaucoup de conception et de travail. Si une solution est simple, vous pourriez penser qu’elle est facile à concevoir. Cependant, le contraire est vrai :
Si certains les choses se présentent Si cela semble compliqué ou difficile à utiliser, c'est peut-être qu'il n'a pas été entièrement pensé. Que pensez-vous du retour à la nature ? (N'hésitez pas à laisser un commentaire)
1.1 Situations particulières __proto__
Manuel d'utilisation
Même si __proto__
semble simple, il existe des scénarios spécifiques pour vous. faire attention.
对象字面量中 __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
更多编程相关知识,请访问:编程视频!!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!