你需要知道的30个ES6—ES12开发技巧!(1)

简介: 又是一顿爆肝,又是一篇万字长文,重新梳理了一下ES6——ES12的常用新特性,很多特性在开发中还是很实用的,希望对你有一点点帮助!文章内容较多,建议先收藏在学习呢!ECMAScript 是一种由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言,这种语言被称为 JavaScript 。简单来说,ECMAScript 是 JavaScript 的标准与规范,JavaScript 是 ECMAScript 标准的实现和扩展。

又是一顿爆肝,又是一篇万字长文,重新梳理了一下ES6——ES12的常用新特性,很多特性在开发中还是很实用的,希望对你有一点点帮助!文章内容较多,建议先收藏在学习呢!


ECMAScript 是一种由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言,这种语言被称为 JavaScript 。简单来说,ECMAScript 是 JavaScript 的标准与规范,JavaScript 是 ECMAScript 标准的实现和扩展。


自2015年开始,ECMAScript发布的版本如下:

发布时间 正式名称 版本名称 名称缩写
2015 ECMAScript2015 ECMAScript6 ES2015、ES6
2016 ECMAScript2016 ECMAScript7 ES2016、ES7
2017 ECMAScript2017 ECMAScript8 ES2017、ES8
2018 ECMAScript2018 ECMAScript9 ES2018、ES9
2019 ECMAScript2019 ECMAScript10 ES2019、ES10
2020 ECMAScript2020 ECMAScript11 ES2020、ES11
2021 ECMAScript2021 ECMAScript12 ES2021、ES12

下面就来看看ECMAScript各版本有哪些使用技巧吧。

网络异常,图片无法展示
|
**注:**部分知识点已在之前的文章中介绍过了,本文不再赘述,文中已附上对应文章链接。


一、ES6 新特性(2015)


ES6的更新主要是体现在以下方面:

  • 表达式:变量声明,解构赋值
  • 内置对象:字符串拓展、数值拓展、对象拓展、数组拓展、函数拓展、正则拓展、Symbol、Set、Map、Proxy、Reflect
  • 语句与运算:Class、Module、Iterator
  • 异步编程:Promise、Generator、Async。

这里主要介绍一些常用的新特性。还有一些特性,在之前文章中已经介绍过了,这里不在多说,直接上链接:


1. let和const


在ES6中,新增了let和const关键字,其中 let 主要用来声明变量,而 const 通常用来声明常量。let、const相对于var关键字有以下特点:

特性 var let const
变量提升 ✔️ × ×
全局变量 ✔️ × ×
重复声明 ✔️ × ×
重新赋值 ✔️ ✔️ ×
暂时性死区 × ✔️ ✔️
块作用域 × ✔️ ✔️
只声明不初始化 ✔️ ✔️ ×

这里主要介绍其中的四点:


(1)重新赋值

const 关键字声明的变量是“不可修改”的。其实,const 保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。对于基本类型的数据(数值、字符串、布尔值),其值就保存在变量指向的那个内存地址,因此等同于常量。但对于引用类型的数据(主要是对象和数组),变量指向数据的内存地址,保存的只是一个指针,const只能保证这个指针是不变的,至于它指向的数据结构就不可控制了。


(2)块级作用域

在引入let和const之前是不存在块级作用域的说法的,这也就导致了很多问题,比如内层变量会覆盖外层的同名变量:

var a = 1;
if (true) {
  var a = 2;
}
console.log(a);   // 输出结果:2 
复制代码


循环变量会泄漏为全局变量:

var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++) {
  console.log(arr[i]);  // 输出结果:1  2  3
}
console.log(i); // 输出结果:3
复制代码


而通过let和const定义的变量存在块级作用域,就不会产生上述问题:

let a = 1;
if (true) {
  let a = 2;
}
console.log(a); // 输出结果:1
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);  // 输出结果:1  2  3
}
console.log(i); // Uncaught ReferenceError: i is not defined
复制代码


