4月阅读周·你不知道的JavaScript | ES6语法,消除常见编程技巧中的痛点
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读三个月。
4月份的阅读计划有两本,《你不知道的JavaScrip》系列迎来收尾。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》。
当前阅读周书籍:《你不知道的JavaScript(下卷)》。
ES6语法
块作用域声明
JavaScript中变量作用域的基本单元一直是function。如果需要创建一个块作用域,最普遍的方法除了普通的函数声明之外,就是立即调用函数表达式(IIFE)。
let声明
我们可以创建绑定到任意块的声明,其被称为块作用域(block scoping)。这意味着我们只需要一对{ .. }就可以创建一个作用域。
应该总是把let声明放在块的最前面。如果有多个变量需要声明的话,建议只用一个let。
在let声明/初始化之前访问let声明的变量会导致错误,而使用var的话这个顺序是无关紧要的(除了代码风格方面)。
{
console.log(a); // undefined
console.log(b); // ReferenceError!
var a;
let b;
}
const声明
const,用于创建常量。常量是一个设定了初始值之后就只读的变量。
const声明必须要有显式的初始化。如果需要一个值为undefined的常量,就要声明const a = undefined。
这个值并没有因为const被锁定或者不可变,只是赋值本身不可变。如果这个值是复杂值,比如对象或者数组,其内容仍然是可以修改的。
{
const a = [1, 2, 3];
a.push(4);
console.log(a); // [1,2,3,4]
a = 42; // TypeError!
}
const可以用在for、for..in以及for..of循环的变量声明中。但如果想要重新赋值就会抛出错误,比如for循环中常用的i++。
块作用域函数
从ES6开始,块内声明的函数,其作用域在这个块内。
if (something) {
function foo() {
console.log('1');
}
} else {
function foo() {
console.log('2');
}
}
foo(); // ? ?
在前ES6环境中,不管something的值是什么,foo()都会打印出"2",因为两个函数声明都被提升到了块外,第二个总是会胜出。
解构
ES6引入了一个新的语法特性,名为解构(destructuring),把这个功能看作是一个结构化赋值(structured assignment)方法,可能会容易理解一些。
可以把将数组或者对象属性中带索引的值手动赋值看作结构化赋值。ES6为解构新增了一个专门语法,专用于数组解构和对象解构。这个语法消除了前面代码中对临时变量tmp的需求,使代码简洁很多。
var [a, b, c] = foo();
var { x: x, y: y, z: z } = bar();
console.log(a, b, c); // 1 2 3
console.log(x, y, z); // 4 5 6
模板字面量(插入字符串字面量)
ES6引入了一个新的字符串字面量,使用 ` 作为界定符。这样的字符串字面值支持嵌入基本的字符串插入表达式,会被自动解析和求值。
var name = 'Kyle';
var greeting = `Hello ${name}! `;
console.log(greeting); // "Hello Kyle! "
console.log(typeof greeting); // "string"
可以看到,这里在一组字符外用`..`来包围,这会被解释为一个字符串字面量,但是其中任何${..}形式的表达式都会被立即在线解析求值。这种形式的解析求值形式就是插入(比模板要精确一些)。
箭头函数
箭头函数定义包括一个参数列表(零个或多个参数,如果参数个数不是一个的话要用( .. )包围起来),然后是标识=>,函数体放在最后。
只有在函数体的表达式个数多于1个,或者函数体包含非表达式语句的时候才需要用{ .. }包围。如果只有一个表达式,并且省略了包围的{ .. }的话,则意味着表达式前面有一个隐含的return。
这里列出了几种不同形式的箭头函数:
var f1 = () => 12;
var f2 = x => x * 2;
var f3 = (x, y) => {
var z = x * 2 + y;
y++;
x *= 3;
return (x + y + z) / 2;
};
for..of循环
ES6在把JavaScript中我们熟悉的for和for..in循环组合起来的基础上,又新增了一个for..of循环,在迭代器产生的一系列值上循环。
for..of循环的值必须是一个iterable,或者说它必须是可以转换/封箱到一个iterable对象的值。iterable就是一个能够产生迭代器供循环使用的对象。
在for (XYZ of ABC)..中,和for以及for..in循环中的语句一样,XYZ语句可以是赋值表达式也可以是声明。所以可以这么做:
var o = {};
for (o.a of [1, 2, 3]) {
console.log(o.a);
}
// 1 2 3
for ({ x: o.a } of [{ x: 1 }, { x: 2 }, { x: 3 }]) {
console.log(o.a);
}
// 1 2 3
和其他循环一样,for..of循环也可以通过break、continue、return(如果在函数中的话)提前终止,并抛出异常。在所有这些情况中,如果需要的话,都会自动调用迭代器的return(..)函数(如果存在的话)让迭代器执行清理工作。
Symbol
ES6为JavaScript引入了一个新的原生类型:symbol。
下面是创建symbol的过程:
var sym = Symbol('some optional description');
typeof sym; // "symbol"
以下几点需要注意:
- 不能也不应该对Symbol(..)使用new。它并不是一个构造器,也不会创建一个对象。
- 传给Symbol(..)的参数是可选的。如果传入了的话,应该是一个为这个symbol的用途给出用户友好描述的字符串。
- typeof的输出是一个新的值("symbol"),这是识别symbol的首选方法。
总结
我们来总结一下本篇的主要内容:
- ES6为JavaScript增加了很多新的语法形式。
- 这些新语法形式中大多数的设计目的都是消除常见编程技巧中的痛点,比如为函数参数设定默认值以及把参数的“其余”部分收集到数组中。解构是一个强有力的工具,用于更精确地表达从数组和嵌套对象中赋值。
- 而像=>箭头函数这样的特性看起来似乎是为了使代码更简洁的语法,但实际上它有非常特别的行为特性,应该只在适当的时候使用。
- 扩展Unicode支持、新的正则表达式技巧,甚至新的基本类型symbol都使ES6的语法发展的更加完善。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)