目录
1. 在对象构造时设置原型
2. 速记方法定义
3. 调用父类方法
4. 计算属性名称
5. 展望未来:rest 和属性展开
6. 结论
首页 web前端 js教程 为什么我要说 JavaScript 对象字面量很酷?

为什么我要说 JavaScript 对象字面量很酷?

Feb 22, 2017 pm 01:43 PM

在 ECMAScript 2015 之前,JavaScript 里的对象字面量(也叫对象初始化器)功能很弱。它只能定义两种属性:

  • 普通键/值对 { name1: value }

  • Getters { get name(){..} } 以及 setters { set name(val){..} },用来设置和获取需要计算的值。

令人心痛地,对象字面量的所有用法只用一个简单的例子就能囊括:

Try in JS Bin

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() 来结合对象字面量和设置原型:

Try in JS Bin

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 在改变。比较令人沮丧的许多问题在 JavaScript 中正在被一步一步地解决。

这篇文章解释 ES2015 是如何解决上面所说的问题以及改进对象字面量以获得额外的好处:

  • 在对象构造的过程中设置原型

  • 速记方法定义

  • 调用父类方法

  • 计算属性名称

同时,让我们展望未来,了解最新的提案(stage 2):对象的 rest 属性和属性展开操作符。

为什么我要说 JavaScript 对象字面量很酷?

1. 在对象构造时设置原型

如你已经知道得,其中一个访问一个已存在对象的原型的方法是使用 getter 属性 __proto__:

Try in JS Bin

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

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

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

让我们用 __proto__ 重写一下上面那个例子,让它看起来好一点:

Try in JS Bin

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 对象使用原型 myProto 创建,这可以通过特殊属性 __proto__ 实现。

这个对象通过简单的语句创建,而不需要额外的函数例如 Object.create()。

如你所见,使用 __proto__ 是简单的。我偏爱简单直接的解决方案。

有点说跑题了,回到主题来。我认为获得简单和可靠的解决方案需要通过大量的设计和实践。如果一个解决方案是简单的,你可能认为它同样也很容易被设计出来,然而事实并不是这样:

  • 让它变得简单明了的过程是复杂的

  • 让它变得复杂和难以理解却很容易

如果某个东西看起来太复杂或者用起来不舒服,很可能它的设计者考虑不周。

元芳,你怎么看?(欢迎在文章底部发表评论参与讨论)

2.1 使用 __proto__ 的特例

尽管 __proto__ 看似简单,却有一些特殊的场景你需要格外注意。

为什么我要说 JavaScript 对象字面量很酷?

在对象字面量中 __proto__ 只允许使用一次。多次使用的话 JavaScript 会抛出异常:

Try in JS Bin

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。

JavaScript 限制了只允许使用 object 或者 null 作为 __proto__ 属性的值。使用其它原生类型(如字符串、数值、布尔类型)或者 undefined 会被忽略,并不能改变对象的原型。

看一个例子:

Try in JS Bin

var objUndefined = {  
  __proto__: undefined }; Object.getPrototypeOf(objUndefined); // => {}  var objNumber = {  
  __proto__: 15 }; Object.getPrototypeOf(objNumber); // => {}
登录后复制

上面的例子里,对象字面量使用 undefined 和数值 15 来设置 __proto__ 值。因为只有对象或者 null 才允许被使用,objUndefined 和 objNumber 仍然是它们默认的原型:简单 JavaScript 对象 {}。__proto__ 的赋值被忽略了。

当然,尝试使用原生类型来设置对象的原型,这本身是很奇怪的。所以在这里做限制是符合预期的。

2. 速记方法定义

现在对象字面量中可以使用一个更短的语法来声明方法,省略 function 关键字和冒号。这被叫做速记方法定义(shorthand method definition)。

让我们用新的方式来定义一些方法:

Try in JS Bin

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'。

3. 调用父类方法

一个有趣的改进是能够使用 super 关键字来访问从原型链继承下来的属性。看下面的例子:

Try in JS Bin

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 里面,要调用原型 calc上的方法,可以使用 super 关键字: super.sumArray()。

所以 super 是从原型链访问被继承的属性的一个快捷的方法。

在上一个例子里,我们也可以直接调用 cale.sumArray(),不过 super 是一个更好的选择因为它访问对象的原型链。它的存在清晰地暗示了继承的属性将被使用。

3.1 使用 super 的限制

在对象字面量中, super 只能用在速记方法定义中

如果在普通的方法声明 { name: function() {} } 中使用它,JavaScript 会抛异常:

Try in JS Bin

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要求在速记方法中使用,在其中调用 super 将抛出异常:SyntaxError: 'super' keyword unexpected here。

这个限制不会对对象字面量声明有多少影响,因为大部分情况下我们没有理由不用速记方法定义,毕竟它语法更简单。

