ES新特性

293 阅读3分钟

理解语言与平台之间的关系,系统化地学习ECMAJavaScript

内容概要

  • ECMAScript 与 JavaScript
  • ECMAScript 的发展过程
  • ECMAScript 2015 的新特性
  • And more...

ECMAScript 与 JavaScript

  • ECMAScript是由Ecma国际标准组织以ECMA-262和ECMA-402规范的形式进行标准化的脚本程序设计语言
  • ECMAScript是形成JavaScript语言基础的脚本语言
  • 在浏览器中,JavaScript = ECMAScript + Web API
  • 在nodejs中,JavaScript = ECMAScript + Nodejs API

ECMAScript 的发展过程

名称标准版本发⾏时间
ECMAScript2019(ES2019)102019年6⽉
ECMAScript2018(ES2018)92018年6⽉
ECMAScript2017(ES2017)82017年6⽉
ECMAScript2016(ES2016)72016年6⽉
ECMAScript2015(ES2015)62015年6⽉
ECMAScript5.1(ES5.1)5.12011年6⽉
ECMAScript5(ES5)52009年12⽉
ECMAScript4(ES4)4被放弃
ECMAScript3(ES3)31999年12⽉
ECMAScript2(ES2)21998年6⽉
ECMAScript1(ES1)11997年6⽉

ECMAScript 2015 的新特性

ECMAScript2015(ES2015)因为排在ES5.1之后,所以称为ES6。由于距离上一版本的发布相隔了4年之久,ES6相较上个版本发生很大的变化,我们来认识ES6的新特性。 • 对原有语法进⾏增强 • 解决原有语法上的⼀些问题或者缺陷 • 全新的对象、全新的⽅法、全新的功能 • 全新的数据类型和数据结构

let与块级作用域

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

{
  let a = 0;
  var b = 1;
}
a // ReferenceError: a is not defined.
b // 1

这里用let和var声明了两个变量a和b,然后在代码块之外调用a和b,变量a报错,变量b返回正确的值。 这表明,let声明的变量只在它所在的代码块有效。

var arr = [1,2,3,4,5,6];
for (var i = 0; i < arr.length; i++) {
  setTimeout(function(){
        console.log(arr[i])
    }, 0)
}
// undefined undefined undefined undefined undefined undefined

相信大家也明白,var i 声明的变量作用域是全局的,for循环执行完了再执行定时器里面的代码,此时获取的i值已经被修改成6,我们就只输出6次arr[6]的, 而没有依次输出数组arr 的内容了。使用let 命令就可以很好解决这个问题。

var arr = [1,2,3,4,5,6];
for (let i = 0; i < arr.length; i++) {
  setTimeout(function(){
        console.log(arr[i])
    }, 0)
}
// 1 2 3 4 5 6

ES6 的块级作用域

let实际上为 JavaScript 新增了块级作用域。

for (let i = 0; i < 3; i++) {
  let i = '^_^';
  console.log(i);
}
// ^_^
// ^_^
// ^_^

上面代码正确运行,输出了 3 次^_^。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。当然我们不建议样使用,因为容易引起变量名混乱。

不存在变量提升

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象实在怪异,像是某种缺陷。 let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

// var 的情况
console.log(a); // 输出undefined
var a = 0;

// let 的情况
console.log(b);  // Uncaught ReferenceError: b is not defined
let b = 2;

const命令

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const a = 0;
a = 1;  // Uncaught TypeError: Assignment to constant variable
// 一开始为0,终生为0,怎么改也不行了

尝试改变const声明的变量会引起报错。 这就意味着,const 声明变量必须立即初始化。

const b // Uncaught SyntaxError: Missing initializer in const declaration
// 现在不给值,以后就没机会了,且行且珍惜啊!

所以正确的做法应该是,const 声明变量立即初始化值

const c = 1;
c // 1
// 一次为一,始终为一,做一个专一的好变量

const 变量的不可修改性,指的是不对变量进行二次赋值,但是可以给变量添加属性。

const obj = {};
obj.name = 'Tom';
obj.age = 20;
obj  // {name: "Tom", age: 20}
// 变量还是那个变量,只要指向的内存地址不变,要加点成员还是可以的^.^

除了不可修改,const 与let 用法大体相同,比如const的作用域与let命令相同:只在声明所在的块级作用域内有效。

