ECMAScript
是一种由 ECMA 组织制定和发布的脚本语言规范
- JavaScript 是 ECMA 的实现
- JS 包含三个部分
- ECMAScript (核心语法)
- BOM (浏览器对象模型)
- DOM (文档对象模型)
- ES 的几个重要版本
- ES5 —— 09年发布
- ES6 (ES2015)
- ES7 (ES2016)
ES5
严格模式
目的
消除 JavaScript 语法的一些不合理、不严谨之处, 减少一些怪异行为
消除代码的一些不安全之处, 为代码的安全运行保驾护航
使用
- 在全局或函数的第一条语句定义为 ——
'use strict'
- 在全局或函数的第一条语句定义为 ——
语法
必须使用 var 声明变量
禁止自定义函数中的 this 指向 window
1
2
3
4
5
6function Person (name, age) {
this.name = name
this.age = age
}
Person('zs', 20) // 报错
new Person('zs', 20) // 正常创建 eval 作用域
1
2
3
4// eval() 是一个函数, 可以将字符串解析成 js 代码, 非严格模式下没有自己的作用域
var str = 'NBA'
eval('var str = "CBA"; alert(str);') // CBA
alert(str) // 不使用严格模式 - CBA 使用严格模式 - NBA对象不能有重名属性
JSON 工具类
API | 说明 |
---|---|
JSON.stringify(obj / arr) | 将 js 对象(数组) 转 json 字符串 |
JSON.parse(json) | 将 json 字符串 转 js 对象(数组) |
Object 扩展
ES5 给 Object 扩展了一些静态方法
Object.create(prototype, [descriptors])
- 作用 —— 以指定对象为原型创建新的对象
- descriptors —— 为新的对象指定新的属性, 并对属性进行描述
- value : 指定值
- writable : 标识当前属性值是否是可修改的, 默认为 false
- configurable: 标识当前属性是否可以被删除 默认为 false
- enumerable: 标识当前属性是否能用 for in 枚举 默认为 false
1
2
3
4
5
6
7
8
9
10
11var obj = {name : 'curry', age : 29}
var obj1 = {};
obj1 = Object.create(obj, {
sex : {
value: '男',
writable: true, // 设置可修改, 不设置则为不可修改
enumerable: true // 设置可枚举
}
});
obj1.sex = '女';
console.log(obj1.sex); // '女'
Object.defineProperties(object, descriptors)
- 作用 —— 为指定对象定义扩展多个属性
- get —— 用来获取扩展属性值的回调函数
- set —— 监听扩展属性值的变化自动调用, 并且实参即为修改的值
- 存取器属性 —— setter / getter 一个用来存值, 一个用来取值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//Object.defineProperties(object, descriptors)
var obj2 = {
firstName : 'curry',
lastName : 'stephen'
};
Object.defineProperties(obj2, {
fullName: {
get: function () {
return this.firstName + '-' + this.lastName
},
set: function (data) {
var names = data.split('-');
this.firstName = names[0];
this.lastName = names[1];
}
}
});
console.log(obj2.fullName); // curry-stephen
obj2.firstName = 'tim';
obj2.lastName = 'duncan';
console.log(obj2.fullName); // tim-duncan
obj2.fullName = 'kobe-bryant'; // 自动调用 set
console.log(obj2.fullName); // kobe-bryant
- 作用 —— 为指定对象定义扩展多个属性
对象本身的两个方法
- get propertyName() {} 获取当前属性
- setpropertyName() {} 设置当前属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var obj = {
firstName : 'kobe',
lastName : 'bryant',
get fullName(){
return this.firstName + ' ' + this.lastName
},
set fullName(data){
var names = data.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
};
console.log(obj.fullName);
obj.fullName = 'curry stephen';
console.log(obj.fullName);
Array 的扩展
- Array.prototype.indexOf(value)
- 得到元素在数组中的第一个下标
- Array.prototype.lastIndexOf(value)
- 得到元素在数组中的最后一个下标
- Array.prototype.forEach(function (item, index) {})
- 遍历数组
- Array.prototype.map(function (item, index) {})
- 遍历数组,
返回一个新数组
, 返回加工后的值组成的新数组 - 新数组和原数组长度一致, 一对一的关系
- 遍历数组,
- Array.prototype.filter(function (item, index) {})
- 遍历
过滤出一个新的子数组
, 返回条件为 true 的值组成的新数组 - 新数组是原数组的子数组, 长度可以比原数组小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31/*
需求:
1. 输出第一个6的下标
2. 输出最后一个6的下标
3. 输出所有元素的值和下标
4. 根据arr产生一个新数组,要求每个元素都比原来大10
5. 根据arr产生一个新数组, 返回的每个元素要大于4
*/
var arr = [1, 4, 6, 2, 5, 6];
console.log(arr.indexOf(6));//2
//Array.prototype.lastIndexOf(value) : 得到在数组中的最后值的下标
console.log(arr.lastIndexOf(6));//5
//Array.prototype.forEach(function(item, index){}) : 遍历数组
arr.forEach(function (item, index) {
console.log(item, index);
});
//Array.prototype.map(function(item, index){}) : 遍历数组返回一个新的数组,返回加工之后的值
var arr1 = arr.map(function (item, index) {
return item + 10
});
console.log(arr, arr1);
//Array.prototype.filter(function(item, index){}) : 遍历过滤出一个新的子数组,返回条件为true 的值
var arr2 = arr.filter(function (item, index) {
return item > 4
});
console.log(arr, arr2);
- 遍历
Function 的扩展
Function.prototype.bind(obj)
- 将函数内部的 this 绑定为 obj, 并将函数返回
区别 bind()、call()、apply()?
- 都能重新指定函数中的 this
- call() 和 apply() 是 立即调用 函数
- 不传参数的时候使用方式一样
- 传参数的时候, call() 直接写参数, apply() 参数需要放在 数组内
- bind() 是将函数返回
- bind() 的特点 - 绑定完 不会立即调用 当前函数, 而是将函数返回
- 传参的方式 - 同 call 一样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var obj = { name: 'zs'}
function foo(data) {
console.log(this, data)
}
// 传入参数的形式
foo(11) // Window 11
foo.call(obj, 11) // {name: 'zs'} 11
foo.apply(obj, [11]) // {name: 'zs'} 11
foo.bind(obj) // 没有输出
foo.bind(obj, 11)() // {name: 'zs'} 11
// 使用
setInterval(function () {
console.log(this) // {name: 'zs'}
}.bind(obj), 1000)
ES6
let / const 关键字
let
作用
- 与 var 类似, 用于声明一个变量
特点
- 在 块级作用域 内有效
- 不能重复声明
不会预处理, 不存在提升
应用
- 循环遍历加监听
- let 取代 var 是趋势
1
2
3
4
5for (let i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
alert(i) // 0 1 2, let 有块级作用域
}
}
const
作用
- 定义一个
常量
- 定义一个
特点
- 不能修改
- 其他特点同 let
应用
- 保存不用改变的数据
1
const URL = '/login.html'
- 保存不用改变的数据
变量的解构赋值
理解 —— 从对象或数组中提取数据, 并赋值给多个变量
- 用途
- 给
多个
形参赋值
- 给
对象的解构赋值
要被赋值的变量名必须是 对象中已有的属性名
1 | let { name, age, xxx } = { name: 'zs', age: 18 } |
数组的解构赋值
要被赋值的变量是 按顺序赋值 的
1 | let [a, b] = [1, 5, 9] |
模版字符串
作用 —— 简化字符串的拼接
- 模版字符串必须用 ``包含
- 变化的部分使用 ${xxx} 定义
1
2
3let obj = { name: 'zs', age:18 }
let str = `我叫: ${obj.name}, 今年${obj.age}岁`
console.log(str)
简化的对象写法
- 与属性名相同的属性值可以省略不写
- 可以省略方法里的 :function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16let name = 'zs'
let age = 18
let obj = {
name: name,
age: age,
getName: function () {
return this.name
}
let obj = {
name,
age,
getName() {
return this.name
}
}
箭头函数
作用 —— 定义匿名函数, 回调函数中常用
基本语法
没有参数的情况下, 小括号不能省
1
() => console.log('xx')
一个参数的情况下, 小括号能省
1
i => i+2
大于一个参数的情况下, 小括号不能省
1
(i, j) => i + j
函数体只有一条语句, 可以不用大括号,
默认返回该条语句的执行结果或 undefined
函数体如果有多条语句, 需要用 {}; 如果有需要返回的内容, 需要
手动返回
1
2
3
4
5
6
7
8let fun = (x, y) => x + y
console.log(fun(20, 30)) // 50
let fun = (x, y) => { x + y }
console.log(fun(20, 30)) // undefined
let fun = (x, y) => { return x + y }
console.log(fun(20, 30)) // 50
使用场景
- 多用于定义回调函数
特点
- 简洁
- 箭头函数没有自己的 this, 箭头函数的 this 不是调用的时候决定的,
而是在定义的时候处在的位置处的对象
就是它的 this
箭头函数的 this
箭头函数的 this 看 其所处的外层函数
- 如果有, 外层函数的 this 就是箭头函数的 this
- 如果没有, 则箭头函数的 this 是 window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23btn.onclick = () => {
console.log(this) // window
}
let obj = {
name: 'zs',
fn: function () {
btn.onclick = () => {
console.log(this)
}
}
}
obj.fn() // obj
let obj = {
name: 'zs',
fn: () => {
btn.onclick = () => {
console.log(this)
}
}
}
obj.fn() // Window
… 运算符
作为参数使用
- rest(可变)参数
- 用来取代 arguments, 但比 arguments 灵活, …rest 只能放在实参最后
- 定义的时候要加 … , 使用的时候直接使用 rest
1
2
3
4function fun(a, ...rest) {
console.log(rest)
}
fun(1, 2, 3) // [2, 3] 拿到的是真数组, 可以使用数组的方法
作为扩展运算符使用
- 扩展运算符
- 可以解构数组
- 也可以解构伪数组
1
2
3
4
5
6
7
8
9
10
11
12let arr = [1, 6]
let arr1 = [2, 3, 4, 5]
arr = [1, ...arr1, 6]
console.log(arr) // [1, 2, 3, 4, 5, 6]
[...divs] // 伪数组转数组
let obj = { name: 'zs' }
let obj2 = { ...obj }
console.log(obj2) // { name: 'zs' }
// 直接 ...obj 会报错,...arr 不会报错
形参默认值
当不传参时, 默认使用形参里的默认值
1 | function Point(x = 1, y = 2) { |
Module 的语法
历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成 互相依赖
的小文件,再用简单的方法拼装起来
ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案
export 命令
模块功能主要由两个命令构成:
export
和import
export
命令用于向外暴露接口,import
命令用于引入其他模块提供的功能
一个模块就是一个独立的文件
- 该文件内部的所有变量,外部无法获取
- 如果你希望外部能够读取模块内部的某个变量,就必须使用
export
关键字输出该变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15export var firstName = 'Michael'
export var lastName = 'Jackson'
export var year = 1958
// 或者
var firstName = 'Michael'
var lastName = 'Jackson'
var year = 1958
export { firstName, lastName, year }
// 对外暴露一个函数
export function multiply(x, y) {
return x * y
}在
export
命令后面,使用大括号指定所要输出的一组变量或函数或类通常情况下,
export
输出的变量就是本来的名字,但是可以使用as
关键字重命名1
2
3
4
5
6
7
8function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
}
⚠️ export
命令规定的是对外的接口,必须与模块 (js 文件) 内部的变量建立一一对应关系
export 向外暴露成员只能有两种写法
写法一
1
export var m = 1
写法二
1
2var m = 1
export { m }
export 向外暴露的成员可以 整体加载 (import * as xxx from ‘./module’)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
// main.js
import { area, circumference } from './circle'
console.log('圆面积:' + area(4))
console.log('圆周长:' + circumference(14))
// 整体加载
import * as circle from './circle'
console.log('圆面积:' + circle.area(4))
console.log('圆周长:' + circle.circumference(14))
export default 命令
export default
命令,为模块指定默认输出- 其他模块加载该模块时,
import
命令可以为其指定任意名字; 这时import
命令后面,不使用大括号
1
2
3
4
5
6
7
8// export-default.js
export default function () {
console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'- 其他模块加载该模块时,
export default
命令用在非匿名函数前,也是可以的1
2
3
4
5
6
7
8
9
10
11
12// export-default.js
export default function foo() {
console.log('foo');
}
// 或者写成
function foo() {
console.log('foo');
}
export default foo;本质上,
export default
就是输出一个叫做default
的变量或方法,然后系统 允许你为它取任意名字export default
也可以用来输出类1
2
3
4
5
6// MyClass.js
export default class { ... }
// main.js
import MyClass from 'MyClass';
let o = new MyClass();
比较 export 和 export default
- 一个 js 模块只能有一个 export default 命令, 却可以有多个 export 命令
- export 和 export default 可以共存
- export 和 export default 在导出
命名函数、命名类
时写法是一样的 - export 向外暴露的成员必须带有名字,
export
命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系 - export 向外暴露的成员在 import 时必须加大括号; export default 向外暴露的成员在 import 时不加大括号
1 | export default function crc32() { // 输出 |
import
使用export
或 export default
命令定义了模块的对外接口以后,其他 JS 文件就可以通过import
命令加载这个模块
1 | // main.js |
import
命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js
)对外接口的名称相同如果想为输入的变量重新取一个名字,
import
命令要使用as
关键字,将输入的变量重命名1
import { lastName as surname } from './profile.js'
import
命令具有提升效果
,会提升到整个模块的头部,首先执行1
2
3foo()
import { foo } from 'my_module'
⚠️
- 如果多次重复执行同一句
import
语句,那么只会执行一次,而不会执行多次import
命令导入的变量都是只读的,因为它的本质是引入接口- import 命令可以引入静态资源文件
1 | // 1. 直接引入自定义的静态文件 |
Promise 对象
理解
Promise 对象代表了未来某个将要发生的事件 (通常是一个
异步操作
)有了 promise 对象, 可以将异步操作以同步的流程表达出来, 避免了 “回调地狱” 问题
ES6 的 Promise 是一个构造函数, 用来生成 promise 实例
promise 一旦创建, 括号内的函数就立即调用
- 如果不想让它立即执行, 应该把它放到一个函数中
promise 对象上有两个函数 resolve 和 reject
- resolve 和 reject 从哪来?
在 new 出来的 promise 实例上调用 .then() , 预先为这个 promise 异步操作指定成功的回调 resolve 和失败的回调 reject
- promise 对象的 resolve() 方法只能接收一个参数,如果需要传递多个参数可以传递一个对象
- resolve 和 reject 从哪来?
promise 构造函数的 prototype 上有 then 方法
promise 对象的 then()
then() 之后可以链式调用 then()
后续的每一个 then 中指定的回调处理函数都会被执行
后续的每一个 then 中指定的回调处理函数可以接收上一个 then 中第一个函数的返回值
如果上一个 then 中第一个函数没有指定返回值,后续的 then 接收的就是
undefined
如果上一个 then 中第一个函数指定返回值是普通数据类型,后续的 then 接收的就是该普通类型的返回值
如果上一个 then 中第一个函数指定返回值是 promise 对象,后续的 then 的第一个参数就是该 promise 对象的 resolve() 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34new Promise((resolve, reject) => {
fs.readFile('a.txt', 'utf8', (err, data) => {
if (err) {
return reject(err)
}
resolve(data)
})
})
.then((data) => {
console.log(data) // a.txt 的内容
})
.then((data) => {
console.log(data) // undefined, 上一个 then() 没有指定返回值
})
// 返回普通数据
......
.then((data) => {
console.log(data) // a.txt 的内容
return 123
})
.then((data) => {
console.log(data) // 123, 上一个 then() 的返回值
})
// 返回 promise 对象
......
.then((data) => {
console.log(data) // a.txt 的内容
return promise对象
})
.then((data) => {
console.log(data) // 上一个 then() 返回的 promise 对象的 resole(data) 中的 data
})
promise 应用场景
使用 promise 基本步骤
- 将异步操作封装成 promise 对象
异步操作成功, 调用 resolve()
操作失败, 调用reject() - promise 实例调用 then (function () {}[, function () {}])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44// 应用场景一
// 按顺序读多个文件
var fs = require('fs')
function pReadFile(filePath) {
return new Promise(function(resolve, reject) {
fs.readFile(filePath, 'utf8', function(err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}
pReadFile('./data/a.txt')
.then(function(data) { // 通过 .then 指定回调的时候, 可以只传成功的回调
console.log(data)
return pReadFile('./data/b.txt') // 返回一个新的 promsie 实例, 可以继续用 .then 来处理
// 如果返回一个普通值,就直接将这个普通值传递给后续的 then
})
.then(function(data) {
console.log(data)
return pReadFile('./data/c.txt')
})
.then(function(data) {
console.log(data)
})
// 应用场景二
// 请求多个借口才能拿到综合的数据
var data = {}
$.get(借口地址1)
.then(function (userData) {
data.user = userData
return $.get(借口地址2)
})
.then(function (jobsData) {
data.jobs = jobsData
var htmlStr = template('tpl', data)
$('form').html(htmlStr)
})
promise 对象的三个状态
pending —— 初始化状态
fullfilled —— 成功状态
rejected —— 失败状态
应用
- 使用 promise 实现超时处理
- 使用 promise 封装 ajax 请求
⚠️
- promise 本身不是异步, 但其内部通常封装一个异步操作
promise 替代回调
只需要把之前传给回调函数的异步结果传给 promise 对象的 resolve 函数就可以了
1 | // 回调 |
promise 捕获异常
方式一
- 手动指定回调, 就是定义 .then 的第二个参数(失败的回调)
- 并且在失败的回调里也 return 新的 promise ,
以保证前边的执行失败不影响后边的执行
方式二
如果后边的 promise 的执行依赖前面 promise 的结果; 前边的执行失败, 应该终止后边的执行
在最后面 .catch
⚠️ 如果前边某个操作添加了单独的 reject(), 则该操作的错误不会进入 catch()
1
2
3.catch(err => { // 如果前面有任何的错误发生, 立即终止所有 then() 的执行, 进入 catch 的执行
console.log(err.message)
})
jquery 中的 ajax 天生支持 promise
$.get(url) 默认返回一个 promise 对象, 就是说可以调用 .then() 的方式执行后续操作
1 | $.get(url) |
1 | $.ajax({ |
async 函数
真正意义上去解决 异步回调
的问题 —— 同步流程表达异步操作
简化 promise 的操作 —— 不再需要去调用.then() 来指定成功/失败的回调
本质
- Generator 的语法糖
语法
1
2
3
4async function foo() {
await 异步操作
await 异步操作
}返回值
- async 函数的返回值是一个新的 promsie 对象,promsie 成功回调的结果由 async 的返回值决定
特点
- 不需要像 Generator 去调用 next 方法, 遇到 await 等待, 当前的异步操作完成就往下执行
- async 取代 Generator 函数的 * , await 取代 Generator 的 yield
- async 函数通常返回的总是 promise 对象, 可以调用 then 方法进行下一步操作
- 语义上更为明确, 使用简单
- await 后边如果是普通函数, 得到的就是普通函数的返回值
- await 后边如果是 promise 对象, 得到的就是 promise 对象的 resolve 函数被调用时所传递的参数
- 如果异步请求失败, 会报错
可以通过 try catch 来捕获异常
- await 操作写在 try 里边,捕获异常写在 catch(err) 里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function getNews(url) {
return new Promise((reslove, reject) => {
$.ajax({
type: 'get',
url,
success: data => reslove(data),
error: error => reject(error)
// 如果前边的请求出错了,下边的 result 没有值, 如何提示用户?
// 解决办法 —— error: error => resolve(false)
})
})
}
async function send() {
// await 后边通常都是跟一个 promise 对象
let result = await getNews('http://localhost:3000/')
// 请求出错的提示
// if (!result) { alert('暂时没有数据') return }
console.log(result)
result = await getNews('http://localhost:3000/' + result.commentUrl)
console.log(result)
}
send()
多个请求(await)的情况
如果在 async 函数中有多个请求,需要写多个 await ,这样后边的 await 会等待前面的执行完才执行
造成等待时间过长
解决 —— Promise.all([promise1, promise2….])
1
2
3
4
5
6
7
8async componentDidMount() {
// Promsie.all() 接收一个数组,数组的元素是每一个 pormise 对象
// 返回一个数组,数组的每个元素是对应的 请求的返回结果
const results = Promise.all([reqCategory(pCategroryId), reqCategory(categoryId)])
const cName1 = results[0].data.name
const cName2 = results[1].data.name
......
}
总结 Promise 和 async 函数
基本操作都是 先将异步操作封装成一个返回值为 promise 对象的函数
- 然后
- Promise 方式是使用封装函数得到的 promsie 实例直接去 调用 then() 方法
- then() 的第一个参数为 promise 对象的 resolve 方法,第二个参数为 reject 方法
- async 函数是 在调用封装的函数前加上 await
- await 封装函数的返回值为 promise 对象的 resolve 方法的参数
- Promise 方式是使用封装函数得到的 promsie 实例直接去 调用 then() 方法
Symbol
ES5 中对象的属性名都是字符串, 容易造成重名, 污染环境
ES6 中添加了一种
原始数据类型 symbol
特点
- symbol 属性对应的值是唯一的, 解决命名冲突问题
- symbol 值不能与其他数据进行运算, 包括同字符串拼接
- for in for of 遍历时不会遍历 symbol 属性
使用
- 调用 Symbol() 函数得到 symbol 值 (每次得到的值都不一样)
1
2
3
4let symbol = Symbol()
let obj = {}
obj[symbol] = 'hello' // 不能 obj.symbol
console.log(obj) // { Symbol(): 'hello' }
- 调用 Symbol() 函数得到 symbol 值 (每次得到的值都不一样)
传参标识
1
2
3let symbol = Symbol('one')
let symbo2 = Symbol('two')
console.log(symbol, symbo2) // Symbol(one) Symbol(two)内置 Symbol 值
- 除了自定义的 symbol 值以外, ES6 还提供了 11 个内置的 symbol 值
- Symbol.iterator
- 对象的 Symbol.iterator 属性, 指向该对象的默认遍历器方法
Iterator 遍历器
iterator 是一种接口机制, 为各自不同的数据结构提供统一的访问机制
作用
- 为各种数据结构, 提供一个统一的、简便的访问接口
- 是的数据结构的成员可以按某种次数排列
- ES6 创造一种新的遍历, for of 循环, Iterator 接口主要供 for of 消费
工作原理
- 创建一个指针对象 (遍历器对象), 指向数据结构的起始位置
- 第一次调用 next() 方法, 指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法, 指针会一直往后移动, 直到指向最后一个成员
- 每次调用 next 方法返回的是一个包含 value 和 done 的对象, { value: 当前成员的值, done:布尔值}
value 标售当前成员的值, done 对应的布尔值表示当前的数据的结构是否遍历结束
当遍历结束的时候返回 value 的值是 undefined, done 值为 true
原生具备 iterator 接口的数据 (
可用 for of 遍历
)当数据结构上都部署了 Symbol, iterator 接口, 该数据就是可以用 for of 遍历
数组、字符串、arguments、set 容器、map 容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 遍历数组
for (let i of arr) {
console.log(i)
}
// 遍历字符串
for (let i of str) {
console.log(i)
}
// 遍历 arguments
function fun() {
for (let i of arguments) {
console.log(i)
}
}
fun(1, 4, 9)⚠️ 对象不能用 for of 遍历
Generator 函数
- ES6 提供的解决异步编程的方案之一
- Generator 函数是一个状态机, 内部封装了不同状态的数据
- 用来生成遍历器对象
- 可暂停函数(惰性请求), yield 可暂停, next 方法可启动; 每次返回的是 yield 后的表达式的结果
- 特点
- function 与函数名之间有一个星号
- 内部用 yield 表达式来定义不同的状态
- generator 函数返回的是指针对象, 而不会执行函数内部的逻辑
- 调用 next 方法函数内部逻辑开始执行, 遇到 yield 表达式停止, 返回 { value: yield 后的表达式的结果 / undefined, done:}
- 再次调用 next 方法会从上一次停止时的 yield 处开始, 直到最后
class 类
通过 class 定义类, 实现类的继承
- 在每个 class 内部,
都
有一个 constructor 构造器, 如果没有显示定义, 那么类内部默认有个看不见的 constructor - constructor 的作用
- 好比之前的 function Person() {} 构造函数
- 每当通过 new 来创建类的实例, 必然会调用 constructor()
- class 作用域里边,
只能写方法和静态属性(static)
- 静态属性和方法挂载在
构造器
上, 一般的方法挂载在类实例的原型
上
- 静态属性和方法挂载在
- 通过
extends
来实现类的继承, 不仅可以继承属性, 还可以继承父类的原型方法- 继承的本质是 —— 原型式继承(子类的原型指向了父类的原型)
- 通过
super
调用父类的构造方法- 如果在子类中没有写 constructor(), 不用调
- 如果写了 constructor(), 必须调用 super()
- 可以在子类中重写从父类继承的一般方法
class 类内部的语法
class 作用域里只能写静态属性和方法, 没有其他语句
声明一般方法只能是以下两种方式
1
2
3
4
5
6
7
8
9
10
11
12
13class Person {
...
// 方式1
show() {
console.log(this.name, this.age)
}
// 方式2
show = () => {
console.log(this.name, this.age)
}
...
}每个 } 之后 没有逗号
注意与对象语法的区分
1 | class Person { |
super
super
这个关键字,既可以当作函数使用,也可以当作对象使用
作为函数时
- 代表父类的构造函数
- 调用 super 相当于调用父类的构造函数
- 但 super 内部的 this 指向的是子类的实例,即
super()在这里相当于
A.prototype.constructor.call(this)
- 但 super 内部的 this 指向的是子类的实例,即
- 作为函数时,
super()
只能用在子类的构造函数之中
作为对象时
在普通方法中,指向父类的原型对象;在静态方法中,指向父类
在子类普通方法中通过
super
调用父类的方法时,方法内部的this
指向当前的子类实例如果通过
super
对某个属性赋值,这时super
就是this
,赋值的属性会变成子类实例的属性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
// super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined
其他扩展
字符串扩展
- includes(str)
- 判断是否包含指定的字符串, 返回 true / false
- startsWith(str)
- 判断是否以指定的字符串开头, 返回 true / false
- endsWith(str)
- 判断是否以指定的字符串结尾, 返回 true / false
- repeat(count)
- 重复指定次数, 返回重复 几次 后的字符串
数值扩展
二进制用
0b
开头, 八进制用0o
开头1
console.log(0b1010) // 10
Number.isFinite(i)
- 判断是否是有限大的数
Number.isNaN(i)
- 判断是否是 NaN
Number.isInteger(i)
- 判断是否是整数
Number.parseInt(str)
- 将字符串转换为对应的数值
Math.trunc(i)
- 直接去除小数部分
数组方法的扩展
Array.from(v)
将伪数组对象或可遍历的对象转换为真数组
1
2
3
4let btns = document.getElementsByTagName('button')
Array.from(btns).forEach(function (item, index) {
console.log(item)
})
Array.of(v1, v2, v3)
将一系列值转换为数组
1
2let arr = Array.of(1, 2, 6)
console.log(arr) // [1, 2, 6]
find(function (item, index) { return true })
找出第一个满足条件的值
1
2
3let result = arr.find(function (item, index) {
return item > 4
})
findIndex(function (item, index) { return true })
找出第一个满足条件的值的索引
1
2
3let resultIndex = arr.findIndex(function (item, index) {
return item > 4
})
对象扩展
Object.is(v1, v2)
判断两个数据是否完全相等 (底层比较的是字符串)
1
2
3
4
5
6
7// 默认关系运算符会将其转为 number 进行比较(两个字符串比较除外)
console.log(0 == -0) // true
console.log(NaN == NaN) // false
// 将其转为字符串再比较
console.log(Object.is(0, -0)) // false
console.log(Object.is(NaN, NaN)) // true
Object.assign(target, source1, source2)
将源对象的属性复制到目标对象上
1
2
3
4
5let obj = {}
let obj1 = { name: 'sz' }
let obj2 = { sex: '女' }
Object.assign(obj, obj1, obj2)
console.log(obj) // { name: 'sz', sex: '女' }
直接操作 __ proto__ 属性
1
2
3
4
5var obj2 = {}
var obj = { money: 1000 }
obj2.__proto__ = obj
// obj2 可以访问 obj 上的所有属性, 打印自身是空, 但是可以通过原型链拿到 obj 上的属性
console.log(obj2, obj2.money) // {} 1000
set 和 map 容器
set 容器
- 无序
不可重复
的多个 value 的集合体 (就是要传个数组) - Set()
- Set(array)
- set 实例的方法
- add(value)
- delete(value)
- has(value)
- clear()
- size
- 无序
map 容器
无序的
key 不重复
的多个 key-value 的集合体 (数组内嵌套数组)Map()
Map(array)
Map 实例的方法
- set(key, value)
- get(key)
- delete(key)
- has(key)
- clear()
- size
1
2
3
4
5
6
7
8
9
10
11let set = new Set([1, 2, 3, 4, 2]) // 传递的是数组
console.log(set.size, set) // 4 Set(4) {1, 2, 3, 4}
set.add(7)
console.log(set.size) // 5
console.log(set.has(8)) // false
let map = new Map([['a', 'b'],['age', 36]])
console.log(map) // Map(2) {'a' => 'b', 'age' => 36}
map.set('name', 'zs')
console.log(map) // Map(2) {'a' => 'b', 'age' => 36, 'name' => 'zs'}
map.delete('age')
for of 循环遍历
- 遍历数组
- 遍历 Set
- 遍历 Map
- 遍历字符串
- 遍历伪数组
- 不能遍历对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14let set = new Set([1, 2, 3, 4])
for (let i of set) {
console.log(i)
}
// 数组去重
let arr = [1, 2, 5, 5, 6, 1]
let arr1 = []
let set = new Set(arr)
for (let i of set) {
arr1.push(i)
}
arr = arr1
ES7
指数运算符(幂)
- **
1
console.log(3 ** 3) // 27
- **
Array.prototype.includes(value)
- 判断数组里是否包含指定的 value
1
2let arr = [1, 2, 3]
console.log(arr.includes(2)) // true
- 判断数组里是否包含指定的 value