初识ES6
ES6,全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。
- ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念,但是目前浏览器的 JavaScript 是 ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。
ECMAScript 的背景
- JavaScript 是大家所了解的语言名称,但是这个语言名称是商标( Oracle 公司注册的商标)。因此,JavaScript 的正式名称是 ECMAScript 。1996年11月,JavaScript 的创造者网景公司将 JS 提交给国际化标准组织 ECMA(European computer manufactures association,欧洲计算机制造联合会),希望这种语言能够成为国际标准,随后 ECMA 发布了规定浏览器脚本语言的标准,即 ECMAScript。这也有利于这门语言的开放和中立。
开始学习ES6新语法
变量的定义(let和const)
-
let的作用是声明一个变量,与var类似
let str = 'oh,yes'
-
let特点: 在块作用域下有效,不能重复声明,不会预处理,不存在变量提升
-
什么时候使用?
- 循环遍历加监听
- 使用let代替var是一种流行趋势
-
小demo(进行遍历监听事件的时候,会存在值覆盖的问题,可以使用闭包来解决,但是这种方式比较麻烦,再加入let关键字后,我们可以使用let来代替var声明变量,则会有块作用域效果)
for(var i=0;i<btns.length;i++){
var btn = btns[i];
(function(i){
btn.onclick = function(){
alert(i)
}
})(i)
}
- const的作用是定义一个常量
- 在ES6之前,并没有真正意义上的常量,只是在定义变量的时候,给变量名为全大写作为常量的标明,但是在浏览器解析是它任然只是一个变量。
- 语法
const USER = 'root'
,特点:一旦定义不能修改 ,其他同let一样。一般用于不会修改的数据
解构赋值
-
从对象或者数组中提取数据,并赋值给一个或多个变量(相比传统取值方式,更加便捷并且解析速度会更快),也可用与参数的传递
-
基本语法:
- 数组:
let [a,b] = [1,2]
- 对象:
let {name,age} = {name:'jack',age:18}
- 数组:
-
对象解构时,需要在接收的时候也是一个对象,接收的对象中写入需要接收的属性,属性名需要在解构的对象中存在
-
数组解构时,需要在接收的时候也是一个数组,接收的数组中的变量位置,对应数组下标取值(可以使用逗号占位的形式取下标)
模板字符串
- 简化字符串的操作
- 模板字符串必须使用`` 反引号括起来
- 使用变量的部分用
${变量名}
定义
let love = '爱情'
let food = '面包'
let str = `${love}和${food}都会有的 `
对象的简化写法
- 省略同名的属性值
- 省略方法的function关键字
- 小demo(在对象方法或者设置属性值时,调用对象中的属性需要使用this关键字)
let name = 'dson'
let age = 18
let obj = {
name,
age,
set(name){
this.name = name
}
}
obj.set('jack')
console.log(obj.name)
剩余运算符
-
rest可变参数 (可以用来代替argument对象)
- 比arguments对象更加灵活,可以接受剩余参数,但是只能是最后部分参数
- 只在第一次使用的时候加上… ,下次使用时直接用后面的变量名即可
- 获取得到的是一个纯粹的数组,可以使用forEach方式遍历
function demo(a,b,...gather){ console.log(arguments) arguments.forEach(function(item,index){ console.log(item,index) }) console.log(gather) gather.forEach(function(item,index){ console.log(item,index) }) } demo(1,2,3,4)
-
扩展运算符 (可以用于操作数组,不能用于对象)
let arr = [2,3,4] let arr2 = [1,5] arr2 = [1,...arr,5] console.log(arr.concat(arr2)) let obj2 = { name:'jack', age:18 } console.log(...obj2)
形参的默认值
- 在ES5中形参的默认值为undefined,ES6以后可以给形参手动设置默认值
- 当没有参数传入的时候,默认使用手动设置的值
function demo(num1 = 0, num2 = 0){
this.num1 = num1
this.num2 = num2
}
let dm = new demo(2,3)
console.log(dm)
箭头函数的使用 [ 搭配demo食用效果更佳 ]
-
箭头函数是用来定义匿名函数的,基本语法
demo = () => console.log('内容')
-
箭头函数可以传递参数
demo = str => str
等于function demo(str){return str}
-
参数部分
- 不传参数时,必须使用()进行占位
- 传递一个参数的时()可加,可不加
- 传递多个参数的时候需要使用()将参数包裹
-
函数体部分
- 函数体不使用{}时,默认返回结果
- 函数体中如果存在多个语句,则需要使用{}包裹,需要返回值时,可以手动添加返回值
-
-
箭头函数多用于定义回调函数
-
箭头函数的特性
- 语法简洁
- 箭头函数没有自己this,箭头函数的this是定义的时候处在的对象,而不是调用时候的对象
- 可以理解为:
- 箭头函数的this是看外层是否存在函数
- 如果有,外层函数的this就是箭头函数的this
- 如果没有,this则是指向window
Promise对象
-
什么是Promise对象
- Promise对象指代未来某个时间节点将要发生的事件 [ 通常为一个异步操作 ]
- 该对象可以将异步操作以同步的流程表达出来,避免多层嵌套回调函数
- 该对象是一个构造函数,用来生成promise实列
-
Promise 异步操作有三种状态:pending(进行中 [ 初始化 ])、fulfilled(已成功)和 rejected(已失败)
- fulfilled和rejected状态只会有其中一个生效
-
Promise对象基本使用
-
创建promise对象
let promise = new Promise((resolve,reject)=>{ //进行异步操作,一般为ajax数据请求 //resolve(res) reject(res) }) promise.then((res)=>{ console.log('成功') },(res)=>{ console.log('失败') })
-
调用成功的resolve(),或者失败的reject()方法最终都会返回一个promise对象。使用then方法可以对不同状态进行操作。
-
then的第一个回调函数对应的是resolve()方法,第二个回调函数对应reject()方法
-
then的回调函数中可以接收对应方法传递的参数
-
-
-
在保证返回值为promise对象时,可以进行链式操作then()方法[ 具体请看demo ]
Symbol类型的使用
-
ES6 数据类型除了 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,还新增了 Symbol
-
Symbol类型的特性:
- Symbol属性对应的值是唯一的,解决命名冲突问题
- Symbol的值不能与其他数据类型进行计算,字符串拼接也不行
- Symbol不能使用 for in 和 for of 遍历
-
基本使用:
let symbol = Symbol()
-
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
-
Symbol的内置值
- ES6中为Symbol提供了11个内置的值,指向语言内部使用的方法
- Symbol.iterator属性,通常用于定义对象的唯一属性
let obj = {
name:'jack',
age:18
}
obj[Symbol.iterator] = function(){
}
Iterator(遍历器)
-
概念:遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。
-
iterator主要作用
-
为各种数据结构,提供一个统一的、简便的访问接口
-
使得数据结构的成员能够按某种次序排列
-
ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费
-
-
Iterator 的遍历过程是这样的。
-
创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
-
第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
-
第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
-
不断调用指针对象的next方法,直到它指向数据结构的结束位置。
-
遍历结束时,value的值为undefined,done的值为true,是否执行的依据为done的值为false时。
- 每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
-
-
原生具备iterator接口数据(可使用for of 遍历)
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList (DOM)对象
-
for of方法的使用
-
for of 不能遍历普通对象
-
使用的iterator接口
-
剩余运算符使用的也是 iterator接口
for(let key of arr){ console.log(key) }
-
Symbol.iterator基本实现原理
function iterator(arr) {
// 相当于内置的指针对象
let index = 0
return {
next() {
// 判断使用三元运算符判断当前指针下标是否超过数组的长度
return index < arr.length ?
{ value: arr[index++], done: false } :
{ value: arr[index++], done: true };
}
}
}
let arr = [5, 4, 3, 2, 1]
//调用一次这个方法,就取出对应下标的值
let iter = iterator(arr)
console.log(iter.next())
Symbol.iterator底层实现原理
数组遍历实现
- 手动修改Array原型中的Symbol.iterator 指向到自定义函数中
Array.prototype[Symbol.iterator] = iteratorTool
- 修改原型对象方法后,自定义的iteratorTool()方法,不在接收参数,此时函数中的数组长度调用会发生错误。
- 在iteratorTool()方法中,this指向的是遍历的数组,是因为当调用for…of方法时,会触发arr数组中的Symbol.iterator属性中的方法。
- 首先将this保存,防止作用域污染
let _this = this
- 其次修改函数中的
arr.length
为_this.length
即可
- 首先将this保存,防止作用域污染
对象遍历和数组遍历共同实现
-
函数需要同时实现两种遍历形式,需要进行判断,当前this是数组还是对象,
- 使用 instanceof 关键字用于判断
_this instanceof Array
- 使用 instanceof 关键字用于判断
-
在对象中不存在下标概念,所以使用取下标的形式,不能将对象中的属性值拿出来
- 将需要遍历的对象转换成数组形式
Object.keys(obj)
- 数组形式下我们可以拿到所有属性的length长度
Object.keys(obj).length
,用于判断next()函数是否继续执行 - 将返回的对象中的value属性的值修改为对象取值形式
_this[Object.keys(obj)[index++]]
- 将需要遍历的对象转换成数组形式
Generator 函数(过渡异步处理-不常用)
-
Generator 函数的概念
- Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
- Generator 函数内部封装不同状态的数据
- 用来生成遍历器对象
- 可暂停函数(惰性求值),yield关键字进行暂停,next()方法启动。每次返回的是yield后的表达式执行的结果
-
Generator 函数的特性
- function与函数名之间有一个星号*
function* demo(){}
- 内部使用yield来定义不同状态,基本定义
- function与函数名之间有一个星号*
function* demo(){
let result = yield 1;
yield 2;
}
- 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象
- 调用next()方法函数内部的逻辑开始执行[ 至少调用一次,函数体才会执行 ],遇到yield或return语句时停止。yield后面的那个表达式的值,作为返回的对象的value属性值
- 在次调用next()方法会从上一次的yield出开始执行,直到最后
- yield在未设定返回值是默认为undefined
3. Generator 函数的基本使用
- next()方法需要在得到调用函数的返回对象下使用
```
function* generatorTool(){
console.log('我要开始啦~')
yield 1
console.log('我在中间')
yield 2
console.log('我已经结束了~')
}
let Gt = generatorTool()
Gt.next()
```
- Generator 函数处理异步操作
-
在yield关键字后可以调用函数(进行数据请求等)
-
在调用的函数中使用next()方法,将对象指针下移,逻辑代码继续执行。
-
调用next()方法时可以进行传参,参数作为yield关键字后的表达式返回值
function AsyncTool(){ setTimeout(()=>{ let data = '我是数据' Gt.next(data) },2000) } function* generatorTool(){ let res = yield AsyncTool() yield AsyncTool(res) }
-
####async 函数的使用
-
概念:真正意义上解决了异步回调的问题,同步流程表达异步操作
-
本质:Generator的语法糖
-
async 函数的基本语法:
async function demo(){ await 异步操作; await 异步操作; }
4.async 函数的特性:
- 不需要像Generator去调用next()方法,遇到await关键字时等待,当前的异步操作完成后继续往下执行
- 返回的是一个Promise对象,可以使用then()方法进行操作
- async关键字取代了Generator函数的*号,await取代了yield
- 相对其他异步操作解决方案更简洁
5.async搭配Promise进行异步操作
- await关键字会自动识别Promise对象的state状态值,用于判断是否继续执行
function promiseTool(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('我执行了')
resolve(res)
},2000)
})
}
async function asyncTool(){
console.log('我开始了~')
let res = await promiseTool()
console.log('我继续')
await promiseTool(res)
console.log('我完事了')
}
asyncTool()
Class关键字
Class的基本使用
-
通过class定义类
class Person{}
-
在类中通过constructor() 定义构造方法
class Person{ constructor(name,age){ console.log('构造函数会被自动调用'); this.name = name this.age = age } }
-
通过new来创建类的实列
let person = new Person('jack',18)
-
类的一般方法 [ 在类中直接定义的方法可以用于继承 ]
-
等同于在原型对象中直接添加方法
Person2.prototype.say = function(){}
say(){ console.log(this.name,this.age); }
-
5.static关键字定义静态方法或者属性
- static静态关键字声明的方法或属性只提供给类的对象使用,实例不能使用
static demo(){
console.log('我是静态方法')
}
类的继承
-
通过extends实现类的继承
-
继承了父类的所有属性和方法
-
等同于
ChildPerson.prototype = new Person()
class ChildPerson extends Person{}
-
-
通过super()调用父类的构造方法
-
子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象
class ChildPerson extends Person{ constructor(name,age,sex){ super() this.sex = sex } } let Cp = new ChildPerson('tom',23,'1') Cp.say()
-
如果需要使用父类中的的构造函数的属性,需要对super进行传参
super(name,age)
-
-
重写继承父类中的一般方法
-
默认继承的方法在隐式原型中,运行时会一层一层往下找
-
当父类的方法不能满足子类实例对象的需求时进行重写 [ 当子类中有和父类中重名方法时,使用子类中的方法,不再继续往下 ]
say(){ console.log(this.name,this.age,this.sex) }
-
其他新增扩展
字符串的新增方法
- includes(str) 判断是否包含指定的字符串
- startsWith(str) 判断是否以指定的字符开头
- endsWith(str) 判断是否以指定的字符结尾
- repeat(number) 将字符串重复指定次数
数值的扩展
- Number.isFinite() 判断是否为一个有限大的数
- Number.isNaN() 判断是否是NaN
- Number.isInteger() 判断一个数值是否为整数
- Number.parseInt() 转换字符串为数值
- Math.trunc(number.float) 去除小数部分
数组的扩展
-
Array.from() 将伪数组对象或者可遍历对象转换成纯粹的数组
-
此处获取到一组button按钮的对象是一个伪数组
let btns = document.getElementsByTagName('button') Array.from(btns).forEach((item,index)=>{ console.log(item) })
-
-
Array.of() 用于将一组值,转换为数组
Array.of(1,'str',true)
-
find() 找出第一个满足条件的元素,返回该成员 ,没有则返回undefined
-
这是实例对象中的方法,完整写法 Array.prototype.find()
find(function(value,index,arr){return 要找的元素})
-
基本使用
let arr = [1,2,4,2,4,6]
let res = arr.find(function(item,index){
return item > 3
}
-
-
findIndex() 找出第一个满足条件的元素,返回该元素在数组中的下标 ,没有则返回-1
findIndex(function(value,index,arr){return 要找的元素})
对象的扩展
-
Object.is(value1,value2) 比较两个值是否相等
-
Object.assign(target,sourcel) 用于对象的合并,将源数组的属性赋值到目标属性上
let obj1 = {name:'jack',age:18} let obj2 = {} Object.assign(obj2,obj1) console.log(obj2)
-
直接操作__porto__ 属性
set和map数据结构
set的使用
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)); // add()是set对象添加元素的方法
for (let i of s) {
console.log(i);
}
利用这个特性可以简单的完成一些数组中相同值的排除
向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===),主要的区别是向 Set 加入值时认为NaN等于自身,而精确相等运算符认为NaN不等于自身。另外,两个对象总是不相等的。
Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
// 例三
const set = new Set(document.querySelectorAll('div'));
set.size // 56
// 类似于
const set = new Set();
document
.querySelectorAll('div')
.forEach(div => set.add(div));
set.size // 56
Set 实例的属性和方法
Set 结构的实例有以下属性。
- Set.prototype.constructor:构造函数,默认就是Set函数。
- Set.prototype.size:返回Set实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
- Set.prototype.add(value):添加某个值,返回 Set 结构本身。
- Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
- Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
- Set.prototype.clear():清除所有成员,没有返回值。
s.add(1).add(2).add(2);
// 注意2被加入了两次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
Array.from方法可以将 Set 结构转为数组。
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
遍历操作
Set 结构的实例有四个遍历方法,可以用于遍历成员。
- Set.prototype.keys():返回键名的遍历器
- Set.prototype.values():返回键值的遍历器
- Set.prototype.entries():返回键值对的遍历器
- Set.prototype.forEach():使用回调函数遍历每个成员
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
WeakSet
WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。
首先,WeakSet 的成员只能是对象,而不能是其他类型的值。
const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
第二个区别是WeakSeet中的对象都弱引用
Map
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
下面代码使用 Map 结构的set方法,将对象o当作m的一个键,然后又使用get方法读取这个键,接着使用delete方法删除了这个键。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
作为构造函数,Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
实例的属性和操作方法
Map 结构的实例有以下属性和操作方法。
-
size属性返回 Map 结构的成员总数。
-
Map.prototype.set(key, value)
set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。const m = new Map(); m.set('edition', 6) // 键是字符串 m.set(262, 'standard') // 键是数值 m.set(undefined, 'nah') // 键是 undefined
-
Map.prototype.get(key)
get方法读取key对应的键值,如果找不到key,返回undefined。 -
Map.prototype.has(key)
has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。 -
Map.prototype.delete(key)
delete方法删除某个键,返回true。如果删除失败,返回false。 -
Map.prototype.clear()
clear方法清除所有成员,没有返回值。
遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
- Map.prototype.keys():返回键名的遍历器。
- Map.prototype.values():返回键值的遍历器。
- Map.prototype.entries():返回所有成员的遍历器。
- Map.prototype.forEach():遍历 Map 的所有成员。
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"