(3)变量提升

我们知道,在ES6之前是存在变量提升的,所谓的变量提升就是变量可以在声明之前使用:

console.log(a); // 输出结果:undefined
var a = 1;
复制代码


变量提升的本质是JavaScript引擎在执行代码之前会对代码进行编译分析,这个阶段会将检测到的变量和函数声明添加到 JavaScript 引擎中名为 Lexical Environment 的内存中,并赋予一个初始化值 undefined。然后再进入代码执行阶段。所以在代码执行之前,JS 引擎就已经知道声明的变量和函数。


这种现象就不太符合我们的直觉,所以在ES6中,let和const关键字限制了变量提升,let 定义的变量添加到 Lexical Environment 后不再进行初始化为 undefined 操作,JS 引擎只会在执行到词法声明和赋值时才进行初始化。而在变量创建到真正初始化之间的时间跨度内,它们无法访问或使用,ES6 将其称之为暂时性死区:


// 暂时性死区 开始
a = "hello";     //  Uncaught ReferenceError: Cannot access 'a' before initialization
let a;   
//  暂时性死区 结束
console.log(a);  // undefined
复制代码


(4)重复声明

在ES6之前,var关键字声明的变量对于一个作用域内变量的重复声明是没有限制的,甚至可以声明与参数同名变量,以下两个函数都不会报错:


function funcA() {
  var a = 1;
  var a = 2;
}
function funcB(args) {
  var args = 1; 
}
复制代码


而let修复了这种不严谨的设计:

function funcA() {
  let a = 1;
  let a = 2;  // Uncaught SyntaxError: Identifier 'a' has already been declared
}
function funcB(args) {
  let args = 1;  // Uncaught SyntaxError: Identifier 'args' has already been declared
}
复制代码


现在我们项目中已经完全放弃了var,而使用let来定义变量,使用const来定义常量。在ESlint开启了如下规则:

"no-var": 0;
复制代码


2. 解构赋值


ES6中还引入了解构赋值的概念,解构赋值遵循“模式匹配”,即只要等号两边的模式相等,左边的变量就会被赋予对应的值。不同类型数据的解构方式不同,下面就分别来看看不同类型数据的解构方式。

平时在开发中,我主要会用到对象的解构赋值,比如在React中解构porps值等,使用解构赋值来获取父组件传来的值;在React Hooks中的useState使用到了数组的解构赋值;

(1)数组解构

具有 Iterator 接口的数据结构,都可以采用数组形式的解构赋值。

const [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo, bar, baz) // 输出结果:1  2  3
复制代码


这里,ES6实现了对数组的结构,并依次赋值变量foo、bar、baz。数组的解构赋值按照位置将值与变量对应。

数组还可以实现不完全解构,只解构部分内容:

const [x, y] = [1, 2, 3];   // 提取前两个值
const [, y, z] = [1, 2, 3]  // 提取后两个值
const [x, , z] = [1, 2, 3]  // 提取第一三个值
复制代码


如果解构时对应的位置没有值就会将变量赋值为undefined:

const [x, y, z] = [1, 2]; 
console.log(z)  // 输出结果:undefined
复制代码


数组解构赋值可以使用rest操作符来捕获剩余项:

const [x, ...y] = [1, 2, 3];   
console.log(x);  // 输出结果:1
console.log(y);  // 输出结果:[2, 3]
复制代码


在解构时还支持使用默认值,当对应的值为undefined时才会使用默认值:

const [x, y, z = 3] = [1, 2]; 
console.log(z)  // 输出结果:3
复制代码


(2)对象解构

对象的解构赋值的本质其实是先找到同名的属性,在赋值给对应的变量:

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
console.log(foo, bar); // 输出结果:aaa  bbb
复制代码


需要注意的是,在JavaScript中,对象的属性是没有顺序的。所以,在解构赋值时,变量必须与属性同名才能去取到值。

