JavaScript语法糖 | 青训营

92 阅读5分钟

JavaScript语法糖

1.解构赋值

数组解构

 let [firstName, surname] = "John Smith".split(' ');
 alert(firstName); // John
 alert(surname);  // Smith

我们可以将Object.entries() 方法与解构语法一同使用,来遍历一个对象的“键—值”对:

 let user = {
   name: "John",
   age: 30
 };
 ​
 // 使用循环遍历键—值对
 for (let [key, value] of Object.entries(user)) {
   alert(`${key}:${value}`); // name:John, then age:30
 }

对象解构

 let options = {
   title: "Menu"
 };
 ​
 let {width: w = 100, height: h = 200, title} = options;
 ​
 alert(title);  // Menu
 alert(w);      // 100
 alert(h);      // 200

嵌套解构

如果一个对象或数组嵌套了其他的对象和数组,我们可以在等号左侧使用更复杂的模式(pattern)来提取更深层的数据。

在下面的代码中,options 的属性 size 是另一个对象,属性 items 是另一个数组。赋值语句中等号左侧的模式(pattern)具有相同的结构以从中提取值:

 let options = {
   size: {
     width: 100,
     height: 200
   },
   items: ["Cake", "Donut"],
   extra: true
 };
 ​
 // 为了清晰起见,解构赋值语句被写成多行的形式
 let {
   size: { // 把 size 赋值到这里
     width,
     height
   },
   items: [item1, item2], // 把 items 赋值到这里
   title = "Menu" // 在对象中不存在(使用默认值)
 } = options;
 ​
 alert(title);  // Menu
 alert(width);  // 100
 alert(height); // 200
 alert(item1);  // Cake
 alert(item2);  // Donut

智能函数参数

我们可以用一个对象来传递所有参数,而函数负责把这个对象解构成各个参数:

 let options = {
   title: "My menu",
   items: ["Item1", "Item2"]
 };
 ​
 function showMenu({
   title = "Untitled",
   width: w = 100,  // width goes to w
   height: h = 200, // height goes to h
   items: [item1, item2] // items first element goes to item1, second to item2
 }) {
   alert( `${title} ${w} ${h}` ); // My Menu 100 200
   alert( item1 ); // Item1
   alert( item2 ); // Item2
 }
 ​
 showMenu(options);

完整语法和解构赋值是一样的:

对于参数对象,属性 incomingProperty 对应的变量是 varName,默认值是 defaultValue

 function({
   incomingProperty: varName = defaultValue
   ...
 })

请注意,这种解构假定了 showMenu() 函数确实存在参数。如果我们想让所有的参数都使用默认值,那我们应该传递一个空对象:

 showMenu({}); // 不错,所有值都取默认值
 ​
 showMenu(); // 这样会导致错误

我们可以通过指定空对象 {} 为整个参数对象的默认值来解决这个问题:

 function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
   alert( `${title} ${width} ${height}` );
 }
 ​
 showMenu(); // Menu 100 200

在上面的代码中,整个参数对象的默认是 {},因此总会有内容可以用来解构。

总结

  • 解构赋值可以简洁地将一个对象或数组拆开赋值到多个变量上。

  • 解构对象的完整语法:

     let {prop : varName = default, ...rest} = object
    

    这表示属性 prop 会被赋值给变量 varName,如果没有这个属性的话,就会使用默认值 default

    没有对应映射的对象属性会被复制到 rest 对象。

  • 解构数组的完整语法:

     let [item1 = default, item2, ...rest] = array
    

    数组的第一个元素被赋值给 item1,第二个元素被赋值给 item2,剩下的所有元素被复制到另一个数组 rest

  • 从嵌套数组/对象中提取数据也是可以的,此时等号左侧必须和等号右侧有相同的结构。

2.JSON处理

JavaScript 提供了如下方法:

  • JSON.stringify 将对象转换为 JSON。
  • JSON.parse 将 JSON 转换回对象。

JSON.stringify()

方法 JSON.stringify(student) 接收对象并将其转换为字符串。