if (true) {
  const MIN = 18;
}
MIN // Uncaught ReferenceError: MIN is not defined
// 花括号{ }围起来的是别人家的地盘了,别人家的东西不能随便拿o_o

数组的解构

ES6中可以从数组中提取值,按照对应位置,对变量赋值。

const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at ] = date_range;
start_at	// '2020-10-23'
end_at		// '2021-03-23'
// 开始时间-结束时间,一前一后,非常直观

如果解构不成功,变量的值就等于undefined。

const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at, work_at ] = date_range;
start_at	// '2020-10-23'
end_at		// '2021-03-23'
work_at		// undefined
// 解构超过了数组的长度,所以没有获取第三个元素的值,work_at为undefined

解构赋值允许指定默认值

const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at, work_at = '2021-05-01'] = date_range;
start_at	// '2020-10-23'
end_at		// '2021-03-23'
work_at		// '2021-05-01'
// 没有第三个元素,work_at赋值为默认值'2021-05-01'

对象的解构赋值

除了数组,解构还可以用于对象。

const obj = {name: "Tom", age: 20};
const { name, age } = obj;
name 	// "Tom"
age 	// 20

当然,对象里面的数组也是可以一起解构的.

const obj = {name: "Tom", age: 20, date_range: ['2020-10-23','2021-03-23']};
const { name, age, date_range: [start_at, end_at ] } = obj;
name 	// "Tom"
age 	// 20
start_at	// '2020-10-23'
end_at		// '2021-03-23'

模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at, work_at = '2021-05-01'] = date_range;
// 普通字符串
`This course start at 2020`

// 多行字符串
`This course start at
2020`

// 字符串中嵌入变量
`This course start at ${start_at}, and end at ${end_at}`

模板字符串还有一个更高级的用法,就是在字符串前面添加一个函数,可以获取字符串片段和嵌入的变量,返回值是模板字符串的结果。

const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at, work_at = '2021-05-01'] = date_range;
function myTagFunc(strings, a, b){
	console.log(strings, a, b)
    return a+b
}
// 字符串中嵌入变量
const str = myTagFunc`This course start at ${start_at}, and end at ${end_at}`
// ["This course start at ", ", and end at ", "", raw: Array(3)] "2020-10-23" "2021-03-23"
str // '2020-10-232021-03-23'

字符串的扩展方法

  • includes 判断当前字符串是否包含指定的字符串
  • startsWith 判断当前字符串是否以指定的字符串开始
  • endsWith 判断当前字符串是否以指定的字符串结束
const str = `hello world`;
str.includes('hello')	// true
str.includes('apple')	// false
str.startsWith('hello')	// true
str.startsWith('apple')	// false
str.endsWith('world')	// true
str.endsWith('hello')	// false

函数的扩展

函数参数的默认值

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'Shenzhen') // Hello Shenzhen
log('Hello', '') // Hello

参数默认值可以与解构赋值的默认值,结合起来使用。

function foo({x, y = 0}) {
  console.log(x, y);
}

foo({}) // undefined 0
foo({x: 1}) // 1 0
foo({x: 1, y: 2}) // 1 2

rest 参数

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(1, 2, 3) // 6

箭头函数

ES6 允许使用“箭头”(=>)定义函数。

const f = data => data;

// 等同于
const f = function (data) {
  return data;
};

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

const f = () => 100;

// 等同于
const f = function () {
  return 100;
};
const sum = (a, b) => a + b;
// 等同于
const sum = function(a, b) {
	return a + b;
}

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

const sum = (num1, num2) => {
  console.log(num1, num2);
  return num1 + num2; 
}

箭头函数与rest 参数一起使用:

const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]

箭头函数有几个使用注意点。

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

对象的扩展

属性的简洁表示法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

	const name = 'Tom';
    const age = 20;
    const obj = { name, age }
    // 等同于
    const obj = { name: name, age: age }

属性名表达式

ES6 允许字面量定义对象时,表达式作为对象的属性名,即把表达式放在方括号内。

const obj = {
	['date'+'_'+'range']: ['2020-10-23','2021-03-23']
}

方法的 name 属性

函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。

const obj = {
	name: 'Tom',
    sayName(){
    	console.log(this.name);
	}
}

Classes(类)

Promises

Generators

Modules

Template literals(模板字⾯量)

Default parameters(默认参数)

Enhanced object literals(对象字⾯量增强)

Destructuring assignments(解构分配)

Spread operator(展开操作符)

for ... of 循环

Map 和 Set

Proxy