ES6的常用用法

ES6的常用方法

ES6的常用用法
http://es6.ruanyifeng.com/

ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。本书中提到 ES6 的地方,一般是指 ES2015 标准,但有时也是泛指“下一代 JavaScript 语言”。

用vue或者react,很多时候我们都会把ES6这个大兄弟加入我们的技术栈中。

但是ES6那么多那么多特性,我们需要全部都掌握吗?

秉着二八原则,掌握好常用的,有用的;可以让我们快速起飞。

一、变量声明const和let
ES6以前,var关键字声明变量,无论声明在何处,都会被视为声明在函数的最顶部(不在函数内即在全局作用域的最顶部)。这就是函数变量提升。
例如:
function aa() {
 if(bool) {
 var test = ‘hello everyone’
 } else {
 console.log(test)
 }
}
所以不用关心bool是否为true or false。实际上,无论如何test都会被创建声明。

var 用法的两个弊端:
1:第一种场景就是你现在看到的内层变量覆盖外层变量;
var name = ‘john’
while (true) {
var name = ‘lily’
console.log(name) //lily
break
}
console.log(name) //lily
这是因为ES5只有全局作用域和函数作用域,没有块级作用域。
而let则实际上为JavaScript新增了块级作用域。用它所声明的变量,只在let命令所在的代码块内有效。

2 : var带来的第二个不合理场景就是用来计数的循环变量泄露为全局变量;

var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a6; //10
变量i是var声明的,在全局范围内都有效。所以每一次循环,新的i值都会覆盖旧值,导致最后输出的是最后一轮的i的值。而使用let则不会出现这个问题。

接下来ES6主角登场:
我们通常用let和const来声明,let表示变量、const表示常量。let和const都是块级作用域。怎么理解这个块级作用域呢?
在一个函数内部在一个代码块内部, 说白了 { }大括号内的代码块即为let 和 const的作用域。
let命令:

let用于声明变量,但是所有声明的变量只在let命令所在的代码块有效。
let 不允许在同一作用域中重复声明变量。
let不存在变量提升,所以变量一定要在声明后使用,否则会报错。

function aa() {
if(bool) {
let test = ‘hello man’
} else {
//test 在此处访问不到
console.log(test)
}
}

** let的作用域是在它所在当前代码块,但不会被提升到当前函数的最顶部。

const命令:
const命令用来声明常量,一旦声明,其值就不能改变,即:const不可重复声明常量;const一旦声明常量就必须立刻初始化,不能留到以后赋值。
1:const声明常量,一旦声明,不可改变;
const a=10;
a=100;
2:既然const一旦声明不可改变,所以在声明时必须初始化。
const a;

3 : const所在的代码块为块级作用域,所以其变量只在块级作用域内使用或其中的闭包使用。
if(true) {
const a=10;
}
console.log(a);
4 : const声明的变量不存在变量提升。
if(true){
console.log(a);
const a=10;
}

5 : const不可重复声明常量。
var a=10;
const a=5;
const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变。

相同点:

let和const命令声明的变量都只在它们所在的块级作用域中才有效。

如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域。

let 或者 const,会将声明关进一个小黑屋也是TDZ -Temporal Dead Zone(暂时性死区),只有执行到变量声明这句语句时,变量才会从小黑屋被放出来,才能安全使用这个变量。

二、字符串
模板字符串:
ES6模板字符简直是开发者的福音啊,解决了 ES5 在字符串功能上的痛点。
第一个用途,基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定。

// ES5 写法
var name = ‘lux’
console.log(‘hello’ + name)
// es6 写法
const name = ‘lux’
console.log(hello ${name}) //hello lux

第二个用途,在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反引号(``)直接搞定。

// ES5 写法
var msg = "Hi \
man!
"
// ES6 写法
const template = `<div>
    <span>hello world</span>
</div>`

对于字符串 ES6+ 当然也提供了很多厉害也很有意思的方法,说几个常用的。
// 1.includes:判断是否包含然后直接返回布尔值
const str = ‘hahay’
console.log(str.includes(‘y’)) // true
// 2.repeat: 获取字符串重复n次
const str = ‘he’
console.log(str.repeat(3)) // ‘hehehe’
//如果你带入小数, Math.floor(num) 来处理
// s.repeat(3.1) 或者 s.repeat(3.9) 都当做成 s.repeat(3) 来处理
// 3. startsWith 和 endsWith 判断是否以 给定文本 开始或者结束
const str = ‘hello world!’
console.log(str.startsWith(‘hello’)) // true
console.log(str.endsWith(’!’)) // true

// 4. padStart 和 padEnd 填充字符串,应用场景:时分秒
setTimeout(() => {
const now = new Date()
const hours = now.getHours().toString()
const minutes = now.getMinutes().toString()
const seconds = now.getSeconds().toString()
console.log(now); //Wed Sep 26 2018 10:22:11 GMT+0800 (中国标准时间)
console.log(${hours.padStart(2, 0)}:${minutes.padStart(2, 0)}:${seconds.padStart(2, 0)})
//输出当前时分秒: 10:22:11
}, 1000)