得到的 json 字符串是一个被称为 JSON 编码(JSON-encoded)序列化(serialized)字符串化(stringified)编组化(marshalled) 的对象。我们现在已经准备好通过有线发送它或将其放入普通数据存储。

请注意,JSON 编码的对象与对象字面量有几个重要的区别:

  • 字符串使用双引号。JSON 中没有单引号或反引号。所以 'John' 被转换为 "John"
  • 对象属性名称也是双引号的。这是强制性的。所以 age:30 被转换成 "age":30
 let student = {
   name: 'John',
   age: 30,
   isAdmin: false,
   courses: ['html', 'css', 'js'],
   spouse: null
 };
 ​
 let json = JSON.stringify(student);
 ​
 alert(typeof json); // we've got a string!
 ​
 alert(json);
 /* JSON 编码的对象:
 {
   "name": "John",
   "age": 30,
   "isAdmin": false,
   "courses": ["html", "css", "js"],
   "spouse": null
 }
 */

JSON 是语言无关的纯数据规范,因此一些特定于 JavaScript 的对象属性会被 JSON.stringify 跳过。

即:

  • 函数属性(方法)。
  • Symbol 类型的键和值。
  • 存储 undefined 的属性。

支持嵌套对象转换,并且可以自动对其进行转换。

 let meetup = {
   title: "Conference",
   room: {
     number: 23,
     participants: ["john", "ann"]
   }
 };
 ​
 alert( JSON.stringify(meetup) );
 /* 整个结构都被字符串化了
 {
   "title":"Conference",
   "room":{"number":23,"participants":["john","ann"]},
 }
 */

但是不得有循环引用。

 let room = {
   number: 23
 };
 ​
 let meetup = {
   title: "Conference",
   participants: ["john", "ann"]
 };
 ​
 meetup.place = room;       // meetup 引用了 room
 room.occupiedBy = meetup; // room 引用了 meetup
 ​
 JSON.stringify(meetup); // Error: Converting circular structure to JSON

手写JSON时的典型错误,此外,JSON 不支持注释。向 JSON 添加注释无效。

 let json = `{
   name: "John",                     // 错误:属性名没有双引号
   "surname": 'Smith',               // 错误:值使用的是单引号(必须使用双引号)
   'isAdmin': false                  // 错误:键使用的是单引号(必须使用双引号)
   "birthday": new Date(2000, 2, 3), // 错误:不允许使用 "new",只能是裸值
   "friends": [0,1,2,3]              // 这个没问题
 }`;

JSON.parse()

例如:

 // 字符串化数组
 let numbers = "[0, 1, 2, 3]";
 ​
 numbers = JSON.parse(numbers);
 ​
 alert( numbers[1] ); // 1

对于嵌套对象:

 let userData = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';
 ​
 let user = JSON.parse(userData);
 ​
 alert( user.friends[1] ); // 1

总结

  • JSON 是一种数据格式,具有自己的独立标准和大多数编程语言的库。
  • JSON 支持 object,array,string,number,boolean 和 null
  • JavaScript 提供序列化(serialize)成 JSON 的方法 JSON.stringify 和解析(反序列化) JSON 的方法 JSON.parse
  • 这两种方法都支持用于智能读/写的转换函数。
  • 如果一个对象具有 toJSON,那么它会被 JSON.stringify 调用。

3.运算符

可选链操作符?.与空值合并操作符??

在开发过程中,我们可能需要获取深层次属性,例如 system.user.addr.province.name。但在获取 name 这个属性前需要一步步的判断前面的属性是否存在

在编写代码时,如果某个属性不为 null 和 undefined,那么就获取该属性,如果该属性为 null 或 undefined,则取一个默认值

 ​
 let a = null;  
 let b = undefined;
 ​
 a?.   // true
 b?.   // true
 a ?? "default"   // "default" 
 b ?? "default"   // "default"

隐式转换符!!

!!将值转换为布尔值

 !!0   // false
 !!1   // true
 !!""  // true
 !![]  // true
 !!{}  // true 
 !!null // false
 !!undefined // false
 !!NaN // false