对象的解构赋值也是支持默认值的,当定义的变量在对象中不存在时,其默认值才会生效:

let { foo, bar, baz = 'ccc'} = { foo: 'aaa', bar: 'bbb', baz: null };
console.log(foo, bar, baz); // 输出结果:aaa  bbb  null
let { foo, bar, baz = 'ccc'} = { foo: 'aaa', bar: 'bbb' };
console.log(foo, bar, baz); // 输出结果:aaa  bbb  ccc
复制代码


可以看到,只有定义的变量是严格的===undefined时,它的默认值才会生效。

除此之外,我们还需要注意,不能给已声明的变量进行赋值,因为当缺少 let、const、var 关键词时,将会把 {baz} 理解为代码块从而导致语法错误,所以下面代码会报错:

let baz;
{ baz } = { foo: 'aaa', bar: 'bbb', baz: 'ccc' };
复制代码


可以使用括号包裹整个解构赋值语句来解决上述问题:

let baz;
({ baz } = { foo: 'aaa', bar: 'bbb', baz: 'ccc' });
console.log(baz)
复制代码


在对象的解构赋值中,可以将现有对象的方法赋值给某个变量,比如:

let { log, sin, cos } = Math;
log(12)  // 输出结果:2.4849066497880004
sin(1)   // 输出结果:0.8414709848078965
cos(1)   // 输出结果:0.5403023058681398
复制代码


(3)其他解构赋值

剩下的几种解构赋值,目前我在项目中应用的较少,来简单看一下。


  • 字符串解构

字符串解构规则:只要等号右边的值不是对象或数组,就先将其转为类数组对象,在进行解构:

const [a, b, c, d, e] = 'hello';
console.log(a, b, c, d, e)  // 输出结果:h e l l o
复制代码


类数组对象有 length 属性,因此可以给这个属性进行解构赋值:

let {length} = 'hello';    // 输出结果: 5
复制代码


由于字符串都是一个常量,所以我们通常是知道它的值是什么的,所以很少会使用变量的解构赋值。

  • 数值和布尔值解构赋值

对数值和布尔值进行解构时,它们将会先被转为对象,然后再应用解构语法:

let {toString: s} = 123;
s === Number.prototype.toString // 输出结果:true
let {toString: s} = true;
s === Boolean.prototype.toString // 输出结果:true
复制代码


注意null和undefined不能转换为对象,所以如果右边是这两个值,就会报错。

  • 函数参数解构赋值

函数参数表面上是一个数组,在传入参数的那一刻,就会被解构为x和y。

function add([x, y]){
  return x + y;
}
add([1, 2]);   // 3
复制代码


除此之外,我们还可以解构函数的返回值:

function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();
复制代码


3. 模板字符串


传统的JavaScript语言中,输出模板经常使用的是字符串拼接的形式,这样写相当繁琐,在ES6中引入了模板字符串的概念来解决以上问题。

模板字符串是增强版的字符串,用反引号``来标识,他可以用来定义单行字符串,也可以定义多行字符串,或者在字符串中嵌入变量。


// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// 字符串中调用函数
` ${fn()} 
复制代码


在平时的开发中,除了上面代码中的应用,很多地方会用到模板字符串,比如拼接一个DOM串,在Emotion/styled中定义DOM结构等,都会用到模板字符串。不过在模板字符串中定义DOM元素就不会有代码提示了。


在使用模板字符串时,需要注意以下几点:

  • 如果在字符串中使用反引号,需要使用\来转义;
  • 如果在多行字符串中有空格和缩进,那么它们都会被保留在输出中;
  • 模板字符串中嵌入变量,需要将变量名写在${}之中;
  • 模板字符串中可以放任意的表达式,也可以进行运算,以及引用对象的属性,甚至可以调用函数;
  • 如果模板字符中的变量没有声明,会报错。


4. 函数默认参数


在ES6之前,函数是不支持默认参数的,ES6实现了对此的支持,并且只有不传入参数时才会触发默认值:

function getPoint(x = 0, y = 0) {
  console.log(x, y);
}
getPoint(1, 2);   // 1  2
getPoint()        // 0  0 
getPoint(1)       // 1  0
复制代码


当使用函数默认值时,需要注意以下几点:

(1)函数length属性值

函数length属性通常用来表示函数参数的个数,当引入函数默认值之后,length表示的就是第一个有默认值参数之前的普通参数个数:

const funcA = function(x, y) {};
console.log(funcA.length);  // 输出结果:2 
const funcB = function(x, y = 1) {};
console.log(funcB.length);  // 输出结果:1
const funcC = function(x = 1, y) {};
console.log(funcC.length);  // 输出结果 0 
复制代码


(2)参数作用域

当给函数的参数设置了默认值之后,参数在被初始化时将形成一个独立作用域,初始化完成后作用域消解:

let x = 1;
function func(x, y = x) {
  console.log(y);
}
func(2);  
复制代码


这里最终会打印出2。在函数调用时,参数 x, y 将形成一个独立的作用域,所以参数中的y会等于第一个参数中的x,而不是上面定义的1。


5. 箭头函数


ES6中引入了箭头函数,用来简化函数的定义:

const counter = (x, y) => x + y;
复制代码


相对于普通函数,箭头函数有以下特点:

(1)更加简洁

  • 如果没有参数,就直接写一个空括号即可
  • 如果只有一个参数,可以省去参数的括号
  • 如果有多个参数,用逗号分割
  • 如果函数体的返回值只有一句,可以省略大括


// 1. 不传入参数
const funcA = () => console.log('funcA');
// 等价于
const funcA = function() {
  console.log('funcA');
} 
// 2. 传入参数
const funcB = (x, y) => x + y;
// 等价于
const funcB = function(x, y) {
  return x + y;
} 
// 3. 单个参数的简化
const funcC = (x) => x;
// 对于单个参数,可以去掉 (),简化为
const funcC = x => x;
// 等价于
const funcC = function(x) {
  return x;
}
// 4. 上述代码函数体只有单条语句,如果有多条,需要使用 {}
const funcD = (x, y) => { console.log(x, y); return x + y; }
// 等价于
const funcD = function(x, y) {
  console.log(x, y);
  return x + y;
}



相关文章
|
3月前
|
JavaScript 前端开发
ES6学习(6)
ES6学习(6)
|
3月前
|
网络架构
ES6学习(5)
ES6学习(5)
|
前端开发 JavaScript
每天3分钟,重学ES6-ES12系列文章汇总
每天3分钟,重学ES6-ES12系列文章汇总
67 0
|
Docker 容器
es应用笔记1-es部署
es应用笔记1-es部署
119 0
|
JavaScript 前端开发
每天3分钟,重学ES6-ES12(十八)ES Module(二)
每天3分钟,重学ES6-ES12(十八)ES Module
82 0
|
JavaScript 前端开发
每天3分钟,重学ES6-ES12(十八)ES Module(一)
每天3分钟,重学ES6-ES12(十八)ES Module
85 0
|
JavaScript 前端开发
每天3分钟,重学ES6-ES12(六)ES7 ES8 新增内容
每天3分钟,重学ES6-ES12(六)ES7 ES8 新增内容
121 0
|
JavaScript 前端开发 Java
每天3分钟,重学ES6-ES12(八)ES11 ES12新增内容
每天3分钟,重学ES6-ES12(八)ES11 ES12新增内容
111 0
每天3分钟,重学ES6-ES12(七)ES10 新增内容
每天3分钟,重学ES6-ES12(七)ES10 新增内容
90 0
|
缓存 JavaScript 算法
每天3分钟,重学ES6-ES12(十八) CJS
每天3分钟,重学ES6-ES12(十八) CJS
90 0