三、函数
1,函数默认参数
 在ES5我们给函数定义参数默认值:
 function action(num) {
 num = num || 200
 //当传入num时,num为传入的值
 //当没传入参数时,num即有了默认值200
 return num;
 }
 console.log(action(0)); //200
 // num传入为 0 的时候就是false, 此时num = 200 与我们的实际要的效果明显不一样。


ES6为参数提供了默认值。在定义函数时便初始化了这个参数,以便在参数没有被传递进去时使用。
 function action(num = 200) {
 console.log(num)
 }
 action() //200
 action(0) //0

2、箭头函数

 操作符左边为输入的参数,而右边则是进行的操作以及返回的值 ;
 Inputs=>outputs
 箭头函数最直观的三个特点:
1、不需要function关键字来创建函数
2、省略return关键字
3、继承当前上下文的 this 关键字

例如:
 [1,2,3].map( x => x + 1 )

等同于:
 [1,2,3].map((function(x){
 return x + 1
 }).bind(this))
** 当你的函数有且仅有一个参数的时候,是可以省略掉括号的。当你函数返回有且仅有一个表达式的时候可以省略{};例如:
 var people = name => ‘hello’ + name //参数name就没有括号

四、拓展的对象功能
ES5我们对于对象都是以键值对的形式书写,是有可能出现键值对重名的。
function people(name, age) {
 return {
 name: name,
 age: age
 };
}
键值对重名,ES6可以简写如下:
 function people(name, age) {
 return {
 name,
 age
 };
 }

ES6 同样改进了为对象字面量方法赋值的语法。ES5为对象添加方法:
const people = {
name: ‘lux’,
getName: function() {
console.log(this.name)
}
}

ES6通过省略冒号与 function 关键字,将这个语法变得更简洁。
const people = {
name: ‘lux’,
getName () {
console.log(this.name)
}
}

ES6 对象提供了 Object.assign()这个方法来实现浅复制。
Object.assign() 可以把任意多个源对象自身可枚举的属性拷贝给目标对象,然后返回目标对象。第一参数即为目标对象。在实际项目中,我们为了不改变源对象。一般会把目标对象传为{}。

const objA = { name: 'cc', age: 18 }
const objB = { address: 'beijing' }
const objC = {}     // 这个为目标对象
const obj = Object.assign(objC, objA, objB)

// 我们将 objA objB objC obj 分别输出看看
console.log(objA)    // { name: 'cc', age: 18 }
console.log(objB)    // { address: 'beijing' }
console.log(objC)   // { name: 'cc', age: 18, address: 'beijing' }
console.log(obj)     // { name: 'cc', age: 18, address: 'beijing' }

// 目标对象ObjC的值被改变了。
// 所以,如果objC也是你的一个源对象的话。请在objC前面填在一个目标对象{}
Object.assign({}, objC, objA, objB)

5、更方便的数据访问–解构
数组和对象是JS中最常用也是最重要表示形式。为了简化提取信息,ES6新增了解构,这是将一个数据结构分解为更小的部分的过程。
ES5我们提取对象中的信息形式如下:
 const people = {
 name: ‘lux’,
 age: 20
 }
 const name = people.name
 const age = people.age
 console.log(name + ’ — ’ + age)

在ES6之前我们就是这样获取对象信息的,一个一个获取。现在,解构能让我们从对象或者数组里取出数据存为变量,例如:
 //对象
 const people = {
 name: ‘lux’,
 age: 20
 }
 const { name, age } = people
 console.log(${name} --- ${age})
 //数组
 const color = [‘red’, ‘blue’]
 const [first, second] = color
 console.log(first) //‘red’
 console.log(second) //‘blue’

6、Spread Operator 展开运算符
组装对象或者数组
//数组
const color = [‘red’, ‘yellow’]
const colorful = […color, ‘green’, ‘pink’]
console.log(colorful) //[red, yellow, green, pink]
//对象
const alp = { fist: ‘a’, second: ‘b’}
const alphabets = { …alp, third: ‘c’ }
console.log(alphabets) //{ “fist”: “a”, “second”: “b”, “third”: “c”
有时候我们想获取数组或者对象除了前几项或者除了某几项的其他项
//数组
const number = [1,2,3,4,5]
const [first, …rest] = number
console.log(rest) //2,3,4,5
//对象
const user = {
username: ‘lux’,
gender: ‘female’,
age: 19,
address: ‘peking’
}
const { username, …rest } = user
console.log(rest) //{“address”: “peking”, “age”: 19, “gender”: “female”

对于 Object 而言,还可以用于组合成新的 Object 。(ES2017 stage-2 proposal) 当然如果有重复的属性名,右边覆盖左边
const first = {
 a: 1,
 b: 2,
 c: 6,
 }
 const second = {
 c: 3,
 d: 4
 }
 const total = { …first, …second }
 console.log(total) // { a: 1, b: 2, c: 3, d: 4 }




7、import 和 export
import 导入模块,和export导出模块
 //全部导入
 import people from ‘./example’
 //有一种特殊情况,即允许你将整个模块当作单一对象进行导入
 //该模块的所有导出都会作为对象的属性存在
