课程资源:链接:https://pan.baidu.com/s/1YChjK2-p9rkl0Veds6nMNw 提取码:897y
笔记PDF版本:链接:https://pan.baidu.com/s/1dzTkWnpGXeofYjvJ4sUUpg 提取码:a6rl
文章目录
1 课程介绍与环境搭建
1-1 课程介绍
配套电子书地址:http://es.xiecheng.live/introduction/preface.html
1-2 Node安装与NPM源切换
1、Node环境安装:
下载地址:https://nodejs.org/zh-cn/ (下载长期支持版本LTS)
安装完成,在终端输入:node -v
查看版本号
2、NPM:Node Package Manager
查看版本:npm -
3、NRM(Npm Registry Manager )是npm的镜像源管理工具源切换,找到当前最快的安装源
安装:npm install -g nrm
NRM安装不成功
如果安装nrm出现下面信息,安装失败,解决办法参考:https://blog.csdn.net/blue_698/article/details/117874021
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gcXs54pK-1650374741706)(C:\Users\联想\AppData\Roaming\Typora\typora-user-images\image-20220418121123274.png)]
-
执行:
npm config list -l
,出现下面结果 -
执行
npm cache clean --force
清除缓存 -
执行
nrm ls
出现下面结果即解决
NRM常用命令
测试源的速度:nrm test npm
切换源:nrm use taobao
查看可用源:nrm ls
查看当前源:nrm current
增加定制源:nrm add imooc http://192.168.1.100:6666
删除源:nrm del imooc
1-3 构建开发环境
1、构建步骤
安装:npm install -g imooc-es-cli
或者 npm i -g imooc-es-cli
查看版本号:imooc-es-cli --version
进入项目建立文件夹:cd 文件夹位置
初始化项目:imooc-es-li init
(注意:此处需要输入项目名称:imooc-es-demo,一定要是英文的)
进入项目:cd imooc-es-demo
安装当前项目所依赖的包:npm install
启动项目:npm run start
访问地址:http://localhost:8080/出现下面结果即成功
2、项目结构说明
用webstorm打开项目,查看项目结构
-
build:webpack相关的配置文件
-
node_modules:当前项目所依赖的包,即前面
npm install
所下载的包 -
static:不会被webpack打包编译,而是直接复制到生产环境当中
-
package-lock.json package.json:当前项目包所需要的一些配置文件
-
src项目文件
在浏览器界面,F12进入编辑,查看source下的index.js,看到eval("var sum = function sum(x, y) {\n return x + y;\n};
说明webpack打包编译成功
2 ECMAScript2015(ES6)之必备知识
2-1 新的声明方式let & const
- 不属于顶层对象window
- 不允许重复声明
- 不存在变量提升
- 暂时性死区
- 块级作用域
作用域
对象 | 类型 |
---|---|
global/window | 全局作用域 |
function | 函数作用域(局部作用域) |
{} | 块状作用域 |
this | 动态作用域 |
可以直接使用webstorm自带的终端启动运行项目,启动后,在static文件夹下创建js文件,并在src中的index.html中引入js文件,<script src="static/2-1.js"></script>
写好代码之后,新建一个终端,输入npm run build
使项目打包重新上线,项目结构中会出现dist文件夹。(注意:src下的文件会被webpack打包编译,而static会被直接复制过去)
let
// delete 删除对象的属性,并不能删除变量
// var a = 5//变量
// b = 6//隐藏的对象
// //早期,js将顶层对象属性和全局变量挂钩,污染了全局变量
// console.log(window.a)
// console.log(window.b)
// delete a //删除失败
// delete b //删除成功
// console.log(a)
// console.log(b) //报错undefined
// 1.不属于顶层对象window
// let声明的变量不可以通过window.变量名 的方式访问,而var声明的全局变量是window的属性,是可以通过window.变量名的方式访问
// let a = 5
// console.log(a)
// console.log(window.a) //undefined
//2.不能重复声明
// var可以重复定义,使用let不可以
// let a = 5
// let a = 6 //报错:Uncaught SyntaxError: Identifier 'a' has already been declared
// 3.不能变量提升
// let声明的变量必须先定义,后使用,否则会报错
// console.log(a) //2-1-let.js:22 Uncaught ReferenceError: Cannot access 'a' before initialization
// let a = 5
//4.暂时性死区:防止在变量声明之前使用变量,使代码更加安全
// 只要块级作用域内存在let命令,它所声明的变量就绑定在了这个区域,不再受外部的影响
// 在代码块内,使用let命令声明变量之前,该变量都是不可用的
// var a = 5
// if (true) {
// a = 6 //报错,在{}作用域内,没有声明就使用
// let a
// }
// 较隐蔽的暂时性死区
// function foo(a = b, b = 2) {
// console.log(a, b) //报错:b在没有声明的情况下就使用了
// }
// foo()
//5.块级作用域
// for (let i = 0; i < 3; i++) {
// console.log('循环内:' + i)
// }
// console.log('循环内:' + i) //报错,let有块级作用域,此处i没有声明
// if (true) let a = 5 //Lexical declaration cannot appear in a single-statement context
// let有块级作用域,必须写在{}内
//6.面试题
// for (var i = 0; i < 3; i ++) {
// setTimeout(function () {
// console.log(i) //3个3
// }, 100)
// }
// setTimeout异步执行,js有事件循环机制,会等到主线程执行完成,异步操作才开始执行
// for循环是同步操作,setTimeout会等for循环执行完毕才开始执行,此时i=3
// 解决办法:
// for (var i = 0; i < 3; i ++) {
// //1)使用闭包
// (function (j) {
// setTimeout(function () {
// console.log(j) //0 1 2
// }, 100)
// })(i)
// }
//闭包特点:有一个外部函数,一个内部函数,内部函数会调用外部函数传来的变量,使得外部函数的变量不被释放
// 2)使用let
for (let i = 0; i < 3; i ++) {
setTimeout(function () {
console.log(i) //0 1 2
}, 100)
}
const
// es5定义一个常量
// Object.defineProperty(window, 'PI', {
// value: 3.14,
// writable: false // writable:当前变量是否可写
// })
// console.log(PI)
// PI = 5
// console.log(PI) //3.14 变量值不可修改
// es6
// 1.const修饰的变量不能被修改
// const a = 5
// a = 6 //报错
//2.const变量只能在定义的时候赋值
// const a
// a = 5 //报错
//3.const作用域:块级作用域
//4.const存在暂时性死区
// 5.重点
// 基本数据类型存储在 栈 内存中,引用数据类型存储在 堆 内存中(在栈内存中只保存引用地址,实际的值是在堆内存中)
// const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动
const obj = {
name: 'mys',
age: 18,
skill: {
name: 'code',
year: 4
}
}
console.log(obj)
obj.school = 'imooc' // 成功 const变量指向地址的不变,内容如果是数组或对象可以改变,常量不能改变
console.log(obj)
Object.freeze(obj) // 冻结,freeze只能传递对象,不能传数组
obj.skill.year = 5 // 修改成功,因为freeze只是浅层的冻结
console.log(obj)
// 如果需要深层的冻结,需要:Object.freeze(obj.skill)
//如果变量在后续的业务逻辑中不会被改变,即可以用const,否则用let定义
2-2 解构赋值
解构赋值:允许按照一定模式,从数组和对象中提取值,对变量进行赋值
- 数组解构
- 对象解构
- 字符串解构
最常用:Object 和 Array
// 1.数组解构赋值
// let [a, b, [c, d]] = [1, 2, [3, 4]]
// console.log(a, b, c, d) // 1 2 3 4
// let [a, b, [c]] = [1, 2, [3, 4]]
// console.log(a, b, c) // 1 2 3
// let [a, b, c] = [1, 2, [3, 4]]
// console.log(a, b, c) // 1 2 [3, 4]
// let [a, b, c, d] = [1, 2, [3, 4]]
// console.log(a, b, c, d) // 1 2 [3, 4] undefined
// 数组是通过下标去对应的,是惰性的
//2.对象解构赋值 (常用)
// let user = {
// name: 'mys',
// age: 18
// }
// let {age: uage, name: uname} = user // uname uage使用别名
// console.log(uname, uage) // mys 18
//对象是通过key名称去对应的,不是顺序
// 3. 字符串解构赋值(类似于数组解构赋值)
// let str = 'imooc'
// let [a, b, c, d, e] = str
// console.log(a, b, c, d, e)
// 4.默认值
// 1)数组
// let [a, b, c = 8] = [4, 5]
// console.log(a, b, c) // 4 5 8
// 2)对象
// let {name, age = 18} = {
// name: 'mys',
// // age: 20
// }
// console.log(name, age)
// 3)函数
// function foo() {
// console.log(123)
// }
// // let [a = foo()] = [1]
// // console.log(a) //1
// let [a = foo()] = []
// console.log(a) //123 undefined
// 5.应用
// 1)函数参数解构赋值
// function foo([a, b, c]) {
// console.log(a, b, c) //1 2 3
// }
// let arr = [1, 2, 3]
// foo(arr)
// 2)函数参数是对象
// function foo({name, age, school = 'imooc'}) {
// console.log(name, age, school) //mys 18 xxx
// }
// let obj = {
// name: 'mys',
// age: 18,
// school: 'xxx'
// }
// foo(obj)
// 3)返回值解构
// function foo() {
// let obj = {
// name: 'mys',
// age: 18,
// school: 'xxx'
// }
// return obj
// }
//
// let {name, age} = foo()
// console.log(name, age)
// 4)提取json数据
let json = '{"a": "hello", "b": "world"}'
let {a, b} = JSON.parse(json)
console.log(a, b)
2-3 数组的各种遍历方式
let arr = [1, 2, 3]
// es5
// 1.for
// for (let i = 0; i < arr.length; i ++) {
// console.log(i)
// }
// 2.forEach
/*
参数:(遍历的数组内容,对应的数组索引,数组本身)
语法:[ ].forEach(function(value,index,array){
});
没有返回值
*/
// arr.forEach(function (elem, index, array) {
// console.log(elem, index)
// })
// forEach不支持break continue
// 3.map
/*
语法:arr.map(function(currentValue,index,arr),thisValue)
参数:currentValue 必须 当前元素值
index 可选 当前元素的索引值
arr 可选 当前元素属于的数组对象
返回:新数组
*/
// let res = arr.map(function (value) {
// value += 1
// return value
// })
// console.log('res:', res) // [2, 3, 4]
// console.log('array:', arr) // [1, 2, 3]
// map不会改变原有数组
//4.filter
/*
语法:filter(function(item,index){
return func(item);
}):
返回:新数组
*/
// let res = arr.filter(function (value) {
// return value === 2
// })
// console.log(arr) // [1, 2, 3]
// console.log(res) // 2
// 5.some
// 遍历数组中是否有符合条件的值,返回结果是Boolean类型的
// let res = arr.some(function (value) {
// return value === 2
// })
// console.log(arr) // [1, 2, 3]
// console.log(res) // true
// 6.ever:每一个都符合,返回true
// let res = arr.every(function (value) {
// return value === 2
// })
// console.log(arr) // [1, 2, 3]
// console.log(res) // false
// every 的代码块中不能使用 break、continue
// 7.reduce
/*
reduce() 方法接收一个函数作为累加器
语法:array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
参数:total 上一次回调函数的返回值
currentValue 当前元素值
currentIndex 当前索引
arr 调用 reduce 的数组
initialValue 初始值
*/
// 1)求数组每项的和
// let sum = arr.reduce(function (prev, cur, index, arr) {
// return prev + cur
// }, 0) //第二个参数:初始值
// console.log(sum) //6
// 2)求数组中最大值
// let max = arr.reduce(function (prev, cur) {
// return Math.max(prev, cur)
// })
// console.log(max)
// 3)去重
// arr1 = [1, 2, 2, 3, 3]
// let res = arr1.reduce(function (prev, cur) {
// // prev.indexOf(cur):上一个数组中是否包含当前值
// // prev.push(cur):如果不包含就放入当前元素
// prev.indexOf(cur) === -1 && prev.push(cur)
// return prev
// }, []) // 初始值为一个新的数组
// console.log(res) //1 2 3
// 8.for in (不使用)
// 如果给原型定义了函数,遍历数组时函数也会输出
// Array.prototype.foo = function () {
// console.log('foo')
// }
// for (let index in arr) {
// console.log(index, arr[index])
// }
// es6
// 9.find:返回第一个通过测试的值
// arr1 = [1, 2, 2, 3, 3]
// let res = arr1.find(function (value) {
// return value === 2
// })
// console.log('res', res) // 2
// console.log('arr', arr1) // [1, 2, 2, 3, 3]
// 10.findIndex:返回第一个通过测试的值的下标(从0开始)
// let res = arr1.findIndex(function (value) {
// return value === 2
// })
// console.log('res', res)
// console.log('arr', arr)
// 11.for of(常用)
// 1)获取值 arr/arr.values()
// // for (let item of arr.values()) 效果一样
// for (let item of arr) {
// console.log(item) // 1 2 3
// }
//2)获取当前的下标:arr.keys()
// for (let item of arr.keys()) {
// console.log(item) // 0 1 2
// }
//3)获取下标和值:arr.entries() [index, item]
for (let [index, item] of arr.entries()) {
console.log(index, item)
}
2-4 数组的扩展
- 类数组/伪数组:具有length属性;按照索引方式存储数据;不具有数组的方法,比如push() pop()
- Array.from():类数组/伪数组 => 数组
- Array.of()
- CopyWithin()
- fill()
- includes()
// 1. DOM
// let divs = document.getElementsByTagName('div');
// console.log(divs) //HTMLCollection
//
// let divs2 = document.getElementsByClassName('xxx');
// console.log(divs2) //HTMLCollection
// let divs3 = document.querySelectorAll('.xxx');
// console.log(divs3) //NodeList
// console.log(divs3 instanceof Array) //false
// 2.类数组、伪数组 =>数组
// 1)slice es5 : Array.prototype.slice.call() 把调用方法的参数取出来
// let arr = Array.prototype.slice.call(divs3);
// console.log(arr) // []
// arr.push(123)
// console.log(arr) // [123]
// function foo() {
// console.log(arguments instanceof Array) //false 说明arguments也不是一个真正的数组
// }
// foo(1, 'imooc', true)
//2)es6 Array.from()
/*
语法:Array.from(arrayLike[, mapFn[, thisArg]])
参数: arrayLike 必选,想要转换成数组的伪数组对象或可迭代对象
mapFn 可选,如果指定了该参数,新数组中的每个元素会执行该回调函数
thisArg 可选,执行回调函数 mapFn 时 this 对象
*/
// let arrayLike = {
// 0: 'es6',
// 1: 'es7',
// 2: 'es8',
// length: 3
// }
// let arr = Array.from(arrayLike)
// arr.push('es9')
// console.log(arr) //['es6', 'es7', 'es8', 'es9']
// 3.构造数组
// 1)通过new Array()构造数组时,参数数量不同,表现得结果不同
// 如果只传一个参数,表示的是数组的长度;如果传多个参数,表述的是数组的内容
// let arr = new Array(1, 2)
// let arr2 = new Array(3)
// console.log(arr, arr2)
// 2)Array.of()构造数组,参数都是数组的值
// let arr = Array.of(3)
// console.log(arr)
// Array.of()可以将不同类型的数据拼装成一个数组
// let arr = Array.of(1, true, 'imooc', [1, 2, 3], {name: 'mys'})
// console.log(arr) //[1, true, 'imooc', Array(3), {…}]
// 4. copyWithin() 应用:替换元素
/*
从数组的 指定位置 拷贝元素到数组的另一个指定位置
语法:copyWithin(target, start = 0, end = this.length)
参数:target 复制的位置
start 复制的起始位置
end 复制的结束位置(不包含)(默认为 *array*.length) 如果为负值,表示倒数
*/
// let arr = [1, 2, 3, 4, 5]
// console.log(arr.copyWithin(1, 3)) // [1, 4, 5, 4, 5]
// 5.fill
/*
用一个固定值填充到一个数组中
语法:arr.fill(value, start, end)
参数:value 用来填充数组元素的
start 起始索引,默认值为0
end 终止索引,默认值为 this.length
*/
// let arr = new Array(3).fill(7) //填充一个长度为3的全为7的数组
// console.log(arr) // [7, 7, 7]
// let arr = [1, 2, 3, 4, 5]
// arr.fill('imooc', 1, 3)
// console.log(arr) // [1, 'imooc', 'imooc', 4, 5]
//6.includes:检查数组中是否包含目标元素,包含返回true,否则返回false
let arr = [1, 2, 3, NaN]
//indexOf:检查数组中是否包含目标元素,包含返回1,否则返回-1;不能检查NaN
console.log(arr.indexOf(NaN)) //-1
console.log(arr.includes(NaN)) //true
2-5 函数的参数
- 参数的默认值
- 与解构赋值结合
- length属性
- 作用域
- 函数的name属性
//1.函数默认参数
// function foo(x, y = 'world') {
// console.log(x, y)
// }
// foo('hello', 0)
// function foo(x = 5) {
// //都报错,因为函数参数是默认声明的,相当于在函数内部加一条语句:let x = 5
// // let x = 1
// // let const = 1
// }
// 注意:
// 1.函数参数默认声明
// 2.参数不能重复
// 3.参数默认值一定要放在最后
// 2.与解构赋值结合
// function foo({x, y = 5}) {
// console.log(x, y) // 1 5
// }
// foo({
// x: 1
// })
// 解构赋值两边的结构需要匹配
// 应用:
// function ajax(url, {
// body = '',
// method = 'GET',
// headers = {}
// } = {}) { //默认参数为{}空对象
// console.log(method)
// }
// //解构赋值匹配
// ajax('http://www.imooc.com', {
// method: 'POST'
// })
// 3. length属性
// function foo(x = 1, y) {
// console.log(x, y)
// }
// console.log(foo.length) //foo.length:返回函数 没有指定默认值 的参数个数
// 4.作用域 *****
// 题目1:
// let x = 1
// function foo(x, y = x) {
// console.log(y) //2
// }
// foo(2)
//当函数执行时,传入一个参数2,赋值给x,在函数参数作用域中,再将x的值赋给y,此时x的值为2,所以y=2
// 题目2:
// let x = 1
// function foo(y = x) {
// let x = 2
// console.log(y) //1
// }
// foo()
//函数执行时,参数y=x形成一个单独的作用域,当前作用域没有x,沿着作用域链网上找,此时找到的x是全局变量,所以y=x=1
// 题目3:
// function foo(y = x) {
// let x = 2
// console.log(y) //报错,x未定义
// }
// foo()
// 5.函数的name属性
// function foo() {}
// console.log(foo.name) //foo
// console.log((new Function).name) //anonymous
function foo(x, y) {
//this指向bind参数对象
console.log(this, x, y) //{name: 'mys'} 1 2
}
foo.bind({name: 'mys'})(1, 2)
console.log(foo.bind({}).name) // bound foo(函数名)
console.log((function () {}).bind({}).name) // bound (没有函数名就不输出)
// bind()方法主要就是将函数绑定到某个对象
// bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值
2-6 扩展运算符与rest参数
...
- 扩展运算符:把数组、类数组展开成用
,
隔开的值 - rest参数:把用
,
隔开的值组合成一个数组
// 1.扩展运算符
// function foo(a, b, c) {
// console.log(a, b, c)
// }
// let arr = [1, 2, 3]
// foo(...arr)
// console.log(...arr) //1 2 3
// ...将数组或类数组展开成用,隔开的值
// 合并数组
// let arr1 = [1, 2, 3]
// let arr2 = [4, 5, 6]
// //es5
// // Array.prototype.push.apply(arr1, arr2)
// // console.log(arr1)
// //es6
// arr1.push(...arr2)
// console.log(arr1)
// let str = 'imooc'
// var arr = [...str]
// console.log(arr) // ['i', 'm', 'o', 'o', 'c']
// 2.rest参数
//求和
// function foo(x, y, z) {
// // console.log(arguments) // arguments:不是真正的数组
// let sum = 0
// //es5
// // Array.prototype.forEach.call(arguments, function (item) {
// // sum += item
// // })
// //es6
// Array.from(arguments).forEach(function (item) {
// sum += item
// })
// return sum
// }
// console.log(foo(1, 2)) // 3
// console.log(foo(1, 2, 3)) // 6
// 1)...args:将当前参数变成数组
// function foo(...args) {
// // console.log(args) // [1, 2] 将以,隔开的数转换成数组
// let sum = 0
// args.forEach(function (item) {
// sum += item
// })
// return sum
// }
// console.log(foo(1, 2)) // 3
// console.log(foo(1, 2, 3)) // 6
// 2)接收剩余参数
// function foo(x, ...args) {
// console.log(x) // 1
// console.log(args) // [2, 3]
// }
// foo(1, 2, 3)
// 3)数组解构
// let [x, ...y] = [1, 2, 3]
// console.log(x) // 1
// console.log(y) // [2, 3]
//总:
// 1)...放在=的左边或者形参上,就是rest参数 (合并成数组)
// 2)...放在=的右边或者实参上,就是扩展运算符 (展开单个值)
2-7 箭头函数
-
this指向定义时所在的对象,而不是调用时所在的对象
-
不可以当作构造函数
-
不可以使用arguments对象
箭头函数:左边参数,右边函数体
箭头函数中本质上没有this,而是继承了其上层的this
// es5
// 函数声明:会有函数的预定义
// function sum(x, y) {
// return x + y
// }
// console.log(sum(4, 5))
// 函数表达式
// let sum = function (x, y) {
// return x +y
// }
// console.log(sum(4, 5))
// es6
// 箭头函数:左边参数,右边函数体
// let sum = (x, y) => {
// return x + y
// }
// 简写:参数:只有一个参数,()可省略;函数体:只有一条语句,且有返回值:省略{}、return
// let sum = (x, y) => x + y
// console.log(sum(4, 5))
// let x = x => x
// //等价于下面:
// let x = function (x) {
// return x
// }
let oBtn = document.getElementById('#btn');
// oBtn.addEventListener('click', function () {
// setTimeout(function () {
// console.log(this) // window 当前对象
// }, 1000)
// })
// 三个方法可以改变this指向:call apply bind
// oBtn.addEventListener('click', function () {
// console.log(this) // button
// setTimeout(function () {
// console.log(this) // button
// }.bind(this), 1000) // bind的this指向第一个参数对象
// })
// 使用箭头函数
// 1. 在箭头函数中,this指向的是定义时的对象
// 箭头函数中本质上没有this,而是继承了其上层的this
// oBtn.addEventListener('click', function () {
// setTimeout (() => {
// console.log(this) // button 此处外层就是oBtn
// })
// })
// 2. 箭头函数不可以当做构造函数
// 类
// function People(name, age) {
// // this.name:属性名称 name:传递的参数
// this.name = name
// this.age = age
// }
// let People = (name, age) => {
// this.name = name
// this.age = age
// }
// let p1 = new People('mys', 18)
// console.log(p1) // People {} 空的,在浏览器中运行会报错:People is not a constructor
//eval()函数:参数是字符串,作用是把这段字符串当作javascript语句
// 3. 箭头函数中不可以使用arguments对象
// let foo = () => {
// console.log(arguments) // 浏览器中运行报错:arguments is not defined
// }
// foo(1, 2, 3)
//箭头函数可以使用...args去代替arguments参数来使用
let foo = (...args) => {
console.log(args) // [1, 2, 3]
}
foo(1, 2, 3)
2-8 对象的扩展
- 属性简洁表示法
- 属性名表达式
- Object.is()
- 扩展运算符与Object.assign()
- in
- 对象的遍历方式
// let name = 'mys'
// let age = 18
// let s = 'school'
// let obj = {
// // name: name,
// // age: age
// // 对象中key和value一样,可以简写
// // 1.属性简写表达式
// name,
// age,
// // 2.属性名表达式
// [s]: 'imooc',
// //对象里的方法不要用箭头函数,箭头函数的this是定义时的对象
// // study: function () {
// // console.log(this.name + ' is learn')
// // }
// //3. 对象中方法的简写
// study() {
// console.log(this.name + ' is learn')
// }
// }
// console.log(obj) // {name: 'mys', age: 18, school: 'imooc', study: ƒ}
// obj.study() // mys is learn
// 4.Object.is()判断两个值是否相等
// console.log(Object.is(2, '2')) // false
// console.log(Object.is(NaN, NaN)) // true
// console.log(Object.is(+0, -0)) // false
// let obj1 = { // new Object 在堆中开辟新空间
// name: 'mys',
// age: 18
// }
// let obj2 = {
// name: 'mys',
// age: 18
// }
// console.log(Object.is(obj1, obj2)) // false 对象比较的是地址
// let obj3 = obj1 //指向同一个地址
// console.log(Object.is(obj1, obj3)) // true
// 5.扩展运算符 & Object.assign()
// let x = {
// a: 3,
// b: 4
// }
// let y = {...x}
// console.log(y) // {a: 3, b: 4}
// let z = {}
// Object.assign(z, x) // 合并对象
// console.log(z) // {a: 3, b: 4}
// let x1 = {
// a: 5,
// b: 6
// }
// Object.assign(x, x1) // 用后面的去合并前面的,如果变量相同会覆盖
// console.log(x) //{a: 5, b: 6}
//6.in
// let x = {
// a: 3,
// b: 4
// }
// console.log('aa' in x) // false
// let arr = [1, 2, 3]
// console.log(3 in arr) // false
// console.log(2 in arr) // true 2是位置
// 7.对象的遍历
let obj = {
name: 'mys',
age: 18
}
// 1) for in
for (let key in obj) {
console.log(key, obj[key]) // name mys age 18
}
// 2) forEach Object.keys
Object.keys(obj).forEach(key => {
console.log(key, obj[key]) // name mys age 18
})
// 3) forEach Object.getOwnPropertyNames
Object.getOwnPropertyNames(obj).forEach(key => {
console.log(key, obj[key])
})
//4) forEach Reflect.ownKeys
Reflect.ownKeys(obj).forEach(key => {
console.log(key, obj[key])
})
2-9 深拷贝与浅拷贝
如何把一个对象复制给另一个对象
Object.assign()
//1. Object.assign()浅拷贝,不安全
// let target = {
// a: {
// b: {
// c: 1
// },
// d: 2,
// e: 5
// }
// }
// let source = {
// a: {
// b: {
// c: 1
// },
// d: 4
// }
// }
// Object.assign(target, source)
// console.log(target)
//输出:
// a:
// b: {c: 1}
// d: 4
// e不见了,Object.assign()拷贝有问题,对于基本数据类型的拷贝没问题,
// 但是对于引用类型的数据,拷贝的是地址,如果拷贝的内容结构复杂,有些属性可能会被丢掉
//2.深拷贝
//深拷贝:两变量的值变化互不影响
// let a = 5
// let b = a
// a = 6
// console.log(a, b) // 6 5
//浅拷贝:两个的值变化时会相互影响,此处两个对象指向的是同一个内存地址
// let obj1 = {
// name: 'mys',
// age: 18
// }
// let obj2 = obj1
// obj1.age = 20
// console.log(obj1) // {name: 'mys', age: 20}
// console.log(obj2) // {name: 'mys', age: 20}
// // json:'{"a": "hello", "b": "world"}' json的本质是字符串
// let obj = JSON.parse('{"a": "hello", "b": "world"}') // 将json格式的数据转换成对象
// console.log(obj) // {a: 'hello', b: 'world'}
// let str = JSON.stringify(obj) // 将对象转换成json格式
// console.log(str) // {"a":"hello","b":"world"}
//1) 利用JSON的方法实现深拷贝
// let obj1 = {
// name: 'mys',
// age: 18
// }
// let str = JSON.stringify(obj1)
// let obj2 = JSON.parse(str)
// obj1.age = 20 // obj1改变,obj2不会改变,obj1 obj2是两个独立的对象
// console.log(obj2) // {name: 'mys', age: 18}
//typeof只能判断基本类型数据,不能判断引用类型数据
//2) 可以通过toString()方法判断类型
// 检查类型
let checkType = data => {
//slice(8, -1):直接获取的类型有[]{},需要去除
//[object String]:从第8位开始截取,-1代表截取到倒数第一位(不包含),正好截取到需要的String
return Object.prototype.toString.call(data).slice(8, -1)
}
//深拷贝
let deepClone = target => {
let targetType = checkType(target) // 当前数据的类型
let res // 返回值
if (targetType === 'Object') {
//当前数据是Object类型,返回空对象
res = {}
} else if (targetType === 'Array') {
//当前数据是Array类型,返回空数组
res = []
} else {
//当前数据既不是对象,也不是数组,就是基本数据类型
return target
}
for(let i in target) {
let value = target[i]
let valueType = checkType(value)
// 如果获取到的value也是对象或数组类型,需要递归进行拷贝
if (valueType === 'Object' || valueType === 'Array') {
res[i] = deepClone(value)
} else {
// 如果是基本数据类型,直接赋值即可
res[i] = value
}
}
return res
}
let obj1 = {
name: 'mys',
hobby: ['coding', 'eating']
}
let obj2 = deepClone(obj1)
obj2.hobby[0] = 'sleeping'
console.log(obj1) // hobby: ['coding', 'eating']
console.log(obj2) // hobby: ['sleeping', 'eating']
3 ECMAScript2015(ES6)之新特性
3-1 ES5中的类与继承
// 1.类
// 构造函数
function People(name, age) {
// console.log(this) // this指向当前实例化的对象
// 实例属性
this.name = name
this.age = age
People.count++
}
// 静态属性
People.count = 0
//静态方法
People.getCount = function () {
console.log(this) // this指向当前构造函数
console.log('当前人数:' + People.count)
}
// 方法不定义在构造函数中,而是定义在类的原型中
// 实例方法
People.prototype.showName = function () {
console.log('名字:' + this.name)
}
let p1 = new People('mys', 18)
console.log(p1)
p1.showName()
console.log(People.count)
console.log(People.getCount())
// 静态属性通过类来获取
// 静态属性定义在类中,实例属性定义在构造函数中
//静态方法
console.log(Math.max(4, 5))
console.log('------------------')
// 2.类的继承 (组合继承)
//父类
function Animal(name) {
this.name = name
}
Animal.prototype.showName = function () {
console.log('名字:' + this.name)
}
//子类
function Dog(name, color) {
Animal.call(this, name) // 继承属性
this.color = color
}
// 继承方法
Dog.prototype = new Animal()
Dog.prototype.constructor = Dog
let d1 = new Dog('wangcai', 'white')
console.log(d1)
d1.showName()
3-2 ES6中的类与继承
- class
- extends
- constructor
- static
- super
- get/set
// 类
class People {
constructor(name, age) {
this.name = name
this.age = age
this._sex = -1
}
// gex/set:设置属性,可以实现一些业务逻辑操作,可以对属性的读写进行一些拦截操作
// 如果只是constructor就做不到
// eg.0:female 1:male
get sex() {
if (this._sex === 1) {
return 'male'
} else if (this._sex === 0) {
return 'female'
} else {
return 'error'
}
}
set sex(val) {
if (val === 0 || val === 1) {
this._sex = val
}
}
// 普通方法
showName() {
console.log(this.name)
}
// 静态方法
static getCount() {
return 5
}
}
// 静态属性
People.count = 9
let p1 = new People('mys', 18)
console.log(p1)
p1.sex = 1
console.log(p1.sex)
console.log(People.getCount()) // 静态方法只能用类来调用
console.log(People.count)
// 继承
class Coder extends People {
constructor(name, age, company) {
super(name, age); // 调用父类构造函数
this.company = company // 子类自身的属性
}
showCompany() {
console.log('company:' + this.company)
}
}
let c1 = new Coder('mys', 18, 'imooc')
console.log(c1)
c1.showName() // 子类调用父类的方法
c1.showCompany() // 子类自身的方法
c1.sex = 0 // 子类调用父类的set get设置的属性
console.log(c1.sex)
console.log(Coder.getCount()) // 子类也可以调用父类的静态方法
3-3 新的原始数据类型Symbol
- 一种新的原始数据结构
- 声明方式
- 应用场景
// Symbol 独一无二
// 1. 声明方式
// 1) 直接声明
// let s1 = Symbol()
// let s2 = Symbol()
// console.log(s1) // Symbol()
// console.log(s1 === s2) // false
// 2) 可以传递一个字符串作为参数,用来描述
// let s1 = Symbol('foo')
// console.log(s1) // Symbol(foo)
// 3) 如果传递一个对象,会调用对象的toString()方法,将其转成字符串,再生成Symbol
// const obj = {
// name: 'mys',
// toString() {
// return this.name
// }
// }
// let s = Symbol(obj)
// console.log(s) // Symbol(mys)
// 1.description
// let s = Symbol('foo')
// console.log(s.description) // foo
// 2.for
// 通过Symbol.for()声明的是全局的
// let s1 = Symbol.for('foo')
// let s2 = Symbol.for('foo')
// console.log(s1 === s2) // true
//
// function foo() {
// return Symbol.for('foo')
// }
// const x = foo()
// const y = Symbol.for('foo')
// console.log(x === y) // true
// 3.kekFor
// 返回一个已经登记的Symbol的key
// const s1 = Symbol('foo')
// console.log(Symbol.keyFor(s1)) // undefined
// const s2 = Symbol.for('foo')
// console.log(Symbol.keyFor(s2)) // foo
// const grade = {
// 张三: {address: 'xxx', tel: '111'},
// 李四: {address: 'yyy', tel: '222'},
// 李四: {address: 'zzz', tel: '333'}
// }
// console.log(grade)
// // 张三: {address: 'xxx', tel: '111'}
// // 李四: {address: 'zzz', tel: '333'} 会覆盖上一个李四
// 对象的key也可以是一个变量
// const stu1 = '李四'
// const stu2 = '李四'
// const grade = {
// [stu1]: {address: 'yyy', tel: '222'},
// [stu2]: {address: 'zzz', tel: '333'}
// }
// console.log(grade) // 李四: {address: 'zzz', tel: '333'} 还是被覆盖
// const stu1 = Symbol('李四')
// const stu2 = Symbol('李四')
// const grade = {
// [stu1]: {address: 'yyy', tel: '222'},
// [stu2]: {address: 'zzz', tel: '333'}
// }
// console.log(grade)
// // Symbol(李四): {address: 'yyy', tel: '222'}
// // Symbol(李四): {address: 'zzz', tel: '333'}
// 利用Symbol声明不会被覆盖
// 利用Symbol声明变量,作为对象的key,保证key不重复
// const sym = Symbol('imooc')
// class User {
// constructor(name) {
// this.name = name
// this[sym] = ' imooc.com'
// }
// getName() {
// return this.name + this[sym]
// }
// }
// const user = new User('mys')
// console.log(user.getName()) // mys imooc.com
//
// for (let key in user) {
// console.log(key) // name 无法遍历Symbol声明的变量,即可以用Symbol声明来隐藏变量
// }
// for (let key in Object.keys(user)) {
// console.log(key) // 0
// }
// for (let key of Object.getOwnPropertySymbols(user)) {
// console.log(key) // Symbol(imooc) 只能获取Symbol
// }
//
// for (let key of Reflect.ownKeys(user)) {
// console.log(key) // name Symbol(imooc) 能够获取普通属性和Symbol属性
// }
// function getArea(shape) {
// let area = 0
// switch (shape) {
// case 'Triangle':
// area = 1
// break
// case 'Circle':
// area = 2
// break
// }
// return area
// }
// console.log(getArea('Triangle')) // Triangle耦合,魔术字符串
//应用场景:消除魔术字符串
const shapeType = {
triangle: Symbol(),
circle: Symbol
}
function getArea(shape) {
let area = 0
switch (shape) {
case shapeType.triangle:
area = 1
break
case shape.circle:
area = 2
break
}
return area
}
console.log(getArea(shapeType.triangle))
3-4 新的数据结构Set
- 一种新的数据结构
- 常用方法
- 遍历
- 应用场景
- WeakSet
// Set里面的值不重复
// 1. Set常用方法
// let s = new Set([1, 2, 2, 3])
// console.log(s) // {1, 2, 3}
// s.add('imooc').add('es') // 链式添加
// console.log(s) // {1, 2, 3, 'imooc', 'es'}
// s.delete(2) // 删除
// s.has('imooc') // 判断是否含有某个值 true
// s.size // 长度
// // s.clear() // 清空
//
// 2.遍历
// 1) forEach
// s.forEach(item => console.log(item))
//
// //Set中的key和value值相同, keys\values\entries结果相同
// 2) for of s.keys()
// for (let item of s.keys()) {
// console.log(item)
// }
// 3)for of s.values()
// for (let item of s.values()) {
// console.log(item)
// }
// 4) for of s.entries()
// for (let item of s.entries()) {
// console.log(item)
// }
// 3.应用场景
// 1)数组去重
// let arr = [1, 2, 3, 3, 2, 1]
// let s = new Set(arr)
// console.log(s) // {1, 2, 3}
// 2)合并去重
// let arr1 = [1, 2, 3, 4]
// let arr2 = [1, 2, 3, 5, 6]
// let s = new Set([...arr1, ...arr2])
// console.log(s)
// 0: 1
// 1: 2
// 2: 3
// 3: 4
// 4: 5
// 5: 6
// 3)Set转为数组Array
// console.log([...s]) // [1, 2, 3, 4, 5, 6]
// console.log(Array.from(s)) // [1, 2, 3, 4, 5, 6]
// 4)交集
// let s1 = new Set(arr1)
// let s2 = new Set(arr2)
// let res = new Set(arr1.filter(item => s2.has(item)))
// console.log(res) // {1, 2, 3}
// 5)差集
// let arr3 = new Set(arr1.filter(item => ! s2.has(item)))
// let arr4 = new Set(arr2.filter(item => ! s1.has(item)))
// console.log([...arr3, ...arr4]) // [4, 5, 6]
// 4.WeakSet
// 添加和删除必须是同一个对象
let ws = new WeakSet()
// 添加
ws.add({
name: 'mys'
})
const obj = {
age: 18
}
ws.add(obj)
// 删除 不成功,因为此处删除的是另一个对象,虽然与上面添加的对象长的一样,但这两个不是同一个对象
ws.delete({
name: 'mys'
})
ws.delete(obj) // 删除成功
console.log(ws)
// WeakSet不能遍历
// 垃圾回收机制 GC
// WeakSet中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用
// 如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中
// 应用:创建临时对象;利用对象绑定相关信息
// WeakSet与Set区别
// 1. WeakSet只能存储对象,Set各数据类型都可以
// 2.WeakSet不可以循环遍历,Set可以
// 3.WeakSet是弱引用,不会被垃圾回收机制考虑
3-5 新的数据结构Map
- 一种新的数据结构
- 常用方法
- 遍历
- 应用场景
- WeakMap
// Map(key, value)
// 对于Array的key:字符串;Symbol
// 对于Map的key:各类型都可以作为key
// 1.常用方法
// let m = new Map()
// // 对象可以作为key
// let obj = {
// name: 'imooc'
// }
// m.set(obj) // 设置
// console.log(m)
// console.log(m.get(obj)) // 获取
// // m.delete(obj) // 删除
// console.log(m.has(obj)) // true
// let map = new Map([
// ['name', 'mys'],
// ['age', 18]
// ])
// console.log(map) // {'name' => 'mys', 'age' => 18}
// console.log(map.size) // 2
// console.log(map.get('age')) // 18
// console.log(map.has('name')) // true
// map.set('name', 'zhangsan')
// console.log(map) // {'name' => 'zhangsan', 'age' => 18}
// map.delete('name')
// console.log(map) // {'age' => 18}
// 2. 遍历
let map = new Map([
['name', 'mys'],
['age', 18]
])
//1)
// map.forEach((value, key) => console.log(key, value)) // name mys age 18
// 2)
// for (let [key, value] of map) {
// console.log(key, value) // // name mys age 18
// }
// 3)
// for (let key of map.keys()) {
// console.log(key) // name age
// }
// 4)
// for (let value of map.values()) {
// console.log(value) // mys 18
// }
// 5)
// for (let [key, value] of map.entries()) {
// console.log(key, value) // // name mys age 18
// }
// map object 应用场景一样
// map比object更加灵活,key也各种各样
// 频繁增删map性能更好
// 3. WeakMap
// let wm = new WeakMap()
// wm.set([1], 2)
// wm.set({
// name: 'mys'
// }, 'es')
// console.log(wm)
// WeakMap不支持clear,也不支持遍历,没有size方法
// WeakMap也是弱引用,有助于防止内存泄漏,当没有对象引用时,自动消失
let wm = new WeakMap()
let elem = document.getElementsByTagName('h1')
wm.set(elem, 'info')
console.log(wm.get(elem))
3-6 字符串的扩展
模板字符串重要
// 1.字符的unicode表示
// es6 \uxxxx 码点 0000~ffff
// \u20BB7 -> \u20BB+7 ===> 超出码点范围用{}表示:\u{20BB7}
// 表示字符
// console.log('\z' === 'z') // true \z是非制表符,反斜杠\什么也不表示
// 浏览器中运行:console.log('\172' === 'z') //true
/**
* 6中表示字符的方式
* (0)z
* (1)\z
* (2)\172
* (3)\x7A
* (4)\u007A
* (5)\u{7A}
*/
// 2. 字符串的遍历器接口
// for (let item of 'imooc') {
// console.log(item)
// }
// 3.模板字符串 *****
// ``
// 1)多行字符串可以直接换行
// const str = `
// <ul>
// <li></li>
// <li></li>
// <li></li>
// </ul>
// `
// console.log(str)
// // <ul>
// // <li></li>
// // <li></li>
// // <li></li>
// // </ul>
//
// // 2)使用${表达式}直接运算
// const a = 2, b = 3
// const str1 = `a+b=${a + b}`
// console.log(str1) // a+b=5
//
// // 3)嵌套模板
// // 需求: icon icon-big
// const isLargeScreen = () => {
// return true
// }
// // es5
// let class1 = 'icon'
// class1 += isLargeScreen() ? ' icon-big' : ' icon-small'
// console.log(class1)
// // es6
// const class2 = `icon icon-${isLargeScreen() ? 'big' : 'small'}`
// console.log(class2)
//
// // 3) 带标签的模板字符串
// const foo = (a, b, c, d) => {
// console.log(a) // ['姓名:', ',年龄:', ',', '', raw: Array(4)]
// console.log(b) // mys
// console.log(c) // 18
// console.log(d) // 123
// }
// const name = 'mys'
// const age = 18
// foo`姓名:${name},年龄:${age},${123}`
// 4.方法
// 1)静态方法 fromCodePoint
console.log(String.fromCharCode(0x20BB7)) // ஷ es5 不能识别码点超过范围:0000~ffff
console.log(String.fromCodePoint(0x20BB7)) // 𠮷 es6 范围外也能识别
// 2) indexOf includes startsWith endsWith repeat
const str = 'imooc'
console.log(str.indexOf('mo')) // 1
console.log(str.includes('mo')) // true
console.log(str.startsWith('im')) // true
console.log(str.endsWith('oc')) // true
const newStr = str.repeat(4)
console.log(newStr) // imoocimoocimoocimooc
3-7 正则表达式的扩展
- es5中修饰符:i:忽略大小写 m:多行匹配 g:全局匹配
- y修饰符
- u修饰符 eg:\uD842\uDFB7
// es5 i:忽略大小写 m:多行匹配 g:全局匹配
//es6
// y修饰符 粘连(sticky)修饰符
// const str = 'aaa_aa_a'
// const reg1 = /a+/g
// const reg2 = /a+/y
// // g 每次匹配剩余的
// console.log(reg1.exec(str)) // ['aaa', index: 0, input: 'aaa_aa_a', groups: undefined]
// console.log(reg1.exec(str)) // ['aa', index: 4, input: 'aaa_aa_a', groups: undefined]
// console.log(reg1.exec(str)) // ['a', index: 7, input: 'aaa_aa_a', groups: undefined]
// // y 每次匹配会在剩余的第一个进行匹配
// console.log(reg2.exec(str)) // ['aaa', index: 0, input: 'aaa_aa_a', groups: undefined]
// console.log(reg2.exec(str)) // null 剩余:_aa_a 第一个为_,匹配不上
// // 上次没匹配上,现在有从头开始匹配
// console.log(reg2.exec(str)) // ['aaa', index: 0, input: 'aaa_aa_a', groups: undefined]
// u修饰符 unicode \u以unicode模式匹配
const str = '\uD842\uDFB7' // 表示一个字符
console.log(/^\uD842/.test(str)) // es5 true 错误的
console.log(/^\uD842/u.test(str)) // es6 false
// . 除换行意外的任意单个字符
console.log(/^.$/.test(str)) // false
console.log(/^.$/u.test(str)) // true
console.log(/\u{61}/.test('a')) // false
console.log(/\u{61}/u.test('a')) // true
console.log(/𠮷{2}/.test('𠮷𠮷')) // false
console.log(/𠮷{2}/u.test('𠮷𠮷')) // true
3-8 数值的扩展
// es5
// // 十进制 => 二进制
// const a = 5
// console.log(a.toString(2)) // 101
// // 二进制 => 十进制
// const b = 101
// console.log(parseInt(b, 2)) // 5
/**
* Number方法
* Number.parseInt()
* Number.parseFloat()
* Number.isFinite()
* Number.isNaN()
* Number.isInteger()
* Number.MAX_SAFE_INTEGER
* Number.isSafeInteger()
*/
// es6 0B二进制 0O八进制
// const a = 0B0101
// console.log(a) // 5
// const b = 0O77
// console.log(b) // 63
// Number.isFinite() 判断当前数是否是有限的(只判断Number类型,其他都为false)
// console.log(Number.isFinite(5)) // true
// console.log(Number.isFinite()) // false
// console.log(Number.isFinite('imooc')) // false
// console.log(Number.isFinite(true)) // false
// Number.isNaN()
// console.log(Number.isNaN(NaN)) // true
// console.log(Number.isNaN(15)) // false
// console.log(Number.isInteger(5.5)) // false
//精度缺失问题
// 数字在计算机中是以二进制存储的,IEEE 754 双精度标准存储
// console.log(0.1000000000000001) // 0.1000000000000001
// console.log(0.10000000000000001) // 0.1
// console.log(0.10000000000000001 === 0.1) // true 精度缺失
// 整数范围 (-2^53, 2^53)
// const max = Math.pow(2, 53)
// console.log(Number.MAX_SAFE_INTEGER === max - 1) // true
// console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)) // true
// console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER +1)) // false
/**
* Math新增方法
* Math.trunc()
* Math.sign()
* Math.cbrt()
*
*/
// console.log(Math.trunc(5.5)) // 5
// console.log(Math.trunc(- 5.5)) // -5
// console.log(Math.trunc(true)) // 1
// console.log(Math.trunc(false)) // 0
// console.log(Math.trunc(NaN)) // NaN
// console.log(Math.trunc(undefined)) // NaN
// console.log(Math.trunc()) // NaN
//
// console.log(Number.parseInt(5.5)) // 5
// console.log(Number.parseInt(- 5.5)) // -5
// console.log(Number.parseInt(true)) // NaN
// console.log(Math.sign(5)) // 1
// console.log(Math.sign(- 5)) // -1
// console.log(Math.sign(0)) // 0
// console.log(Math.sign(true)) // 1
// console.log(Math.sign(false)) // 0
// console.log(Math.sign(NaN)) // NaN
console.log(Math.cbrt(8)) // 2 立方根
3-9 Proxy
- 代理
- 常用拦截方法
// es5
// let obj = {}
// let newVal = ''
// Object.defineProperty(obj, 'name', {
// get(){
// return newVal
// },
// set(val){
// console.log('set')
// newVal = val
// }
// })
// obj.name = 'es'
// console.log(obj.name)
// es6 proxy
// let obj = {}
// let p = new Proxy(obj, {})
// p.name = 'imooc'
// console.log(obj.name)
// for (let key in obj) {
// console.log(key)
// }
// 1.get
// 需求:通过下标访问数组,如果能访问到直接返回值,访问不到返回error
// let arr = [7, 8, 9]
// // Proxy参数:1.包装的对象 2.代理的相关配置
// arr = new Proxy(arr, {
// get(target, prop) {
// console.log(target, prop)
// return prop in target ? target[prop] : 'error'
// }
// })
// console.log(arr[1]) // 8
// console.log(arr[10]) // error
// let dict = {
// 'hello': '你好',
// 'world': '世界'
// }
// dict = new Proxy(dict, {
// get(target, prop) {
// return prop in target ? target[prop] : prop
// }
// })
// console.log(dict['hello']) // 你好
// console.log(dict['imooc']) // imooc
// 2. set
// let arr = []
// arr = new Proxy(arr, {
// set(target, prop, val) {
// if (typeof val === 'number') {
// target[prop] = val
// return true
// } else{
// return false
// }
// }
// })
// arr.push(5)
// arr.push(6)
// console.log(arr[0], arr[1], arr.length)
// 3. has
// let range = {
// start: 1,
// end: 5
// }
// range = new Proxy(range, {
// has(target, prop) {
// return prop >= target.start && prop <= target.end
// }
// })
// console.log(2 in range) // true
// console.log(9 in range) // false
// 4.ownKeys
// let obj = {
// name: 'imooc',
// [Symbol('es')]: 'es' // []表示变量
// }
// console.log(Object.getOwnPropertyNames(obj)) // ['name']
// console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(es)]
// console.log(Object.keys(obj)) // // ['name']
// for (let key in obj) {
// console.log(key) // name
// }
// 保护属性,拦截_开头的属性
// let userinfo = {
// username: 'mys',
// age: 18,
// _password: 'xxx'
// }
// userinfo = new Proxy(userinfo, {
// ownKeys(target) {
// return Object.keys(target).filter(key => ! key.startsWith('_'))
// }
// })
// for (let key in userinfo) {
// console.log(key) // username age
// }
// console.log(Object.keys(userinfo)) // ['username', 'age']
// get set deleteProperty ownKeys应用
// let user = {
// name: 'mys',
// age: 18,
// _password: 'xxx' // 私有属性
// }
// user = new Proxy(user, {
// get(target, prop) {
// if (prop.startsWith('_')) {
// throw new Error('不可访问')
// } else {
// return target[prop]
// }
// },
// set(target, prop, val) {
// if (prop.startsWith('_')) {
// throw new Error('不可访问')
// } else {
// target[prop] = val
// return true
// }
// },
// deleteProperty(target, prop) {
// if (prop.startsWith('_')) {
// throw new Error('不可访问')
// } else {
// delete target[prop]
// return true
// }
// },
// ownKeys(target) {
// return Object.keys(target).filter(key => !key.startsWith('_'))
// }
// })
// try {
// delete user._password
// } catch (e) {
// console.log(e.message) // 不可访问
// }
// for (let key in user) {
// console.log(key)
// }
// 5.apply
// let sum = (...args) => {
// let num = 0
// args.forEach(item => {
// num += item
// })
// return num
// }
// sum = new Proxy(sum, {
// // 函数调用时会被拦截,执行apply方法
// apply(target, ctx, args) {
// return target(...args) * 2
// }
// })
// console.log(sum(1, 2)) // 6
// console.log(sum.call(null, 1, 2, 3)) // 12
// console.log(sum.apply(null, [1, 2, 3])) // 12
// 6. construct new
let User = class {
constructor(name) {
this.name = name
}
}
User = new Proxy(User, {
construct(target, args, newTarget) {
console.log('construct')
return new target(...args)
}
})
console.log(new User('imooc')) // construct {name: 'imooc'}
3-10 反射Reflect
// Object => Reflect
// es5中,很多方法会设置到Object下面
// 1. 将Object属于语言内部的方法放到Reflect上
// let obj = {}
// let newVal = ''
// Reflect.defineProperty(obj, 'name', {
// get() {
// return newVal
// },
// set(val) {
// console.log('set')
// newVal = val
// }
// })
// obj.name = 'es'
// console.log(obj.name)
// 2. 修改Object某些方法的返回结果,使其更加合理
// // es5
// try {
// Object.defineProperty()
// } catch (e) {
//
// }
// // es6
// if (Reflect.defineProperty()) { // 返回boolean
//
// }
// 3. 让Object操作变成函数行为
// console.log('assign' in Object) // true 命令式操作
// console.log(Reflect.has(Object, 'assign')) // true 函数式操作
// 4.Reflect对象的方法和Proxy对象的方法一一对应
// let user = {
// name: 'mys',
// age: 18,
// _password: 'xxx' // 私有属性
// }
// user = new Proxy(user, {
// get(target, prop) {
// if (prop.startsWith('_')) {
// throw new Error('不可访问')
// } else {
// // return target[prop]
// return Reflect.get(target, prop)
// }
// },
// set(target, prop, val) {
// if (prop.startsWith('_')) {
// throw new Error('不可访问')
// } else {
// // target[prop] = val
// Reflect.set(target, prop, val)
// return true
// }
// },
// deleteProperty(target, prop) {
// if (prop.startsWith('_')) {
// throw new Error('不可访问')
// } else {
// // delete target[prop]
// Reflect.deleteProperty(target, prop)
// return true
// }
// },
// ownKeys(target) {
// // return Object.keys(target).filter(key => !key.startsWith('_'))
// return Reflect.ownKeys(target).filter(key => !key.startsWith('_'))
// }
// })
// try {
// // console.log(user._password)
// // user._password = 'vv'
// delete user._password
// } catch (e) {
// console.log(e.message) // 不可访问
// }
// for (let key in user) {
// console.log(key)
// }
let sum = (...args) => {
let num = 0
args.forEach(item => {
num += item
})
return num
}
sum = new Proxy(sum, {
// 函数调用时会被拦截,执行apply方法
apply(target, ctx, args) {
// return target(...args) * 2
return Reflect.apply(target, target, [...args]) * 2
}
})
console.log(sum(1, 2)) // 6
console.log(sum.call(null, 1, 2, 3)) // 12
console.log(sum.apply(null, [1, 2, 3])) // 12
4 ECMAScript2015(ES6)之异步编程与模块化
4-1 异步操作必备知识
- JS是单线程的
- 同步任务与异步任务
- Ajax原理
- Callback Hell
为什么 JS 是单线程的?作为浏览器脚本语言,JavaScript 的主要用途是与用户互动,以及操作 DOM 。这决定了它只能是单线程,否则会带来很复杂的同步问题。
JS是单线程的,如果有多个线程会排队等待。首先执行的是同步任务的主线程,当主线程执行完毕才会执行异步线程;异步线程如setTimeout(() => {}, 1000)
,如果此时主线程没有执行完毕,异步线程先进入Event Table,等待延时时间,此处是1000ms,之后会进入Event Queue继续等待,直到主线程执行完毕才开始执行。
// const a = 2
// const b = 3
// console.log(a + b) // 同步
// setTimeout(() => {
// console.log(a + b) // 异步
// }, 1000)
// 前后端数据分离 前端<->后端 ajax
// 输出顺序?
console.log(1)
setTimeout(() => {
console.log(2)
}, 0) // 注意:虽然写的是0,但是在底层最小时间是4ms
console.log(3)
// 1 3 2
// 异步任务
setTimeout(() => {
task() // 表示一个任务
}, 1000)
sleep() // 表示一个很复杂的同步任务
4-2 Ajax原理与Callback Hell
Ajax(Asynchronous Javascript And XML)异步 JavaScript 和 XML,是指一种创建交互式、快速动态网页应用的网页开发技术,无需重新加载整个网页的情况下,能够更新部分网页的技术。通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
// 1. Ajax原理
function ajax(url, callback) {
// 1)创建对象
var xmlhttp // 此处使用var是为了兼容老版本浏览器
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest()
} else { // 兼容早期浏览器
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
}
// 2)发送请求
xmlhttp.open('GET', url, true)
xmlhttp.send()
// 3)服务器响应
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
var obj = JSON.parse(xmlhttp.responseText)
// console.log(obj)
callback(obj)
}
}
}
// var url = 'http://jsonplaceholder.typicode.com/users'
// ajax(url, res => {
// console.log(res)
// })
// 2.Callback Hell 回调地狱/回调深渊
// 请求1 -> 2 -> 3 顺序执行
// 1) 在static文件夹下创建三个json文件 a.json b.json c.json,内容如下:
// { { {
// "a": "A" "b": "B" "c": "C"
// } } }
// 2) 调用ajax函数
ajax('static/a.json', res => {
console.log(res)
// b在a的回调函数中执行
ajax('static/b.json', res => {
console.log(res)
ajax('static/c.json', res => {
console.log(res)
})
})
})
4-3 异步编程解决方案Promise
Promise 就是为了解决“回调地狱”问题的,它可以将异步操作的处理变得很优雅。回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象promise可以支持多个并发的请求,获取并发请求中的数据这个promise可以解决异步的问题,本身不能说promise是异步的。
// 1.Promise精髓:状态管理
// resolve:异步操作执行成功时的回调函数,reject:失败
// let p = new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log('imooc')
// // // 使用场景
// // if () {
// // resolve()
// // } else {
// // reject()
// // }
//
// // resolve('成功')
// reject('失败')
// }, 1000)
// }).then(res => { // then的第一个参数必写,第二个参数可选
// // 第一个参数:运行成功进入
// console.log(res)
// }, res => {
// // 第二个参数:运行失败进入
// console.log(res)
// })
// 输出顺序?
// let p = new Promise((resolve, reject) => {
// console.log(1)
// resolve()
// })
// console.log(2)
// // 1 2
// // Promise会立即执行
// p.then(res => {
// console.log(3)
// })
// // 1 2 3
// // Promise的状态是不可逆的
// 2.Promise的状态
// let p1 = new Promise((resolve, reject) => {
// resolve(1)
// })
// let p2 = new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve(2)
// }, 1000)
// })
// let p3 = new Promise((resolve, reject) => {
// setTimeout(() => {
// reject(3)
// }, 1000)
// })
// console.log(p1) // fulfilled
// console.log(p2) // pending
// console.log(p3) // pending
// setTimeout(() => {
// console.log(p2) // fulfilled
// }, 2000)
// setTimeout(() => {
// console.log(p3) // rejected
// }, 2000)
//
// p1.then(res => {
// console.log(res) // 1
// })
// p2.then(res => {
// console.log(res) // 2
// })
// p3.catch(err => { // catch和then的第二个参数作用相同,表示失败
// console.log(err) // 3
// })
// 3. Promise状态不可逆
// let p = new Promise((resolve, reject) => {
// resolve(1)
// reject(2)
// })
// p.then(res => {
// console.log(res)
// }, err => {
// console.log(err)
// })
// // 只输出1,因为Promise调用resolve(1)后,状态由pending变为fulfilled后,
// // 即使再调用reject(2)状态也不会再改变了
// 4. Promise调用Ajax异步操作
function ajax(url, successCallback, failCallback) {
// 1)创建对象
var xmlhttp // 此处使用var是为了兼容老版本浏览器
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest()
} else { // 兼容早期浏览器
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
}
// 2)发送请求
xmlhttp.open('GET', url, true)
xmlhttp.send()
// 3)服务器响应
xmlhttp.onreadystatechange = function () {
// 4:响应完成 200:成功
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
var obj = JSON.parse(xmlhttp.responseText)
// console.log(obj)
// 如果没有传递obj参数,直接 successCallback
successCallback && successCallback(obj)
} else if (xmlhttp.readyState === 4 && xmlhttp.status === 404) {
failCallback && failCallback(obj)
}
}
}
// 层层嵌套 => 复杂
// new Promise((resolve, reject) => {
// ajax('static/a.json', res => {
// console.log(res)
// resolve()
// })
// }).then(res => {
// console.log('a成功')
// // 此处需要return,表示把此处先创建的Promise返回出去,
// // 使得下面输出b成功的then()是此处新建的Promise,而不是一个空的Promise
// return new Promise((resolve, reject) => {
// ajax('static/b.json', res => {
// console.log(res)
// resolve()
// })
// })
// }).then(res => {
// console.log('b成功')
// return new Promise((resolve, reject) => {
// ajax('static/c.json', res => {
// console.log(res)
// resolve()
// })
// })
// }).then(res => {
// console.log('c成功')
// })
// 优化
function getPromise(url) {
return new Promise((resolve, reject) => {
ajax(url, res => {
resolve(res)
}, err => {
reject(err)
})
})
}
// 1) 三种都成功
// getPromise('static/a.json')
// .then(res => {
// console.log(res) // {a: 'A'}
// return getPromise('static/b.json')
// }).then(res => {
// console.log(res) // {b: 'B'}
// return getPromise('static/c.json')
// }).then(res => {
// console.log(res) // {c: 'C'}
// })
// 2)a失败
// getPromise('static/aa.json')
// .then(res => {
// console.log(res) // Not Found
// return getPromise('static/b.json')
// }, err => {
// console.log(err) // undefined
// // 注意:a即使是失败了,也要return getPromise,因为这里创建了b的Promise,
// // 否则后边b将输出undefined,因为如果没有return,b的Promise就是空
// return getPromise('static/b.json')
// }).then(res => {
// console.log(res) // {b: 'B'}
// return getPromise('static/c.json')
// }).then(res => {
// console.log(res) // {c: 'C'}
// })
// 3) 统一管理失败状态
getPromise('static/a.json')
.then(res => {
console.log(res) // {a: 'A'}
return getPromise('static/bb.json')
}).then(res => {
console.log(res) // Not Found
return getPromise('static/c.json')
}).then(res => {
console.log(res) // 无
}).catch(err => { // 统一管理失败的状态,如果a成功,b不成功
// a:正常, b:Not Found, c:无
console.log(err) // undefined
})
4-4 Promise的静态方法
- Promise.resolve()
- Promise.reject()
- Promise.all()
- Promise.race()
// 1. Promise.resolve()
// let p1 = Promise.resolve('success')
// p1.then(res => {
// console.log(res) // success
// })
// 2. Promise.reject()
// let p2 = Promise.reject('fail')
// p2.catch(err => {
// console.log(err) // fail
// })
// 应用场景:利用Promise的静态方法,包装需要返回的字符串
// function foo(flag) {
// if (flag) {
// return new Promise(resolve => {
// resolve('success')
// })
// } else {
// // return 'fail'
// return Promise.reject('fail') // 利用Promise的静态方法,包装需要返回的字符串
// }
// }
// foo(false).then(res => {
// console.log(res)
// }, err => {
// console.log(err)
// })
// 3. Promise.all():全部成功才成功,有一个失败则都失败
// 需求:三个异步操作都执行完成之后再做其他事
// let p1 = new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log(1) // 1
// resolve('1成功')
// }, 1000)
// })
// let p2 = new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log(2) // 2
// resolve('2成功')
// }, 2000)
// })
// let p3 = new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log(3) // 3
// resolve('3成功')
// }, 3000)
// })
// Promise.all([p1, p2, p3]).then(res => {
// console.log(res) // ['1成功', '2成功', '3成功']
// })
// // 输出顺序: 1 2 3 ['1成功', '2成功', '3成功']
// // 实现了需求,当三个异步操作执行完成,再执行其他的操作(then中)
// let p4 = new Promise((resolve, reject) => {
// setTimeout(() => {
// console.log(4) // 4
// reject('4失败')
// }, 1000)
// })
// Promise.all([p1, p2, p3, p4]).then(res => {
// console.log(res)
// }, err => {
// console.log(err) // 4失败
// })
// 4.Promise.race() 只要有一个完成,则全部都完成
// Promise.race([p1, p2, p3]).then(res => {
// console.log(res) // 1成功
// }, err => {
// console.log(err)
// })
// 应用场:1:上传图片
// const imgArr = ['1.jpg', '2.jpg', '3.jpg']
// let promiseArr = []
// imgArr.forEach(item => {
// promiseArr.push(new Promise((resolve, reject) => {
// // 图片上传操作
// // resolve()
// reject()
// }))
// })
// Promise.all(promiseArr).then(res => {
// // 插入数据库操作
// console.log('图片全部上传完成')
// }, err => {
// console.log('图片上传失败')
// })
// 应用场景2:图片加载
function getImg() {
return new Promise((resolve, reject) => {
let img = new Image()
img.onload = function () {
resolve(img)
}
img.src = 'https://www.imooc.com/static/img/index/logo2020.png'
// img.src = 'http://www.xxx.com'
})
}
function timeout() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时')
}, 2000)
})
}
// race:因为图片要么加载成功,要么加载失败
Promise.race([getImg(), timeout()]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
4-5 异步编程解决方案Generator
// generator 生成器函数
// function* foo() {
// for (let i = 0; i < 3; i ++) {
// yield i
// }
// }
//
// console.log(foo()) // Generator{_invoke: ƒ}
// let f = foo()
// console.log(f.next()) // {value: 0, done: false} value:当前的值,done:执行的情况
// console.log(f.next()) // // {value: 1, done: false}
// console.log(f.next()) // // {value: 2, done: false}
// console.log(f.next()) // // {value: undefined, done: true} done:true说明函数执行完成
// 生成器函数不会立即执行,而是返回生成器的迭代器对象。每次执行直到yield后面的语句为止
// 注意:
// 1.不能作为构造函数使用
// 2.关键字yield只能在生成器函数中使用,不能在其他地方使用
// function* gen(args) {
// args.forEach(item => {
// // yield item + 1 // 报错
// })
// }
// next generator是如何执行的?
// function* gen(x) {
// let y = 2 * (yield(x + 1))
// let z = yield(y / 3)
// return x + y + z
// }
// next的参数,上一个yield的返回值
// 1)next不传递参数的情况
// let g = gen(5)
// console.log(g.next()) // 6
// // 此处没有返回值,则yield(x + 1)为undefined,所以y=2*undefined => NaN
// console.log(g.next()) // NaN
// console.log(g.next()) // NaN
// 2)给next传递参数
// let g = gen(5)
// console.log(g.next()) // 6
// console.log(g.next(12)) // 此时y=24,返回8
// console.log(g.next(13)) // 此时z=13, x=5,y=24,返回42
// 应用:每次输出7的倍数
// function* count(x = 1) {
// while (true) {
// if (x % 7 === 0) {
// yield x // 利用yield使得当前循环不是死循环
// }
// x ++
// }
// }
// let n = count()
// console.log(n.next().value) // 7
// console.log(n.next().value) // 14
// console.log(n.next().value) // 21
// console.log(n.next().value) // 28
// console.log(n.next().value) // 35
// 应用:generator管理异步状态
// 案例:利用Ajax请求a/b/c.json
function ajax(url, callback) {
// 1)创建对象
var xmlhttp // 此处使用var是为了兼容老版本浏览器
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest()
} else { // 兼容早期浏览器
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
}
// 2)发送请求
xmlhttp.open('GET', url, true)
xmlhttp.send()
// 3)服务器响应
xmlhttp.onreadystatechange = function () {
// 4:响应完成 200:成功
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
var obj = JSON.parse(xmlhttp.responseText)
// console.log(obj)
callback(obj)
}
}
}
function request(url) {
ajax(url, res => {
getData.next(res)
})
}
function* gen() {
let res1 = yield request('static/a.json')
console.log(res1)
let res2 = yield request('static/b.json')
console.log(res2)
let res3 = yield request('static/c.json')
console.log(res3)
}
let getData = gen()
getData.next()
4-6 迭代器Iterator
- 是一种接口机制,为各种不同的数据结构统一访问的机制
- 主要供for of消费
- 对于不支持遍历的数据结构=>可bianli
// function makeIterator(arr) {
// let nextIndex = 0
// return {
// next() {
// return nextIndex < arr.length ? {
// value: arr[nextIndex ++],
// done: false
// } : {
// value: undefined,
// done: true
// }
// }
// }
// }
// let it = makeIterator(['a', 'b', 'c'])
// console.log(it.next()) // {value: 'a', done: false}
// console.log(it.next()) // {value: 'b', done: false}
// console.log(it.next()) // {value: 'c', done: false}
// console.log(it.next()) // {value: undefined, done: true}
// 可迭代: 当前对象中有Symbol.iterator
// 1)Array
// let arr = ['a', 'b', 'c']
// let it = arr[Symbol.iterator]()
// console.log(it.next())
// 2)Map
// let map = new Map()
// map.set('name', 'es')
// map.set('age', '6')
// // console.log(map)
// let it = map[Symbol.iterator]()
// console.log(it.next()) // {value: Array(2), done: false}
// console.log(it.next()) // {value: Array(2), done: false}
// console.log(it.next()) // {value: undefined, done: true}
// 原生具备Iterator
// Array Map Set String TypedArray 函数的arguments对象 NodeList对象
// 需求:将course变成可迭代的
let courses = {
allCourse: {
frontend: ['ES', '小程序', 'Vue', 'React'],
backend: ['Java', 'Python', 'SpringBoot'],
webapp: ['Android', 'IOS']
}
}
// 可迭代协议:当前对象中是否含有Symbol.iterator属性
// 迭代器协议:return { next() { return {value, done}}}
// courses[Symbol.iterator] = function () {
// let allCourse = this.allCourse
// let keys = Reflect.ownKeys(allCourse) // 获取allCourse中的key 返回:['frontend', 'backend', 'webapp']
// let values = [] // 保存通过key获取到的值
// // 返回的结构固定
// return {
// next() {
// if (!values.length) { // 每次只取一个值,当values为空的时候进入开始取值
// if (keys.length) { // 没取完
// values = allCourse[keys[0]]
// keys.shift() // 取完一个之后删除 shift()删除数组的第一个元素,并返回删除的结果
// }
// }
// return {
// done: !values.length, // 当取完是values长度为0 !0就是true
// value: values.shift()
// // 注意:此处需要先求done再求values,因为如果先求values会进行删除操作,此时获取到的done信息就不准确l
// }
// }
// }
// }
//
// for (let c of courses) {
// console.log(c)
// }
// generator实现
courses[Symbol.iterator] = function* () {
let allCourse = this.allCourse
let keys = Reflect.ownKeys(allCourse)
let values = []
while (1) {
if (!values.length) {
if (keys.length) {
values = allCourse[keys[0]]
keys.shift()
yield values.shift()
} else {
return false
}
}else{
yield values.shift()
}
}
}
for (let c of courses) {
console.log(c)
}
4-7 模块化Module
- 问题:
- 全局变量污染:变量都放在window对象上
- 变量重名:重名后面的变量会覆盖前面的
- 文件依赖顺序:多个文件之间相互依赖,需要保证一定的加载顺序
- 模块化:将每个js文件看作是一个模块,每个模块通过固定的方式引入,并且通过固定的方式向外暴露指定的内容。 按照js模块化的设想,一个个模块按照其依赖关系组合,最终插入到主程序中。
- 模块化规范
CommonJS:Node.js
可以在服务端实现模块同步加载, 但是局限于服务端,客户端如果同步加载依赖的话时间消耗非常大AMD:require.js
异步模块定义, 允许指定回调函数。采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到所有依赖加载完成之后(前置依赖),这个回调函数才会运行。CMD:sea.js
阿里推出的,CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。AMD 推崇依赖前置、提前执行 CMD推崇依赖就近、延迟执行ES6
使用 import 关键字引入模块,通过 exprot 关键字导出模块。但是由于ES6目前无法在浏览器中执行,所以,只能通过babel将不被支持的import编译为当前受到广泛支持的 require
- export / import / as / export default
module.js export
// 1. 使用
// export const a = 5
// export const b = 'imooc'
// export const sum = (x, y) => x + y
// const obj = {
// name: 'es'
// }
// export {obj}
// 统一导出
// const a = 5
// const b = 'imooc'
// const sum = (x, y) => x + y
// const obj = {
// name: 'es'
// }
//
// class People {
// constructor(name) {
// this.name = name
// }
// showName() {
// console.log(this.name)
// }
// }
//
// export {
// a,
// b,
// sum,
// obj,
// People
// }
// 2. default 注意:每个模块中只能有一个default
// const a = 5 // 先定义在导出
// export default a
// 3. 结合
// const add = (x, y) => x + y
// export default add
// export const str = 'imooc'
// 4.将所有内容放入default对象
const a = 5
const b = 'imooc'
const sum = (x, y) => x + y
const obj = {
name: 'es'
}
class People {
constructor(name) {
this.name = name
}
showName() {
console.log(this.name)
}
}
export default {
a,
b,
sum,
obj,
People
}
import
// 1.用法
// 导入,注意导入模块和导出模块名称相同,多个可用,隔开
// import {
// a as aa, // as起别名
// b,
// sum,
// obj,
// People
// } from './module'
// console.log(aa)
// console.log(b)
// console.log(sum(1, 2))
// console.log(obj)
// let p1 = new People('mys')
// p1.showName()
// 2.default
// import aa from './module'
// console.log(aa)
// 3.结合使用
// import add, {str} from './module'
// console.log(add(1, 2), str) // 3 imooc
// 4.将所有内容放入default对象
// 1)
// import mod from './module'
// console.log(mod.a) // 5
// console.log(mod.b) // imooc
//2)
import * as mod from './module'
console.log(mod)
console.log(mod.default.a) // 5
console.log(mod.default.b) // imooc
5 ECMAScript2016(ES7)之数组&数值拓展
5-1 数组扩展:Array.prototype.includes()
- Array.prototype.includes(searchElement, fromIndex) 实例方法
- includes & indexOf
// includes:检查数组里面是否包含某个值
// const arr = ['es5', 'es6', 'es7']
// /**
// * 参数:
// * 第一个参数:要搜索的值
// * 第二个参数:搜索开始的索引 负数:从倒数第index开始往后找
// */
// console.log(arr.includes('es7')) // true
// console.log(arr.includes('es8')) // false
// console.log(arr.includes('es6', 2)) // false
// console.log(arr.includes('es6', - 2)) // true
//
// console.log(arr.indexOf('es7')) // 2
// console.log(arr.indexOf('es7') > -1) // true
// 1. indexOf和includes只能判断基本类型的值,不能判断引用类型的值
// const arr = ['es5', ['es6', 'es7'], 'es8']
// console.log(arr.includes(['es6', 'es7'])) // false
// console.log(arr.indexOf(['es6', 'es7'])) // -1
const arr = ['es5', 'es6', NaN, 'es7', 2]
// 2. indexOf不能检查NaN
console.log(arr.includes(NaN)) // true
console.log(arr.indexOf(NaN)) // -1
// 3. includes和indexOf内部判断是严格相等,即 ===
console.log(arr.includes('2')) // false
console.log(arr.indexOf('2')) // -1
// 4.使用
// NaN => includes
// 只关心值是都存在,不关心其存在的位置 => includes
// 判断值出现的索引下标 => indexOf
5-2 数值扩展:幂运算符
- 幂运算符:**
- 等同于Math.pow()
// 2^10 -> 1024
// 指数运算
function pow(x, y) {
let res = 1
for (let i = 0; i < y; i ++) {
res *= x
}
return res
}
console.log(pow(2, 10)) // 1024
console.log(Math.pow(2, 10))
// es7
console.log(2 ** 10) // 1024
6 ECMAScript2017(ES8)之异步编程&对象拓展
6-1 异步编程解决方案Async Await
- es6 Promise / Generator
- es7 async await
// function foo() {
// return 'imooc'
// }
// console.log(foo()) // imooc
// 1. async返回的是Promise对象
// async function foo() {
// return 'imooc'
// }
// console.log(foo()) // Promise{<fulfilled>: 'imooc'}
// 2. async/wait
// 需求:异步操作1执行完成之后再输出2
// function timeout() {
// return new Promise(resolve => {
// setTimeout(() => {
// // console.log(1)
// resolve(1)
// }, 2000)
// })
// }
// async function foo() {
// const res = await timeout() // resolve的参数就是timeout()的返回值,await需要等其语句执行完毕才执行后面的内容
// console.log(res)
// console.log(2)
// }
// foo() // 1 2
// function timeout() {
// return new Promise((resolve, reject) => {
// // resolve('success')
// reject('fail')
// }, 2000)
// }
// async function foo() {
// return await timeout()
// }
// foo().then(res => {
// console.log(res)
// }).catch(err => {
// console.log(err)
// })
// 伪代码 应用
// async function request() {
// const data = await axios.get('http://www.xxx.com')
// console.log(
// data
// )
// }
//async wait实现ajax请求依次访问a.json b.json c.json
import ajax from './ajax'
function request(url) {
return new Promise(resolve => {
ajax(url, res => {
resolve(res)
})
})
}
async function getData() {
const res1 = await request('static/a.json')
console.log(res1) // {a: 'A' }
const res2 = await request('static/b.json')
console.log(res2) // {b: 'B'}
const res3 = await request('static/c.json')
console.log(res3) // {c: 'C'}
}
getData()
// 异步操作:
// 回调 Promise Generator async(最常用)
6-2 对象扩展:Object.values(),Object.entries()
- Object.values()
- Object.entries()
const obj = {
name: 'mys',
age:'18'
}
// 以前得到key
// console.log(Object.keys(obj)) // ['name', 'age']
// const res = Object.keys(obj).map(key => obj[key])
// console.log(res) // ['mys', '18']
console.log(Object.values(obj)) // ['mys', '18']
console.log(Object.entries(obj))
// 输出:
// [Array(2), Array(2)]
// 0: (2) ['name', 'mys']
// 1: (2) ['age', '18']
for (let [key, value] of Object.entries(obj)) {
console.log(`${key}:${value}`) // name mys age 18
}
6-3 对象属性描述:Object.getOwnPropertyDescriptors()
- Object.getOwnPropertyDescriptors()
value writable configurable enumerable
// const obj = {
// name: 'mys',
// age: 18
// }
// const desc = Object.getOwnPropertyDescriptors(obj)
// console.log(desc)
// 输出:
// age: {value: 18, writable: true, enumerable: true, configurable: true}
// name: {value: 'mys', writable: true, enumerable: true, configurable: true}
/**
* value:当前对象属性的默认值
* writable:是否可修改
* enumerable:当前属性是否可以通过for in遍历
* configurable:当前对象属性能否用delete进行删除
*/
const obj = {}
Reflect.defineProperty(obj, 'name', {
value: 'mys',
writable: true,
enumerable: true,
configurable: true
})
console.log(obj)
delete obj.name
6-4 字符串扩展:String.prototype.padStart(),String.prototype.padEnd()
- String.prototype.padStart()
- String.prototype.padEnd()
const str = 'imooc'
// padStart从开始填充:第一个参数:填充之后目标参数的长度;第二个参数:用什么填充(可选,默认空格填充)
console.log(str.padStart(8, 'x')) // xxximooc
// padEnd从结尾填充
console.log(str.padEnd(8, 'x')) // imoocxxx
// 应用场景1:输出当前日期 yyyy-mm-dd
const now = new Date()
const year = now.getFullYear()
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const day = now.getDate().toString().padStart(2, '0')
console.log(`${year}-${month}-${day}`) // 2022-04-21
// 应用场景2:手机号加密,只显示后四位
const tel = '13012345678'
const newTel = tel.slice(-4).padStart(11, '*')
console.log(newTel) // *******5678
// 应用场景3:时间戳 伪代码
// console.log(new Date().getTime()) // 13位 ms
// timestamp.padEnd(12, '0')
6-5 尾逗号Trailing commas
function foo(
a,
b,
c, // ES8 允许函数的最后一个参数有尾逗号,方便修改参数
) {
console.log(a, b, c)
}
foo(1, 2, 3)
7 ECMAScript2018(ES9)之异步迭代&对象操作升级
7-1 异步迭代:for await of
- for await of
- Symbol.asyncIterator
// 迭代需要遵循:可迭代协议;迭代器协议
// 以前的同步迭代
// const arr = ['es6', 'es7', 'es8']
// arr[Symbol.iterator] = function () {
// let nextIndex = 0
// return {
// next() {
// return nextIndex < arr.length ? {
// value: arr[nextIndex ++],
// done: false
// } : {
// value: undefined,
// done: true
// }
// }
// }
// }
// for (let item of arr) {
// console.log(item)
// }
// 异步迭代
function getPromise(time) {
return new Promise(((resolve, reject) => {
setTimeout(() => {
resolve({
value: time,
done: false
})
}, time)
}))
}
const arr = [getPromise(1000), getPromise(2000), getPromise(3000)]
arr[Symbol.asyncIterator] = function () {
let nextIndex = 0
return {
next() {
return nextIndex < arr.length ? arr[nextIndex++] :
Promise.resolve({
value: undefined,
done: true
})
}
}
}
async function test() {
for await (let item of arr) {
console.log(item)
}
}
test() // 1000 2000 3000
7-2 正则表达式扩展:dotAll,具名组匹配,后行断言
// dot 点 匹配任意单个字符,不能匹配多字节的Unicode和行终结符,eg:\n \r
// const reg = /./
// console.log(reg.test('5')) // true
// console.log(reg.test('\n')) // false
// console.log(reg.test('\u{2028}')) // false
// 1. dotAll
// const reg = /./s
// console.log(reg.test('5')) // true
// console.log(reg.test('\n')) // true
// console.log(reg.test('\u{2028}')) // true
// 修饰符:g全局 i:不区分大小写 m:跨行匹配 y:粘性 u:unicode s:让.能够匹配任意单个字符
// 2. 具名组匹配
// const date = /(\d{4})-(\d{2})-(\d{2})/.exec('2022-04-21')
// console.log(date[1]) // 2022
// console.log(date[2]) // 04
// console.log(date[3]) // 21
// console.log(date) // ['2022-04-21', '2022', '04', '21', index: 0, input: '2022-04-21', groups: undefined]
//
// const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
// console.log(reg.exec('2022-02-01')) // ['2022-02-01', '2022', '02', '01', index: 0, input: '2022-02-01', groups: {…}]
// // groups: {year: '2022', month: '02', day: '01'}
// const groups = reg.exec('2022-02-01').groups
// // const year = groups.year
// // const month = groups.month
// // const day = groups.day
// const {year, month, day} = groups
// console.log(year, month, day) // 2022 02 01
// 3.后行断言 先行断言
// 先行断言:匹配后面跟着script的ecma
const str = 'ecmascript'
console.log(str.match(/ecma(?=script)/)) // ['ecma', index: 0, input: 'ecmascript', groups: undefined]
// 后行断言:匹配前面是ecma的script
console.log(str.match(/(?<=ecma)script/)) // ['script', index: 4, input: 'ecmascript', groups: undefined]
7-3 对象扩展:Rest & Spread
// const arr1 = [1, 2, 3]
// const arr2 = [4, 5, 6]
// const arr3 = [...arr1, ...arr2]
// console.log(arr3) // [1, 2, 3, 4, 5, 6]
// const obj1 = {
// name: 'mys',
// age: 18
// }
// const obj2 = {
// school: 'imooc'
// }
// // 克隆对象
// const obj3 = {
// ...obj1
// }
// obj1.age = 20
// console.log(obj3) // {name: 'mys', age: 18} ...克隆对象是一个新的对象
//
// // 合并对象
// const obj4 = {...obj1, ...obj2}
// console.log(obj4) // {name: 'mys', age: 20, school: 'imooc'} 后面的会覆盖前面的
// 剩余属性
const obj1 = {
name: 'mys',
age: 18,
school: 'imooc',
course: 'es'
}
const {name, age, ...rest} = obj1 // ...rest表示剩余元素,要放在最后
console.log(name) // mys
console.log(age) // 18
console.log(rest) // {school: 'imooc', course: 'es'}
7-4 Promise扩展:Promise.prototype.finally()
new Promise(((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}).finally(() => {
console.log('finally') // 成功失败都很进入
})
// 应用场景1:用户加载数据时的等待框消失操作可以写在finally中,因为不管加载成功还是失败,都会去除等待框
// 应用场景2:关闭数据库的连接
7-5 字符串扩展:放松模板字符串文字限制
放松了模板字符串的转义序列的语法限制
// 带标签的模板字符串
// const foo = (a, b, c, d) => {
// console.log(a) // ['这是', ',年龄是', '岁', raw: Array(3)]
// console.log(b) // mys
// console.log(c) // 18
// console.log(d) // undefined
// }
// const name = 'mys'
// const age = 18
// foo`这是${name},年龄是${age}岁`
const foo = arg => {
console.log(arg)
}
foo`\u{61} and \u{62}` // ['a and b', raw: Array(1)]
foo`\u{61} and \uunicode` // [undefined, raw: Array(1)]
// let str = `\u{61} and \uunicode` //报错 只能在标签中使用
8 ECMAScript2019(ES10)之效率再提升
8-1 对象扩展:Object.fromEntries()
// const obj = {
// name: 'mys',
// age: 18
// }
// const entries = Object.entries(obj) // 将对象转换成数组形式
// console.log(entries) // ['name', 'mys'] ['age', 18]
//
// // es10
// const fromEntries = Object.fromEntries(entries)
// console.log(fromEntries) // {name: 'mys', age: 18} 返回成对象的形式
// // Object.entries和Object.fromEntries作用相反
// 应用场景1:将map结构转成对象结构
// const map = new Map()
// map.set('name', 'mys')
// map.set('age', 18)
// console.log(map) // {'name' => 'mys', 'age' => 18}
// const fromEntries = Object.fromEntries(map)
// console.log(fromEntries) // {name: 'mys', age: 18}
// 应用场景2:数组方法多,可以先转成数组,调用一些方法实现了功能之后,再转回原来的类型
const course = {
math: 90,
english: 85,
chinese: 80
}
// Object.entries(course):转成数组,调用数组的相关方法
const res = Object.entries(course).filter(([key, val]) => val > 80);
console.log(res) // ['math', 90] ['english', 85]
// 数组转对象
console.log(Object.fromEntries(res)) // {math: 90, english: 85}
8-2 字符串扩展:String.prototype.trimStart() ,String.prototype.trimEnd()
const str = ' imooc '
// 正则
// 去掉前面的空格
console.log(str.replace(/^\s+/g, '')) // 'imooc '
// 去掉后面的空格
console.log(str.replace(/\s+$/g, '')) // ' imooc'
// es10
// 去掉前面的空格
console.log(str.trimStart())
console.log(str.trimLeft())
// 去掉后面的空格
console.log(str.trimEnd())
console.log(str.trimRight())
// 去掉前后所有空格
console.log(str.trim())
8-3 数组扩展:Array.prototype.flat(),Array.prototype.flatMap()
// const arr = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]]
// // 扁平化输出 arr.flat() 默认参数为1,数字表示深度
// console.log(arr.flat()) // [1, 2, 3, 4, 5, 6, Array(4)]
// console.log(arr.flat(3)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
// // Infinity直接输出一维数组(但是不推荐)
// console.log(arr.flat(Infinity)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
arr = [1, 2, 3, 4, 5]
// const res = arr.map(x => x + 1) // [2, 3, 4, 5, 6]
// const res = arr.map(x => [x + 1]).flat() // [2, 3, 4, 5, 6]
// flat和map的结合
const res = arr.flatMap(x => [x + 1]) // [2, 3, 4, 5, 6]
console.log(res)
8-4 修订Function.prototype.toString()
返回源代码中的实际文本片段
// toString()
function foo() {
console.log('imooc')
}
console.log(foo.toString())
// 输出
// function foo() {
// console.log('imooc');
// }
8-5 可选的Catch Binding
省略catch绑定的参数和括号
const validJSON = json => {
try {
JSON.parse(json)
return true
} catch{ // 参数e可以不写
return false
}
}
const json = '{"name": "mys", "age":"18"}'
console.log(validJSON(json)) // true
8-6 JSON扩展:JSON superset,JSON.stringify()增强能力
// JSON 超集
// \u2029段分隔符 \u2028行分隔符 不会报错,以前会报错
// eval('var str = "imooc";\u2029 function foo(){return str}')
// console.log(foo())
// 0xD800 ~ 0xDfff
console.log(JSON.stringify('\uD83D\uDE0E')) // emoji
console.log(JSON.stringify('\uD83D')) // "\ud83d" 不是一个字符原样输出
8-7 Symbol扩展:Symbol.prototype.description
// Symbol
const s = Symbol('imooc')
console.log(s) // Symbol(imooc)
s.description = 'es'
console.log(s.description) // imooc 只读
const s2 = Symbol()
console.log(s2.description) // undefined
9 ECMAScript2020(ES11)之新特性初探
9-1 全局模式捕获:String.prototype.matchAll()
// 需求:获取div中的内容
const str = `
<html>
<body>
<div>第一个div</div>
<p>这是p</p>
<div>第二个div</div>
<span>这是span</span>
</body>
</html>
`
// 1. exec g
// function selectDiv(regExp, str) {
// let matches = []
// while (true) {
// // console.log(regExp.lastIndex)
// const match = regExp.exec(str);
// if (match === null) {
// break
// }
// matches.push(match[1]) // 可以选择子结果
// }
// return matches
// }
// const regExp = /<div>(.*)<\/div>/g
// const res = selectDiv(regExp, str);
// console.log(res) // ['第一个div', '第二个div']
// 2. match 匹配所有包含的结果 (不满足需求)
// console.log(str.match(regExp)) // ['<div>第一个div</div>', '<div>第二个div</div>']
// 3. replace
// function selectDiv(regExp, str) {
// let matches = []
// str.replace(regExp, (all, first) => {
// matches.push(first)
// })
// return matches
// }
// const regExp = /<div>(.*)<\/div>/g
// const res = selectDiv(regExp, str)
// console.log(res) // ['第一个div', '第二个div']
// 4.matchAll
function selectDiv(regExp, str) {
let matches = []
for (let match of str.matchAll(regExp)) {
matches.push(match[1])
}
return matches
}
const regExp = /<div>(.*)<\/div>/g
const res = selectDiv(regExp, str)
console.log(res) // ['第一个div', '第二个div']
9-2 动态导入:Dynamic import()
动态导入
按需加载
// 动态导入的按需加载
const oBtn = document.getElementById('#btn')
oBtn.addEventListener('click', () => {
import('./ajax').then(mod => {
// console.log(mod) // Module{__esModule: true, Symbol(Symbol.toStringTag): 'Module', default: ƒ}
mod.default('static/a.json', res => {
console.log(res) // {a: 'A'}
})
})
})
9-3 新的原始数据类型:BigInt
const max = 2 ** 53
console.log(max) // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
const bigInt = 9007199254740993n
console.log(bigInt) // 9007199254740993n
console.log(typeof bigInt) // bigInt
console.log(1n == 1) // true
console.log(1n === 1) // false
// 扩大了数的范围
const bigInt2 = BigInt(9007199254740993n)
console.log(bigInt2) // 9007199254740993n
const num = bigInt + bigInt2
console.log(num.toString()) // 18014398509481986
9-4 Promise扩展:Promise.allSettled()
// 场景:现在页面上有三个请求,分别请求不同的数据,如果一个接口服务异常,整个都是失败的,都无法渲染出数据
// Promise.all() 一个失败,全部失败
// Promise.all([
// Promise.resolve({
// code: 200,
// data: [1, 2, 3]
// }),
// Promise.reject({
// code: 500,
// data: []
// }),
// Promise.resolve({
// code: 200,
// data: [7, 8, 9]
// }),
// ]).then(res => {
// console.log(res)
// console.log('成功')
// }).catch(err => {
// console.log(err)
// console.log('失败')
// })
// Promise.allSettled()
// 在并发任务中,无论一个任务正常或者异常,都会返回对应的的状态
Promise.allSettled([
Promise.resolve({
code: 200,
data: [1, 2, 3]
}),
Promise.reject({
code: 500,
data: []
}),
Promise.resolve({
code: 200,
data: [7, 8, 9]
}),
]).then(res => {
// console.log(res)
// console.log('成功')
const data = res.filter(item => item.status === 'fulfilled')
console.log(data)
}).catch(err => {
console.log(err)
console.log('失败')
})
// 输出
// 0: {status: 'fulfilled', value: {…}}
// 1: {status: 'fulfilled', value: {…}}
// 即使有一个失败,也能得到其他成功的状态
9-5 全局对象:globalThis
提供了一个标准的方式获取不同环境下的全局对象
// node:global
// web: window self
// self.setTimeout
self.setTimeout(() => {
console.log('2022')
}, 1000)
const getGlobal = () => {
if (typeof self !== 'undefined') {
return self
}
if (typeof window !== 'undefined') {
return window
}
if (typeof global !== 'undefined') {
return global
}
throw new Error('无法找到全局对象')
}
const global = getGlobal()
console.log(global) // window
// 与上面函数的作用相同
console.log(globalThis) // window
9-6 可选链:Optional chaining
const user = {
address: {
street: 'xxx街道',
getNum() {
return '80号'
}
}
}
// 以前的判断:先是否存在,再获取需要的值
const street = user && user.address && user.address.street
console.log(street)
const num = user && user.address && user.address.getNum && user.address.getNum()
console.log(num)
// 可选链 ?.不能分开
const street2 = user?.address?.street
console.log(street2)
const num2 = user?.address?.getNum?.()
console.log(num2)
9-7 空值合并运算符:Nullish coalescing Operator
// 需求:null undefined时取默认值
// 以前:
// const b = ''
// const a = b || 5
// console.log(a) // 5 b不是null
// // 或undefined也是默认值5
// es2020
const b = ''
const a = b ?? 5
console.log(a) // ''
// 只有传递null和undefined时才是默认值5
10 ES新特性在Vue实战中的应用
11 Webpack构建环境
未完…