目录
9.9 Iterator (迭代器)与 for...of 循环
9.1 ES6介绍
概述
ES全称EcmaScript,是由Ecma国际通过ECMA-262(Ecma国际制定了很多标准,ECMA-262是其中一个)标准化的脚本程序设计语言,脚本语言的一种规范,而平时编写的JavaScript,是EcmaScript的一种实现,所以ES新特性其实是指JavaScript的新特性
ES6是EcmaScript第六版本,全称:ECMAScript 6.0。2015年出版新增了:模块化,面向对象语法,Promise、箭头函数、let、const、数组解构赋值等等。ECMAScript 6 目前基本成为业界标准,它的普及速度比 ES5 要快很多,主要原因是现代浏览器对 ES6 的支持相当迅速,尤其是 Chrome 和 Firefox 浏览器,已经支持 ES6 中绝大多数的特性。
9.1.1 为什么要学习ES新特性
1.语法简洁,功能丰富
2.框架内开发应用
3.前端开发职业要求
9.1.2 为什么要单独学习ES6
1.ES6版本变动最多,具有里程碑意义
2.ES6加入许多语法新特性,编程实现更加简单、高效
3.ES6是前端发展趋势,就业必备技能
4.3.微信小程序,uni-app等都是基于ES6的语法
9.2 let 、const 详解
9.2.1 let 变量
ES6新增了let变量,它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
let 允许创建块级作用域,ES6 推荐在函数中使用 let 定义变量,而非 var
let变量声明
声明方式:
let a;
let b,c,d;
注意事项:let变量不能重复声明,防止变量重复命名导致变量污染
重复声明报错:
let 作用域
简述:let变量声明仅在同一代码块内可以读取,只做用于块级作用域
{
let hello = 'ES6';
}
console.log(hello); //Error:hello is not defined. hello 未定义
let 不存在变量提升
简述:变量在未声明前不可以使用
console.log(song);
let song = '请出现吧'
//Error: Cannot access 'song' before initialization. 不允许在声明前使用
9.2.2 块级作用域
块级作用域产生原因
ES5只包括全局作用域和函数作用域,但是在应用中一些内层变量因为变量提升而变成全局变量,使内层逻辑不符常理,因此ES6出现“块级作用域”这一方法解决其产生的问题。
var a = '外层变量';
function fn() {
console.log(a);
if (false) {
var a = '内层变量';
}
}
fn();//undefined
块级作用域的应用
let 变量没有变量提升,仅在当前代码块内作用,其特点很好的解决了这一问题。
let实际上为 JavaScript 新增了块级作用域:
let a = '外层变量';
function fn() {
console.log(a);
if (false) {
let a = '内层变量';
}
}
fn();//外层变量
9.2.3 const 常量
同样在块级作用域有效的另一个变量声明方式是 const,const的与let的特点基本相同,同样不存在变量提升,只能在声明后使用。
const 常量声明
声明方式:
const NAME = 'ES6常量';
注意事项:常量声明时就需要赋值且一般常量值需要大写
const 常量值无法修改
const NAME = 6;
NAME = 5;
console.log('NAME');//Error: Assignment to constant variable
对数组和对象元素的修改,不算对常量值的修改,并不会报错
const ARR = ['sa', '56ty', 'assd']
ARR.push('uiop')
console.log(ARR);// 正常输出:["sa", "56ty", "assd", "uiop"]
const 常量作用域
简述:仅在同一代码块内可以读取,只做用于块级作用域
{
const LEARNING = '常量';
}
console.log(LEARNING); //Error:LEARNING is not defined
9.3 解构赋值
概述
ES6允许按照一定模式从数组和对象中提取值,并对变量进行赋值。
解构赋值,左右结构必须一样,使用左边定义的值,快速的取出数据中对应的数据值,而且定义和赋值必须放到一起,不然的话就会报错,取不出来数据值
9.3.1数组模型的解构赋值
将左边数组中的变量按照对应的位置,在右边进行赋值,只要两边模式相同,左边变量会被赋予右边对应的值
基础解构赋值过程:
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
嵌套解构赋值:
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
忽略解构赋值:
let [a, , b] = [1, 2, 3];
// a = 1
// b = 3
不完全解构赋值:
let [a, b] = [1, 2, 3];
// a = 1
// b = 2
9.3.2对象模型的解构赋值
解构不仅可以用于数组,还可以用于对象。
基础解构赋值过程:
let { one, two } = { one: 'aaa', two: 'bbb' };
// one = 'aaa'
// two = 'bbb'
let { one : two } = { one : 'ddd' };
// two = 'ddd'
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { two , one, } = { one: 'aaa', two: 'bbb' };
// one = 'aaa'
// two = 'bbb'
let { tr } = { three : 'ddd' };
tw // undefined
嵌套解构赋值
let a = {b: ['learn', {c: 'ES6'}] };
let {b: [d, { c }] } = a;
// d = 'learn'
// c = 'ES6'
忽略解构赋值:
let a = {b: ['learn', {c: 'ES6'}] };
let {b: [d, { }] } = a;
// d = 'learn'
不完全解构赋值
let a = {b: [{c: 'ES6'}] };
let {b: [c, { d }] } = a;
// d = undefined
// c = 'ES6'
9.4 模板字符串
ES6引入新的声明字符串方式 `` '' ""(反引号、单引号、双引号)
9.4.1声明
let str = `这是一个字符串啊!`
9.4.2 应用
使内容中可以出现换行符
let str = ` <ul>
<li>11</li>
<li>22</li>
<li>33</li>
</ul>`;
变量拼接
let learn = 'ES6';
let view = `${learn}里面的模板字符串学习`
console.log(view); //ES6里面的模板字符串学习
9.5 对象的扩展
9.5.1 简化对象写法
ES6 允许在大括号内,直接写入变量和函数,作为对象的属性和方法 提高代码书写的简洁性
let name = 'ES6';
let change = function() {
console.log('这是普通无省略的函数声明方式!')
}
const school = {
name,
change,
learn() {
console.log("快看变量引入方式和函数声明方式!!");
}
}
console.log(school);
控制台输出:
9.5.2方法的 name 属性
函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。
var person = {
sayName() {
console.log(this.name);
},
get firstName() {
return "Nicholas";
}
};
person.sayName.name // "sayName"
person.firstName.name // "get firstName"
上面代码中,方法的name属性返回函数名(即方法名)。如果使用了取值函数,则会在方法名前加上get。如果是存值函数,方法名的前面会加上set。
9.5.3 扩展运算符
拓展运算符(...)用于取出参数对象所有可遍历属性然后拷贝到当前对象。
基本用法
let student = {name: "aa", age: 10};
let one = { ...student };
someone; //{name: "aa", age: 10}
自定义的属性和拓展运算符对象里面属性的相同的时,自定义的属性在拓展运算符后面,则拓展运算符对象内部同名的属性将被覆盖掉:
let student= {name: "aa", age: 10};
let one = { ...student, name: "bb", age: 18};
one; //{name: "bb", age: 18}
自定义的属性在拓展运算度前面,则变成设置新对象默认属性值。
let student= {name: "aa", age: 10};
let one = { name: "bb", age: 18, ...student };
one; //{name: "aa", age: 10}
扩展运算符应用于数组
数组的合并
const a =['aaa','bbb','ccc'];
const b =['ddd','eee','fff'];
const c = [...a,...b];//['aaa','bbb','ccc','ddd','eee','fff']
数组的克隆
const d = ['G','H','I']
const e = [...d]
console.log(e) //['G','H','I']
将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
const divArr = [...divs];
console.log(e); //[div,div,div]
9.6 函数的扩展
9.6.1 参数默认值
在 ES6 之前,我们不能直接为函数的参数指定默认值,只能采用变通的方法。而 ES6 中允许我们直接为函数的参数设置默认值,通过等号 = 直接在小括号中给参数赋值
function fn(name,age="10")
{
console.log(`我是${name},今年${age}岁`);
}
fn("小E"); //我是小E,今年10岁
传递undefined,取默认值:
function fn(name="小s",age)
{
console.log(`我是${name},今年${age}岁`);
}
fn(undefined,15); //我是小s,今年15岁
9.6.2 rest参数
ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
rest 参数必须要放到参数最后, rest后面的参数没有意义
function data(){
console.log(arguments);//输出格式为对象
}
data('11','22','33');
// rest参数
function data(...args){
console.log(args);//输出格式为数组,便于处理数据
}
data('11','22','33');
9.6.3 严格模式
从ES5开始,函数内部可以设定为严格模式。
function learn(a, b) {
'use strict';
// code
}
《ECMAScript 2016标准》做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
这样规定的原因是,函数内部的严格模式,同时适用于函数体代码和函数参数代码。但是,函数执行的时候,先执行函数参数代码,然后再执行函数体代码。这样就有一个不合理的地方,只有从函数体代码之中,才能知道参数代码是否应该以严格模式执行,但是参数代码却应该先于函数体代码执行。
// 报错
function a(value = 070) {
'use strict';
return value;
}
上面代码中,参数value的默认值是八进制数070,但是严格模式下不能用前缀0表示八进制,所以应该报错。但是实际上,JavaScript引擎会先成功执行value = 070,然后进入函数体内部,发现需要用严格模式执行,这时才会报错。
虽然可以先解析函数体代码,再执行参数代码,但是这样无疑就增加了复杂性。因此,标准索性禁止了这种用法,只要参数使用了默认值、解构赋值、或者扩展运算符,就不能显式指定严格模式。
两种方法可以规避这种限制。第一种是设定全局性的严格模式,这是合法的。第二种是把函数包在一个无参数的立即执行函数里面。
//1.第一种
'use strict';
function learn(a, b = a) {
// code
}
//2.第二种
const learn= (function () {
'use strict';
return function(value = 42) {
return value;
};
}());
9.6.4 name属性
函数的name属性,返回该函数的函数名
function a() {} a.name // "a"
ES6对这个属性的行为做出了一些修改。如果将一个匿名函数赋值给一个变量,ES5的name属性,会返回空字符串,而ES6的name属性会返回实际的函数名。
var a = function () {};
// ES5
func1.name // ""
// ES6
func1.name // "a"
如果将一个具名函数赋值给一个变量,则ES5和ES6的name属性都返回这个具名函数原本的名字。
const b = function a() {};
// ES5
bar.name // "a"
// ES6
bar.name // "a"
9.6.5 箭头函数
概述
简化定义函数语法
省略小括号,当形参有且只有一个
let a = b => b;
上面的箭头函数等同于:
let a = function(b) { return b; };
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
let a = () => 5;
// 等同于
let a = function () { return 5 };
let sum = (num1, num2) => num1 + num2;
// 等同于
let sum = function(num1, num2) {
return num1 + num2;
};
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
let sum = (num1, num2) => { return num1 + num2; }
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。
let a = id => ({ id: id, name: "cc" });
this指向
this 是静态的 this 始终指向函数声明时所在的作用域下的this值
function getName() {
console.log(this.name);
}
let getName2 = () =>{
console.log(this.name);
}
window.name = '设置window对象的name属性';
const school = {
name: "函数声明时所在函数作用域下的name属性"
}
箭头函数不能作为构造函数去实例化对象
let Person = (name, age) => {
this.name = name;
this.age = age;
}
let me = new Person('xiao',30);
console.log(me);//Error:Person is not a constuctor //Person 不是一个构造函数
9.7 Symbol
9.7.1 Symbol介绍
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值.是javaScript语言的第七种数据类型,类似于字符串
9.7.2 Symbol 特点
1.值是唯一的,可以用来解决命名冲突的问题
2.不能与其他数据进行运算
3.使用Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的键名
Symbol函数创建
值是唯一的,可以用来解决命名冲突的问题
let a = Symbol();
let a1 = Symbol('这是a1');
let a2 = Symbol('这是a1');
console.log(a1 === a2);//false
创建Symbol.for(Symbol函数对象创建)
无法与其他数据进行运算
let b = Symbol.for;
let b1 = Symbol.for('这是b')
let b2 = Symbol.for('这是b')
console.log(b1 === b2);//true
Symbol创建对象属性
向对象中添加方法 up down
let game ={}
// 声明一个对象
let methods = {
up: Symbol(),
down: Symbol()
};
// 第一种添加方式:
game[methods.up] = function(){
console.log("upup")
}
game[methods.down] = function(){
console.log("downdown")
}
console.log(game);
// 第二种添加方式:
let learn = {
name:"对象内添加方法",
[Symbol('say')]: function(){
console.log("讲出来");
},
[Symbol('read')]: function(){
console.log("读出来");
},
}
9.8 Promise 对象
概述
Promise 是ES6引入的异步编程的新的解决方案。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。Promise 对象是一个构造函数,用来封装异步操作并可以获取其成功或失败的的结果,可以解决回调地狱的问题
9.8.1 Promise状态
Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。
1.promise 对象初始化状态为 pending
2.当调用resolve(成功),会由pending => fulfilled
3.当调用reject(失败),会由pending => rejected
Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
9.8.2 Promise基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
const promise = new Promise((resolve, reject) => {
// 异步处理
// 处理结束后、调用resolve 或 reject
});
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Promise.prototype.then()
Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
const promise1 = new Promise((resolve, reject) => {
resolve('Success!');
});
promise1.then((value) => {
console.log(value);
// expected output: "Success!"
});
Promise.prototype.catch()
catch() 方法返回一个promise,并且处理拒绝的情况。
p.catch(onRejected);
p.catch(function(reason) {
// 拒绝
});
Promise.prototype.finally()
finally() 方法返回一个promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。
p.finally(onFinally);
p.finally(function() {
// 返回状态为(resolved 或 rejected)
});
9.9 Iterator (迭代器)与 for...of 循环
概述:
迭代器允许每次访问数据集合的一个元素,当指针指向数据集合最后一个元素时,迭代器便会退出。它提供了 next() 函数来遍历一个序列,这个方法返回一个包含 done 和 value 属性的对象。
ES6 中可以通过 Symbol.iterator 给对象设置默认的遍历器,无论什么时候对象需要被遍历,执行它的 @@iterator 方法便可以返回一个用于获取值的迭代器。
//声明一个数组
const web = ['h5','css','javaScript','jQuery','vue'];
// 使用for...of遍历数组, 展现的是数组的value值
for(let v of web){
console.log(v);//h5 css javaScript jQuery vue
}
console.log(web);
//数组内有Symbol(Symbol.iterator): ƒ values() 函数,所以可以遍历
9.10 Generator 函数
Generator 函数是 ES6 的新特性,它允许一个函数返回的可遍历对象生成多个值。
在使用中你会看到 * 语法和一个新的关键词 yield:
function * learn(){
yield '小白兔';
yield '白又白';
yield '两只耳朵';
yield '竖起来';
}
// let iterator = learn();
// console.log(iterator.next());//value: "小白兔" done: false
// console.log(iterator.next());//value: "白又白" done: false
// console.log(iterator.next());//value: "两只耳朵" done: false
// console.log(iterator.next());//value: "竖起来" done: false