import * as example from “./example.js”
console.log(example.name)
console.log(example.age)
console.log(example.getName())


import {name, age} from ‘./example’
// 导出默认, 有且只有一个默认
export default App
// 部分导出
export class App extend Component {};

导入时有没有大括号的区别是什么?

1.当用export default people导出时,就用 import people 导入(不带大括号)

2.一个文件里,有且只能有一个export default。但可以有多个export。

3.当用export name 时,就用import { name }导入(记得带上大括号)

4.当一个文件里,既有一个export default people, 又有多个export name 或者 export age时,导入就用 import people, { name, age }

5.当一个文件里出现n多个 export 导出很多模块,导入时除了一个一个导入,也可以用import * as example

8、Promise

Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。简单点说,它就是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。

它的一般表示形式为:
new Promise(
/* executor /
function(resolve, reject) {
if (/
success /) {
// …执行代码
resolve();
} else { /
fail */
// …执行代码
reject();
}
}
);
接触过Node的人都知道,Node是以异步(Async)回调著称的,其异步性提高了程序的执行效率,但同时也减少了程序的可读性。如果我们有几个异步操作,并且后一个操作需要前一个操作返回的数据才能执行,这样按照Node的一般执行规律,要实现有序的异步操作,通常是一层加一层嵌套下去。
为了解决这个问题,ES6提出了Promise的实现。
在promise之前代码过多的回调或者嵌套,可读性差、耦合度高、扩展性低。通过Promise机制,扁平化的代码机构,大大提高了代码可读性;用同步编程的方式来编写异步代码,保存线性的代码逻辑,极大的降低了代码耦合性而提高了程序的可扩展性。
 其实就是用同步的方式去写异步代码。

其中,Promise中的参数executor是一个执行器函数,它有两个参数resolve和reject。它内部通常有一些异步操作,如果异步操作成功,则可以调用resolve()来将该实例的状态置为fulfilled,即已完成的,如果一旦失败,可以调用reject()来将该实例的状态置为rejected,即失败的。
我们可以把Promise对象看成是一条工厂的流水线,对于流水线来说,从它的工作职能上看,它只有三种状态,一个是初始状态(刚开机的时候),一个是加工产品成功,一个是加工产品失败(出现了某些故障)。同样对于Promise对象来说,它也有三种状态:
pending
初始状态,也称为未定状态,就是初始化Promise时,调用executor执行器函数后的状态。
fulfilled
完成状态,意味着异步操作成功。
rejected
失败状态,意味着异步操作失败。
它只有两种状态可以转化,即:
操作成功
pending -> fulfilled
操作失败
pending -> rejected
并且这个状态转化是单向的,不可逆转,已经确定的状态(fulfilled/rejected)无法转回初始状态(pending)。

方法:
Promise.prototype.then()
Promise对象含有then方法,then()调用后返回一个Promise对象,意味着实例化后的Promise对象可以进行链式调用,而且这个then()方法可以接收两个函数,一个是处理成功后的函数,一个是处理错误结果的函数。
var promise1 = new Promise(function(resolve, reject) {
// 2秒后置为接收状态
setTimeout(function() {
resolve(‘success’);
}, 2000);
});
promise1.then(function(data) {
console.log(data); // success
}, function(err) {
console.log(err); // 不执行
}).then(function(data) {
// 上一步的then()方法没有返回值
console.log(‘链式调用:’ + data); // 链式调用:undefined
}).then(function(data) {
// …
});

Promise.prototype.catch()
catch()方法和then()方法一样,都会返回一个新的Promise对象,它主要用于捕获异步操作时出现的异常。因此,我们通常省略then()方法的第二个参数,把错误处理控制权转交给其后面的catch()函数,如下:
var promise3 = new Promise(function(resolve, reject) {
 setTimeout(function() {
 reject(‘reject’);
 }, 2000);
});
promise3.then(function(data) {
 console.log(‘这里是fulfilled状态’); // 这里不会触发
 // …
}).catch(function(err) {
 // 最后的catch()方法可以捕获在这一条Promise链上的异常
 console.log(‘出错:’ + err); // 出错:reject
});
总之,除非Promise.then()方法内部抛出异常或者是明确置为rejected态,否则它返回的Promise的状态都是fulfilled态,即完成态,并且它的状态不受它的上一级的状态的影响。

Promise.all()
Promise.all()接收一个参数,它必须是可以迭代的,比如数组。
Promise.race()
Promise.race()和Promise.all()类似,都接收一个可以迭代的参数,但是不同之处是Promise.race()的状态变化不是全部受参数内的状态影响,一旦参数内有一个值的状态发生的改变,那么该Promise的状态就是改变的状态。就跟race单词的字面意思一样,谁跑的快谁赢。
Promise.resolve()
Promise.resolve()接受一个参数值,可以是普通的值,具有then()方法的对象和Promise实例。正常情况下,它返回一个Promise对象,状态为fulfilled。但是,当解析时发生错误时,返回的Promise对象将会置为rejected态。
Promise.reject()
Promise.reject()和Promise.resolve()正好相反,它接收一个参数值reason,即发生异常的原因。此时返回的Promise对象将会置为rejected态。