目录
1.ES的兼容性
2.let 和 const 命令
3.类 Class
4.函数参数默认值
5.箭头函数
6.模板字符串
7.变量的解构赋值
8.扩展运算符(spread)
9.模块化 Module
10.对象属性简写
11.Promise
12.for...of
一定要明白,即便 ES2015 到 ES2019 已经发布,也不是所有的浏览器都支持新特性。为了 获得更好的体验,最好使用你选择的浏览器的最新版本。 通过以下链接,你可以检查在各个浏览器中哪些特性可用。
根据上面链接中的兼容性表格来看,它的大部分功能在现代浏览器中都可以使用。即使有些 ES2016+的特性尚未支持,但是我们还是可以开始使用新语法和新功能了。
在ES6以前,js
只有var
一种声明方式,在ES6之后 新增 let 和 const两种方式。var 定义的变量没有块级作用域的概念,let和const声明的都有块级作用域。
var
,但是所声明的变量,只在let
命令所在的代码块内有效。const
声明的是一个只读的常量。一旦声明,常量的值就不能改变。{
let a = 1;
var b = 1;
const c = 3;
}
// let const 的块级作用域
a // ReferenceError: a is not defined.
b // 1
c // ReferenceError: a is not defined.
// const 声明的常量不能重复赋值,let可以
const d = 4
let e = 5
d = 6 // Assignment to constant variable.
e = 7
e // 7
let、const和var的区别:在 变量提升、全局变量、重复声明、重复赋值、暂时死区、块级作用域、只声明不初始化功能的区别
var不会块级作用域,和暂时死区,其他都可以。
let 块级作用域,可重复赋值但是不能 变量提升、全局变量、重复声明。
const 只有暂时死区和块级作用域,其他都不可以。在声明的时候就必须赋值。
在ES6之前,生成实例对象的传统方法是通过构造函数。如下:
function Book(name, page){
this.name = name; // 书名
this.page = page; // 页数
}
Person.prototype.getBookInfo = function () {
return 'Book is ' + this.name + ', it is ' + this.page
}
var myBook = new Book('js',35)
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。所以在ES6之后,上面的代码可以简化为:
class Book {
constructor(name, page){
this.name = name;
this.page = page;
}
getBookInfo() {
return 'Book is ' + this.name + ', it is ' + this.page
}
}
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。像这样:
function changeName(name){
var my_name = name || 'xiao'
console.log(my_name)
}
changeName('lisa') // lisa
changeName('') // xiao
changeName() // xiao
上面代码检查函数changeName
的参数name
有没有赋值,如果没有,则指定默认值为xiao
。这种写法的缺点在于,如果参数name
赋值了,但是对应的布尔值为false
,则该赋值不起作用。就像上面代码的最后一行,参数name
等于空字符,结果被改为默认值。
在ES6里面,这个问题就可以解决:
function changeName(name = 'xiao'){
console.log(my_name)
}
changeName('lisa') // lisa
changeName('') // ''
changeName() // xiao
ES6 允许使用“箭头”(=>
)定义函数。箭头函数表达式的语法比函数表达式更简洁。在ES6前后的函数定义变化:
let fn = function (name) {
return name
};
// es6里面
let fn = name => name;
相比之间是不是简介很多呢,而且如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分 像这样:
let fn = () => 'haha';
// es6之前是这样
let fn = function () { return 'haha' };
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return
语句返回。如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。
let fn = () => console.log('haha');
使用箭头函数需要注意:
this
对象,就是定义时所在的对象,而不是使用时所在的对象。new
命令,否则会抛出一个错误。arguments
对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。yield
命令,因此箭头函数不能用作 Generator 函数。尤其注意箭头函数里面的this的指向是可变的,但是在箭头函数的内部,this的指向是固定。
传统的 JavaScript 语言,拼接语句和输出模板通常是这样写的:
let name = 'javascript', money = 35
let str = 'This is '+name+',it is '+money+' dollar'
console.log(str) // "This is javascript,it is 35 dollar"
在ES6里面就方便了许多,可以这样写:
let name = 'javascript', money = 35
let str = `This is ${name},it is ${money} dollar`
console.log(str) // "This is javascript,it is 35 dollar"
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。在ES6之前,为变量赋值只能这样:
let a = 1;
let b = 2;
let c = 3;
在ES6里面,就可以这样简写:
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。只要等号两边的模式相同,左边的变量就会被赋予对应的值。是不是简洁了许多呢。
8.扩展运算符
(spread)扩展运算符(spread)是三个点(...),
将数组表达式或者string在语法层面展开。
该运算符主要用于函数调用、数组构造的时候。
console.log(...[1,2,3]) // 1 2 3
在ES6之前我们想要对数组进行操作,类似于数组元素求和这种功能,我们需要这样写:
var arr = [1,2,3]
function sum (x, y, z){
return x+y+z
}
var total = sum.apply(this,arr) // 改变this的指向,否则计算会有误
console.log(total); // 6
在ES6里面,我们可以这样写:
var total_1 = sum(...arr)
console.log(total_1); // 6
相比之前的写法,就省事很多。由于扩展运算符可以展开数组,所以不再需要apply
方法,将数组转为函数的参数了。但是要注意的是扩展运算符只能用于可迭代对象数组或是string。
[...'str'] // ["s", "t", "r"]
在ES6之前,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。只有社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
// CommonJS模块
let { stat, exists, readFile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
上面的代码狮子就是整体加载fs模块(就是fs的所有方法),生成一个对象_fs,然后再从这个对象上面读取start、exists、readfile这三个方法。只有在运行的时候才能得到这个对象,导致完全没办法在编译时做“静态优化”。
在ES6的模块不是对象,而是通过export
命令显式指定输出的代码,再通过import
命令输入。这样我们就可以按需引入我们需要的模块,可以实现在编译时做“静态优化”
// ES6模块
import { stat, exists, readFile } from 'fs';
以上这种写法,实质上只是从fs模块中按需只加载3个方法,其他的方法就不用加载出来。这也叫做静态加载。即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。
在ES6之前,假设我们需要将某个变量值直接赋值给同样名称的对象属性时,我们是这样写的:
var name = 'xiao'
var sex = '女'
var obj = { name: name, sex: age }
在ES6中可以这样简写:
var name = 'xiao'
var sex = '女'
var obj = { name, sex }
Promise 是异步编程的一种解决方案。简单说它就像是一个容器,里面放着某个在未来才会执行结束的事件(一般是一个异步操作)的结果。它有三种状态:
pending
(进行中)fulfilled
(已成功)rejected
(已失败)只有异步操作的结果,可以决定当前是哪一种状态,其他的任何其他操作都无法改变这个状态,因为对象的状态不受外界的影响。而且状态一旦改变就不会再变(一旦状态变为 resolved 后,就不能再次改变),任何时候都会得到这个结果。以下是Promise的一个简单的示例:
const promise= new Promise((resolve, reject) => {
resolve('success')
// 以下无效
reject('reject')
})
promise.then(val => {
console.log('ok') // ok
}, err => {
console.log('err')
})
注意:当我们构造Promise的时候,构造函数内部大代码都是立即执行的。像这样
const promise= new Promise((resolve, reject) => {
console.log('promise')
resolve('success')
})
console.log('finish')
promise.then(val => {
console.log('ok') // ok
}, err => {
console.log('err')
})
// promise => finish => ok
Promise 实现了链式调用,也就是在每一次调用then 的时候都会返回一个Promise,并且是一个全新的promise,因为Promise的状态是不可变的。如果你在then中使用了return 那么return 的值也会被 Promise.resolve 包装。像这样:
Promise.resolve(1).then(res => {
console.log(res) // 1
return 2 // 包装成 Promise.resolve(2)
}, err => {
console.log('err')
}).then(res => {
console.log(res); // 2
})
// 1 => 2
而且在ES6之前我们使用ajax的异步请求的时候就会存在ajax内部嵌套ajax,像下面这样,有了 permise 之后就可以改写为较简洁的书写方式,如下所示:
ajax(url, () =>{
ajax(url_1, () =>{
ajax(url_2, () =>{
})
})
})
// 可改写成
ajax(url).then(res => {
console.log(res)
return ajax(url_1)
}).then(res => {
console.log(res);
return ajax(url_2)
}).then(res => console.log(res))
在ES6中 借鉴 C++、Java、C# 和 Python 语言,引入了for...of
循环,作为遍历所有数据结构的统一的方法。
for... of 循环可以使用的范围包括数组 Array、Set和Map结构、某些类似数组的对象(比如arguments
对象、DOM NodeList 对象)以及字符串String等等。在迭代对象上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。
例如:
const arr = ['red', 'green', 'blue']
for(const item of arr){
console.log('item',item);
}
// item red
// item green
// item blue
for...of
循环可以代替数组实例的forEach
方法。例如:
arr.forEach(item => {
console.log('item',item);
});
// item red
// item green
// item blue
运算结果是一样的,JavaScript原有的 for...in 循环,只能获得对象的键名,不能直接获取到键值,如下。在ES6中 提供 for...of 循环,允许遍历取得键值。
for(const item in arr){
console.log('item',item);
}
// item 0
// item 1
// item 2
在持续更新中...