4. 计算属性名称

在 ES2015 之前,对象初始化器的属性名称是字面量,大多数情况下是静态字符串。要创建一个动态计算的属性名,你不得不使用属性访问器:

Try in JS Bin

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}。这个表达式计算结果将成为属性名。

我真的很喜欢这个语法:短而简单。

让我们改进上面的代码:

Try in JS Bin

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 来迭代遍历一个对象的自有属性名,代码如下:

Try in JS Bin

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] 使用迭代器并返回自有属性列表。

5. 展望未来:rest 和属性展开

对象字面量的 Rest 和属性展开 是新的标准草案中的一个提案(stage 2),意味着这一特性是新版本 JavaScript 的规范的候选。

数组的 展开和 rest 操作符 已经被实现了。

Rest 属性 允许我们从解构赋值左侧使用对象来收集属性,看下面的例子:

Try in JS Bin

var object = {  
  propA: 1,
  propB: 2,
  propC: 3 }; let {propA, ...restObject} = object;  
propA; // => 1  restObject; // => { propB: 2, propC: 3 }
登录后复制

属性展开 允许将源对象的自有属性拷进对象字面量内部。在上面的例子中,对象字面量从 souce对象中收集额外的属性。

Try in JS Bin

var source = {  
  propB: 2,
  propC: 3 }; var object = {  
  propA: 1,
  ...source
}
object; // => { propA: 1, propB: 2, propC: 3 }
登录后复制

6. 结论

JavaScript 在快速进步。

即使是相对小的结构比如对象字面量在 ES2015 中都有相当大的改进,更别说还有一大堆新特性在草案中。

你可以从初始化器中使用 __proto__ 属性直接设置对象的原型,这比使用 Object.create() 要更方便。

方法声明可以写成更简短的形式,这样你就不用写 function 关键字了。然后在速记方法中可以使用 super 关键字,它能提供方便的对被继承原型链上属性的访问。

如果一个属性名在运行时计算,现在你可以使用计算属性名称 [表达式] 来初始化对象。

的确,对象字面量现在很酷!

你觉得呢?欢迎在下方发表评论参与讨论。

以上就是为什么我要说 JavaScript 对象字面量很酷的内容,更多相关内容请关注PHP中文网(www.php.cn)!


本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

WebSocket与JavaScript:实现实时监控系统的关键技术 WebSocket与JavaScript:实现实时监控系统的关键技术 Dec 17, 2023 pm 05:30 PM

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

JavaScript和WebSocket:打造高效的实时天气预报系统 JavaScript和WebSocket:打造高效的实时天气预报系统 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

如何将 MySQL 查询结果数组转换为对象? 如何将 MySQL 查询结果数组转换为对象? Apr 29, 2024 pm 01:09 PM

将MySQL查询结果数组转换为对象的方法如下:创建一个空对象数组。循环结果数组并为每一行创建一个新的对象。使用foreach循环将每一行的键值对赋给新对象的相应属性。将新对象添加到对象数组中。关闭数据库连接。

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

PHP 函数如何返回对象? PHP 函数如何返回对象? Apr 10, 2024 pm 03:18 PM

PHP函数可以通过使用return语句后跟对象实例来返回对象,从而将数据封装到自定义结构中。语法:functionget_object():object{}。这允许创建具有自定义属性和方法的对象,并以对象的形式处理数据。

数组和对象在 PHP 中的区别是什么? 数组和对象在 PHP 中的区别是什么? Apr 29, 2024 pm 02:39 PM

PHP中,数组是有序序列,以索引访问元素;对象是具有属性和方法的实体,通过new关键字创建。数组访问通过索引,对象访问通过属性/方法。数组值传递,对象引用传递。

如何在JavaScript中获取HTTP状态码的简单方法 如何在JavaScript中获取HTTP状态码的简单方法 Jan 05, 2024 pm 01:37 PM

JavaScript中的HTTP状态码获取方法简介:在进行前端开发中,我们常常需要处理与后端接口的交互,而HTTP状态码就是其中非常重要的一部分。了解和获取HTTP状态码有助于我们更好地处理接口返回的数据。本文将介绍使用JavaScript获取HTTP状态码的方法,并提供具体代码示例。一、什么是HTTP状态码HTTP状态码是指当浏览器向服务器发起请求时,服务

C++ 函数返回对象时有什么需要注意的? C++ 函数返回对象时有什么需要注意的? Apr 19, 2024 pm 12:15 PM

在C++中,函数返回对象需要注意三点:对象的生命周期由调用者负责管理,以防止内存泄漏。避免悬垂指针,通过动态分配内存或返回对象本身来确保对象在函数返回后仍然有效。编译器可能会优化返回对象的副本生成,以提高性能,但如果对象是值语义传递的,则无需副本生成。

See all articles