Es6 简介
- ECMAScript 6 简称 ES6,是 JavaScript 语言的下一代标准,已经在 2015 年 6 月整事发布了。
- ECMAScript和JavaScript的关系,简单来说ECMAScript是JavaScript语言的国际标准,JavaScript是ECMAScript的实现。
- 它的目标是使 JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ES6新特性
var、let和const
- 除了 var ,我们现在还可以使用两个新的标示符来定义一个变量 —— let 和 const。和 var 不一样的是,let定义的变量不会被变量提升,const定义的变量不能被修改。
- 在ES6前,js是没有块级作用域{}的概念。(有函数作用域,全局作用域,eval作用域)
- 在ES6后,let和const的出现,js也有了块级作用域的概念。
- (变量提升:)在ES6前,var声明的变量无论声明在何处,都会被视为声明在函数的最顶部,不在函数内即在全局作用域的顶部。
举个例子先。
console.log(a);//undefined
var a = "good";
//代码可以解读为
var a;console.log(a);a="good";
//var声明的变量提升到全局作用域的顶部,但是赋值声明不会被提升复制代码
而let就不会被变量提升
console.log(a);//a is not defined
let a = "good"
复制代码
const定义的常亮是不能被修改的
const a = "good";
a = "bad";
console.log(a);//Assignment to constant variable.复制代码
箭头函数
函数的快捷写法。不需要function关键字来创建函数,省略return关键字,继承当前上下文的this关键字。
拿ES5和ES6函数举个栗子。
//ES5
var arr = [1, 2, 3, 4];var newArr = arr.map(function(x) { return x + 1;});console.log(newArr);//[ 2, 3, 4, 5 ]
//ES6
var arr = [1, 2, 3, 4];var newArr = arr.map(x => { return x + 1;});console.log(newArr);//[ 2, 3, 4, 5 ]复制代码
箭头函数小细节:当你的函数有且仅有一个参数的时候是可以省略掉括号的,当你的函数有且仅有一个表达式的时候可以省略{};
var arr = [1, 2, 3, 4];var newArr = arr.map(x =>x+1);console.log(newArr);复制代码
js中的this对象是个有毒的东西,看一个例子。
var obj = { name: "xing", fn1: function() { console.log(this); }, fn2: function() { console.log(this); setTimeout(function() { console.log(this); }, 1000); }};obj.fn2();复制代码
发现两次打印出来的东西不一样。第一次打印this指的对象为obj,第二次打印执行的是setTimeout计时器中的函数,this.fn1作为计时器中的函数体。因为计时器为异步操作,所以计时器中的this所指为全局对象window。
所以为了解决这个问题,我们采用闭包的特性来处理。
var obj = { name: "xing", fn1: function() { console.log(this); }, fn2: function() { console.log(this); var _this = this; setTimeout(function() { console.log(_this.fn1); }, 1000); }};obj.fn2();复制代码
我们可以先用一个变量_this
来存储指向对象obj的this
,此时_this
指向obj,然后在计时器回调函数中调用我们所定义的变量_this
,此时所指的对象就是obj。
模板字符串
解决了ES5在字符串功能上的缺陷。
第一个用途:字符串拼接。将表达式嵌入字符串进行拼接,用`和${}`来界定。
ES5
var name = "牛郎";console.log(name+"&&织女");//牛郎&&织女
ES6
var name = "牛郎";console.log(`${name}&&织女`);//牛郎&&织女复制代码
第二个用途:在ES5时我们通过反斜杠来做多行字符串拼接。ES6中通过``
支持换行并且不需要额外的处理。
ES5
var msg = "hello \girl";
console.log(msg);//hello girlES6var msg = `hi,girl.`console.log(msg);
//hi,girl.复制代码
字符串
ES6更新了许多新的对于字符串的函数,比如includes()
,repeat()
,startsWith
,endsWith
,padStart
,padEnd
.includes()
判断是否包含然后直接返回布尔值
var str = "xing";console.log(str.includes("i"));//true复制代码
.repeat(N)
获取字符串重复N次
var s= "ha";console.log(s.repeat(3));//hahaha复制代码
startsWith
var str = "abc def";console.log(str.startsWith("abc")); //trueconsole.log(str.startsWith("abcd")); //false复制代码
endsWith
var str = "abc def";console.log(str.endsWith("def")); //trueconsole.log(str.endsWith("abcd")); //false复制代码
padEnd和
padStart
var str = "abc def";console.log(str.padStart(15, "!*")); //!*!*!*!*abc defconsole.log(str.padEnd(15, "!*")); //abc def!*!*!*!*复制代码
解构
自动解析数组或对象中的值。比如若一个函数要返回多个值,常规的做法是返回一个对象,将每个值做为这个对象的属性返回。但在ES6中,利用解构这一特性,可以直接返回一个数组,然后数组中的值会自动被解析到对应接收该值的变量中。
var [x, y] = getVal(), [name, , age] = getMsg();
function getVal() { return [1, 2];}function getMsg() { return ["xing", "care", "21"];}console.log(`x:${x},y:${y}`);//x:1,y:2console.log(`name:${name}, age:${age}`);//name:xing, age:21
复制代码
增强的对象字面量
在ES6中,对象字面量被加强了,写法更加简洁与灵活,同时在定义对象的时候能够做的事情更多了。
- 可以在对象字面量里面定义原型
- 定义方法可以不用function关键字
- 直接调用父类方法
var father = { say() { console.log("11111111111"); }};var son = { __proto__: father,//设置此对象的原型是father,相当于继承father name: "xiaoMing", work() { console.log("22222222222222"); }};father.say();//11111111111son.say();//11111111111复制代码
类class
在ES5中最令人头的部分:原型,构造函数,继承,在ES6中引入了Class(类)的概念。
JS本身就是面向对象的,ES6中提供的类实际上只是原型模式的包装。现在提供原生的class支持后,对象的创建和继承更加直观了,并且父类方法的调用、实例化静态方法和构造函数等概念都更加形象化。
class Animal { constructor(name) {//ES6中新型的构造器 this.name = name; } say() { console.log(`我是一只${this.name}`); }}
class Dog extends Animal { constructor(name) { super(name);//直接调用父类构造器进行初始化 } bark() { console.log("5555555555555"); }}var animal = new Animal("cat"), dog = new Dog("dog");animal.say();//我是一只catdog.say();//我是一只dogdog.bark();//5555555555555
复制代码
参数新特性
默认参数值
现在可以在定义函数的时候指定参数的默认值,而不用像以前那样通过逻辑或操作符来达目的了。
function say(name = "girl"){ console.log(`Hello ${name}`);}say();//Hello girlsay("man");//Hello man复制代码
不定参数
不定参数是在函数中使用命名参数同时接受不定数量的未命名参数。这只是一种语法糖,在以前的JavaScript代码中我们可以通过arguments
变量来达到这一目的。不定参数的格式是三个句点后跟代表所有不定参数的变量名。比如下面这个例子中,…x
代表了所有传入add函数的参数。
function add(...x){ return x.reduce((m,n)=>m+n);}console.log(add(1,2,3));//输出:6console.log(add(1,2,3,4,5));//输出:15复制代码
拓展参数
拓展参数则是另一种形式的语法糖,它允许传递数组或者类数组直接做为函数的参数而不用通过apply
。
var people = ["one", "two", "three","four","five"];function say(people1, people2, people3) { console.log(`Hello ${people1},${people2},${people3}`);}
//我们将一个数组以拓展参数的形式传递,它能很好地映射到每个单独的参数say(...people);//Hello one,two,three//而在以前,如果需要传递数组当参数,我们需要使用函数的apply方法say.apply(null, people); //Hello one,two,three复制代码
for of 值遍历
我们知道for in
循环遍历数组,类数组和对象,ES6中新引入的for of
循环功能相似,不同的是每次循环它提供的不是序号而是值。
var arr = [1, 2, 3, 4];for (var a of arr) { console.log(a);}
//输出
1234复制代码
Map,Set
这些是新加的集合类型, 提供了更加方便的获取属性值的方法,不用像以前一样用hasOwnProperty
来检查某个属性是属于原型链上的呢还是当前对象的。同时,在进行属性值添加与获取时有专门的get
,set
方法。
set
还是举个栗子来的实在。
var s = new Set();s.add("hello") .add("goodbye") .add("goodbye") .add("goodbye") .add("hello2") .add("hello3") .add("hello4") .add("hello5");//删除set中的一个元素s.delete("goodbye");
//遍历set集合for (var p of s) { console.log(p);}console.log(s.size); console.log(s.has("hello")); console.log(Array.isArray(s));
//输出:
hellohello2hello3hello4hello55truefalse复制代码
- 可以看出来set里面的值是唯一的没有重复的,利用这点可以做到数组去重。
- set不是数组,是一个像对象的数组,是个伪数组
map
var m = new Map([["name", "xing"], [1, 234]]);m.set(false, "abc");m.set([1, 2, 3], { year: "2018" });console.log(m.get("name")); //xingconsole.log(m.get(1)); //234console.log(m.get(false)); //abcconsole.log(m.get([1,2,3])); //undefined复制代码
- 它类似与对象,里面存放的也是键值对,区别在于:对象中的键名只能是字符串,如果使用map,它里面的键可以使任意值。
- 通过get("键名")获取值是里面不能是数组,因为get()要比较栈中的地址,而不是堆区中的数据
- 重复的数据会覆盖。
严格模式
之前学习的JS,语法非常灵活,JS中这个灵活的特性,弊大于先利。后来增加了严格模式。
使用严格模式的目的:规则,提高编译效率。
启动严格模式 "use strict"
严格模式和非严格模式有什么区别:
在严格模式下不能使用没有var的变量"use strict" function fn(a, b) { console.log(a, b); console.log(arguments[0], arguments[1]); arguments[0] = 11111; arguments[1] = 222222; console.log(a, b); console.log(arguments[0], arguments[1]); } fn(1, 2)
输出://
1 21 21 211111 222222复制代码
"use strict" function f() { console.log(this); } f();//undefined复制代码
promise
Promise 可以让我们远离平行的代码(回调地狱):
func1(function(value1) { func2(value1, function(value2) { func3(value2, function(value3) { func4(value3, function(value4) { func5(value4, function(value5) { // Do something with value 5 }); }); }); });});复制代码
func1(value1) .then(func2) .then(func3) .then(func4) .then(func5, value5 => {});
复制代码
- 用同步的方式去写异步代码
- 没有异步就不需要 promise。
- Promise 本身不是异步,只是我们去编写异步代码的一种方式。
四大术语
一定要结合异步操作来理解。
既然是异步,这个操作需要有个等待的过程,从操作开始,到获取结果,有一个过程的。
1、解决(fulfill ):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。
虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
2、 拒绝(reject ):指一个 promise 失败时进行的一系列操作。
3、 终值(eventual value ):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于
promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称
之终值,有时也直接简称为值(value)。
4、据因(reason ):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
三种状态
在异步操作中有三个状态。
当操作发出时对象处于等待状态(等待态 Pending),当操作完成是,结果有两种,操作成功(执行态 Fulfilled)或者操作失败( 拒绝态 Rejected ),
两种事件
针对三种状态,只有如下两种转化方式
在状态转换的时候,就会触发事件。
- 如果是 pending --> fulfiied,就会触发 onFulFilled 事件
- 如果是 pendeing --> rejected,就会触发 onRejected 事件
一个对象
就是指 promise 对象
在状态转换的时候,就会触发事件
如果是 pending --> fulfiied,就会触发 onFulFilled 事件
如果是 pendeing --> rejected,就会触发 onRejected 事件
Promise 对象提供了 then 方法
Promise.then(onFulFilled,onRejected)
参数作为两个回调函数
p.then( function(res) { console.info(res); }, function(err) { console.info(err); });复制代码
针对 onFulFilled,会自动提供一个参数,作为终值value(res)
针对 onRejected,会自动提供一个参数,作为据因reason(err)
其中第二个回调表示 从 pending ---> rejected 时的回调。
由于这种写法,辨识度不高。Promise 就提供了一个 catch 方法,用于注册 onRejected 回调。
p.then(function(res) { console.info(res);}).catch(function(err) { console.info(err);});复制代码
catch其实是then的简写,then方法调用后返回的还是promise 对象,所以可以链式调用,如下。
p.then(res => console.info(res)).catch(err => console.info(err));复制代码
简单的理解意思就是,我们在使用promise对象,异步操作成功的时候走then,失败的时候走catch
解析一个例子
const fs = require("fs");var p = new Promise(function(resolve, reject) { fs.readFile("./MyTest/SelfTest/data.txt", "utf8", (err, data) => { if (err) { reject(err); } else { resolve(data); } });});p.then(res => console.info(res)).catch(err => console.info(err));复制代码
使用node的fs模块来读取文件创建promise对象,当前对象处在pedding状态回调函数中的两个参数作用是用于状态的转换resolve,将状态从 pending --> fullFilledreject,将状态从 pending --> rejectedresolve函数的参数指的是 终值(res)reject函数的参数指的是 据因(err)
所以读取文件成功的话则打印文本数据data,失败的则打印错误原因err
Symbols
我们知道对象其实是键值对的集合,而键通常来说是字符串。而现在除了字符串外,我们还可以用symbol这种值来做为对象的键。Symbol是一种基本类型,像数字,字符串还有布尔一样,它不是一个对象。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的。之后就可以用这个返回值做为对象的键了。Symbol还可以用来创建私有属性,外部无法直接访问由symbol做为键的属性值。
var key = Symbol("key");function MyClass(privateData) { this[key] = privateData;}MyClass.prototype = { doStuff: function() { this[key]; }};var c = new MyClass("hello");console.log(c.key);//undefined无法访问该属性,因为是私有的复制代码
Symbol()
的一个使用情况是给一个类或者命名空间打上补丁,但是可以确定的是你不会去更新它。比如,你想给 React.Component
类添加一个 refreshComponent
方法,但是可以确定的是你不会在之后更新这个方法:
const refreshComponent = Symbol();
React.Component.prototype[refreshComponent] = () => {
// do something
}复制代码
本文同时借鉴网路一些大神,如有冒犯,吾定改之。-^_^-