环境准备
安装环境
安装node
npx es10-cli create projectName
cd projectName
npm start
配置插件
ES6
作用域
var
全局变量 window的属性
var a = 1; // 全局变量
abcd = 1234; // 作为window的属性,window是全局对象
没有用var定义的变量是作为window的属性,不是真正的意义的全局变量,只是因为window是全局对象,所有具有全局属性。在函数内部没有使用var定义的变量,不是局部函数作用域,是具备全局作用域。
存在变量提升
函数作用域
this
动态作用域
a = '123'
function test(){
console.log(this.a);
}
test()
test.bind({a: 100})()
let
块状作用域
const
数组
问题一
1. ES5中数据遍历有多少种方法?
2. 他们有什么优势和缺点?
- for
- forEach
- every
- for..in
/**
* for循环
* continue/break
* break: 退出当前循环
* continue:
*/
const arr = [1, 2, 3, 4, 5]
for (let i = 0; i < arr.length; i++) {
if(arr[i] == 2){
break
}
console.log(arr[i]);
}
// forEach
// 更加简洁
// 不支持 continue、break,所有元素都将遍历
arr.forEach(item => {
// if(item === 2){
// break
// }
console.log(item);
})
// every
/**
* 能不能继续遍历,取决于函数体的返回值,函数体的默认返回值是false
*/
arr.every(item => {
// if(item === 3){
// return false
// }
// console.log(item);
// return true
if(item===2){
// 不做任何处理,就可跳过
}else {
}
return true
})
// for in
/**
* for in为 Object来做遍历的
* 可以遍历数组,但是会有瑕疵
* 数组是对象的一种,所以for..in能遍历数组,数组也是可遍历的
* 一个对象是不是可遍历的,不是看它是不是数组或对象
* 支持continue/break
*/
arr.a = 8
for (const index in arr) {
// index的类型是字符串
// if(index === 2) {
// continue
// }
if(index == 2) {
continue
}
console.log(index, arr[index]);
}
// 0 1
// 1 2
// 2 3
// 3 4
// 4 5
// a 8
// 0 1
// 1 2
// 3 4
// 4 5
// a 8
- ES6的循环方法
- for...of
var arr = [1,2,3,4,5]
/**
* for..of
* 一个对象是不是可遍历的,不是看它是不是数组或对象
* 除了数组和对象,可以遍历的对象使用 for...of
* 自定义数据结构
*/
for (const item of arr) {
console.log('for....of');
console.log(item);
}
const Price = {
A: [1.5, 2.3, 4.5],
B: [3, 4, 5],
C: [0.5, 0.8, 1.2]
}
for (const key in Price) {
console.log(key, Price[key]);
}
问题二
1. ES5中伪数组转换成数组该怎么办?
2. ES6中如何做呢?
伪数组
伪数组:具备以下两个特性
- 特征一:
- 按照索引方式存储数据的
- 特征二:
- 具备一个length属性
{0:'a', 2: 'b',length: 2}
- 具备一个length属性
ES5
// 伪数组
/**
* 像数组,但是又不能使用数组的方法
*/
// ES5伪数组的转换
// arguments在函数体中使用
let args = [].slice.call(arguments) // collection arguments ===> 数组的转换
let imgs = [].slice.call(document.querySelectorAll('img')) // NodeList
ES6
// ES6伪数组的转换方法
// Array.prototype.from
let args = Array.from(arguments)
let imgs = Array.from(document.querySelectorAll('img'))
Array.from
/**
* Array.from(arrayLike, mapFn, thisArg)
* mapFn
*/
// let array = Array(5)
/**
* {length: 5}:伪数组,长度为5,数据为空
* 伪数组:具备以下两个特性
* 特征一:
* 按照索引方式存储数据的
* 特征二:
* 具备一个length属性
* {0:'a', 2: 'b',length: 2}
*/
let array = Array.from({length: 5}, () => {
return 1
})
console.log(array); // [1, 1, 1, 1, 1]
问题三
1. ES5中创建一个新数组该怎么做?
2. ES6中如何做呢?
ES5
// 生成新数组
let array1 = Array(5)
let array2 = [1,2,3,4,5]
let array3 = [,,,,,]
console.log(array3.length);
ES6
Array.of、Array.fill
// Array.prototype.of()
let array1 = Array.of(1, 2, 3, 4, 5)
console.log(array1);
// Array.prototype.fill() 填充
let array2 = Array(5).fill(1)
console.log(array2);
/**
* Array.fill(value, start, end)
* start: 默认0
* end: 默认为数组的length,不包含
*/
let array3 = [1,2,3,4,5]
console.log(array3.fill(8, 2, 4)); // 不包含位置4
问题四
1. ES5中如何查找一个元素呢?
2. ES6中如何做呢?
ES5
map、filter、for...遍历查找
/**
* ES5
*/
// map、filter、for...遍历查找
// filter 1.是否存在 2.所有满足条件的元素筛出来
// 缺点:数组很庞大,验证存在,效率低
let array = [1,2,3,4,5]
let arr = array.filter(item => {
return item === 3
})
console.log(arr);
ES6
Array.find、Array.findIndex
// Array.prototype.find
/**
* Array.find
* 不关注所有的值
* 关注的是有或没有,返回的是满足条件的第一个值,不满足则返回undefined
*/
let find = array.find(item => {
return item % 2 === 0
})
console.log(find);
// Array.prototype.findIndex
/**
* Array.find
* 不关注所有的值
* 关注的是有或没有,返回的是满足条件的第一个值的索引,不满足则返回undefined
*/
let findIndex = array.findIndex(item => {
return item % 2 === 0
})
console.log(findIndex);
- 遍历
- 转换
- 生成
- 查找
练习一
类
构造函数 初始化 传参 typeof 类 === function
let _age = 3
class Animal {
constructor(type) {
this.type = type
}
eat() {
console.log('animals eat food');
}
get age() {
return _age
}
set age(val) {
if (val < 7) {
_age = val
}
}
}
let cat = new Animal('cat')
cat.eat()
console.log('cat.age', cat.age);
cat.age = 4
console.log('cat.realAge', cat.age);
/**
* getter
* setter
* ES6属性的保护
* 控制读写,有条件的读写
*/
问题----方法
1. ES5中怎么操作一个方法?
2. ES6是如何做的呢?
ES5
// ES5
let Animal = function (type) {
this.type = type
// this.eat = function () {
// console.log('animal eat');
// }
}
Animal.prototype.eat = function () {
console.log(this); // this是dog的实例对象
Animal.walk()
console.log('prototype eat');
}
// 静态方法walk挂载在类上
Animal.walk = function () {
console.log('i am walking');
}
let dog = new Animal('dog')
dog.eat()
ES6
class Animal {
constructor(type) {
this.type = type
}
eat() {
Animal.walk()
console.log('animals eat food');
}
// static表明是静态方法
static walk() {
console.log('animals can walk');
}
}
let dog = new Animal('dog')
Animal.walk()
dog.eat()
什么时候用实例对象的方法,什么时候用类的静态方法
根据业务场景、代码设计,本身两种没有优点、缺点
-
如果方法依赖于对象的某些属性或方法,建议用实例对象的方法
-
不涉及实例对象的内容,建议用类的静态方法
问题---继承
1. ES5中怎么继承另一个类?
2. ES6是如何做的呢?
ES5
let Animal = function (type) {
this.type = type
}
Animal.prototype.eat = function() {
console.log('Animal can eat');
}
let cat = new Animal('cat')
cat.eat()
let Dog = function () {
// 初始化父类的构造函数
// call改变this的指向,将this指向dog的实例对象上
Animal.call(this, 'dog') // 一部分内容的继承
}
// Dog.prototype的地址指针指向Animal.prototype
Dog.prototype = Animal.prototype
let dog = new Dog()
dog.eat()
ES6
class Animal {
constructor(type) {
this.type = type
}
eat() {
Animal.walk()
console.log('animals eat food');
}
static walk() {
console.log('animals can walk');
}
}
class Dog extends Animal {
constructor(type) {
super(type) // super写在constructor第一行
this.age = 3
}
}
let dog = new Dog('dog')
dog.eat()
console.log(dog.age);
Function
-
- 默认值
-
- 不确定参数
-
- 箭头函数
问题
1. ES5中怎么处理函数参数的默认值?
2. ES6是如何做的呢?
ES5
function f (x, y, z) {
if (y === undefined) {
y = 7
}
if (z === undefined) {
z = 8
}
return x + y + z
}
console.log(f(1, 8, 43));
ES6
/**
* 1. undefined会跳过有默认值的参数
* 2. 有默认值的尽量往前面写
* 3. 参数不仅可以指定默认值,也可以还前面参数的表达式
*/
function add(a, b = 4, c = 5) {
return a + b + c
}
let num = add(3, undefined, 6) // undefined会跳过有默认值的参数
console.log(num);
function add1(a, b = 4, c = a + b) {
return a + b + c
}
let num1 = add1(3, undefined) // undefined会跳过有默认值的参数
console.log(num1);
问题
1. ES5中怎么处理不确定参数的问题?
2. ES6是如何做的呢?
ES5
使用arguments
// 输入的所有参数的求和(动态参数个数)
function sum() {
let num = 0;
// Array.prototype.forEach.call(arguments, item => {
// num += item*1
// })
Array.from(arguments).forEach(item => {
num += item * 1
})
console.log(num);
return num
}
sum(1, 2, 3)
ES6
ES6在函数内部禁止使用arguments,剩余参数(Rest parameter:
...)
function sum(base, ...nums) {
// base === 第一个形参
// ... 剩余参数
let num = 0;
nums.forEach(item => {
num += item * 1
})
return num + base * 1
}
console.log(sum(2, 3, 4));
参数确定,从数组中取出对应索引赋值
spread扩展运算符
ES5
//
function sum1(x = 1, y = 2, z = 3) {
return x + y + z
}
console.log(sum1.apply(this, [2, 3, 4]));
ES6
// 参数确定,从数组中取出对应索引赋值
function sum1(x = 1, y = 2, z = 3) {
return x + y + z
}
console.log(sum1(...[2, 3, 4]));
ES6箭头函数
- ES6中的箭头函数是什么? ()=> {}
/**
* 箭头函数
* 小括号可以省略的情况:有且仅有一个参数的情况
* 花括号可以省略的情况:返回的表达式;字面量对象
*/
let Fn = () => {
console.log('箭头函数');
}
Fn()
let sum = (x, y, z) => x + y + z;
console.log(sum(1, 2, 3));
let getObject = (x, y, z) => ({
x, y, z
});
console.log(getObject(1, 2, 3));
let getObject1 = (x, y, z) => {
return {
x, y, z
}
};
console.log(getObject1(1, 2, 3));
this指向
普通函数被谁调用就指向谁
let test = {
name: 'test',
say: function() {
console.log(this.name); // test
}
}
test.say()
箭头函数的this指向是书写的上下文,而不是调用执行的时候
let test = {
name: 'test',
say: () => {
console.log(this.name); // undefined
console.log(this) // {},为什么不是window,因为webpack构建问题,构建的时候执行了eval, eval将代码最外层的作用域指向了空对象
}
}
test.say()
eval,可以将字符串当成代码执行
练习2
Object
问题
1. ES5中Object属性能简写吗?
2. ES6可以吗?
key--value简写
ES5
let x = 1;
let y = 2;
let obj = {
x: x,
y: y
}
ES6
let x = 1;
let y = 2;
let obj = {
x,
y
}
ES6只是简写,本质还是key--value
变量做为属性
ES5
let x = 1;
let y = 2;
let z = 'key'
let obj = {
x: x,
y: y,
hello: function () {
console.log('ES5-HELLO')
}
}
obj[z] = 5
console.log(obj);
ES6
let x = 1;
let y = 2;
let z = 'key'
let obj = {
x,
y,
[z]: 6,
* hello () {
console.log('ES6-HELLO')
}
}
console.log(obj);
obj.hello() // 不输出,因为没有被执行
* hello () {
console.log('ES6-HELLO')
}
====>
// generator
function * functionName(){}
ES5中不允许添加异步函数,ES6中允许:在函数前面加 *
Set
- add 添加
- delete 删除指定数据
- clear 全部清空
- has 查找是否有某个数据
- size 统计数据
- forEach,for..of 遍历
/**
* Set
* 存储的数据是唯一的,不允许重复,重复会被过滤
*/
// let s = new Set()
// 初始化数据
/**
* 参数
* 可遍历的对象
*/
// let s = new Set([1, 2, 3, 4])
let s = new Set()
/*
add 添加
delete 删除指定数据
clear 全部清空
has 查找是否有某个数据
size 统计数据
forEach,for..of 遍历
*/
// s.add('hello')
// s.add('goodbye')
s.add('hello').add('goodbye')
console.log(s);
// s.delete('hello')
// console.log(s);
// s.clear()
// console.log(s);
console.log(s.has('hello'));
console.log(s.size);
console.log(s.keys()); // SetIterator {'hello', 'goodbye'} 遍历器
console.log(s.values()); // SetIterator {'hello', 'goodbye'} 遍历器
console.log(s.entries()); // SetIterator {'hello' => 'hello', 'goodbye' => 'goodbye'}
s.forEach(item => {
console.log(item);
})
for (const item of s) {
console.log(item);
}
Map
- set 添加/修改
- delete 指定key去删除
- clear 清空
- size 统计数据
- has 查找有没有某个key
- get 查找某个key的value
- forEach 循环 参数 value, key
- for...of 循环
问题
1. ES6中Map是什么,解决什么问题,怎么用?
2. new Map
// let map = new Map()
/**
* 参数
* entry Object
* 可以传一个可遍历的对象
* Map中的key可以是任意值
* Object的key只能是字符串
*/
// let map = new Map([1, 2, 3]) // 报错
// [1, 2] ===> key: 1, value: 2
// let map = new Map([[1, 2]]) // Map(1) {1 => 2}
let map = new Map()
console.log(map);
/**
* set 添加/修改
* delete 指定key去删除
* clear 清空
* size 统计数据
* has 查找有没有某个key
* get 查找某个key的value
* forEach 循环 参数 value, key
* for...of 循环 遍历内容的顺序和初始化的顺序相关,和key的大小无关
*/
map.set(1, 2)
map.set(1, 3)
map.set(2, 2)
map.set('name', 'liu')
map.set('age', 19)
console.log(map);
map.delete('name')
// map.clear()
console.log(map);
console.log(map.size);
console.log(map.has(1)); // 是否存在key为1的
console.log(map.get(1)); // 获取key为1的value
console.log(map.keys()); // MapIterator {1, 2}
console.log(map.values()); // MapIterator {3, 2}
console.log(map.entries()); // MapIterator {1 => 3, 2 => 2}
map.forEach((value, key) => {
console.log(value, key);
})
for (const [key, value] of map) {
console.log(key, value);
}
性能方面:Map更优于Object
问题
1. ES5中怎么把一个对象复制到另一个对象上?
2. ES6怎么做呢?
// 复制
const target = { a: { b: { c: { d: 1 } }, e: 2, f: 3, h: 0 } }
// const source = { b: 4, c: 5}
const source = { a: { b: { c: { d: 3 } }, e: 4, f: 5 } }
// ES5 循环逐个copy
/***
* ES6
* Object.assign(target, source)
* 复制没有的,而原有的也被删除了
* assign:浅复制,对于不是引用类型的做替换, 对于引用类型的把地址做替换
*/
Object.assign(target, source)
console.log(target);
console.log(source);
练习三
WeakSet
存储的数据只能是对象
WeakMap
key: 只允许接收对象类型
正则
问题
1. ES6中的y修饰符是什么含义?
2. ES5支持y修饰符吗?
const s = 'aaa_aa_a'
const req = /a+/g
const req1 = /a+/y
console.log(req.exec(s)); // aaa
const s = 'aaa_aa_a'
const req = /a+/g
const req1 = /a+/y
console.log(req.exec(s)); // aaa
console.log(req1.exec(s)); // aaa
console.log(req.exec(s)); // aa
console.log(req1.exec(s)); // null
/**
* y: sticky 粘连
* 第一次 req1.exec(s) 匹配 aaa
* 第二次 req1.exec(s)
* 粘连的是剩余的数据从第一个开始就严格匹配,连续匹配 ====> ^$
*/
/**
* g
* 第一次 req.exec(s) 匹配 aaa
* 第二次 req.exec(s)
* 不需要连续匹配
*/
问题
1. ES5如何在正则中处理中文问题,如果是多个字节呢?
2. ES6有什么高招?
ES5中没有什么办法
- Unicode u修饰符 \uffff
帮你正确处理,大于 \uffff 四节点的单个字符
let s = '𠮷'
let s2 = '\uD842\uDFB7' // 四个字节
console.log(/^\uD842/.test(s2)); // true
console.log(/^\uD842/u.test(s2)); // false
console.log(/^.$/.test(s)); // false
console.log(/^.$/u.test(s)); // true
// unicode码点20BB7
console.log(/\u{20BB7}/u.test(s)); // true
console.log(/\u{61}/u.test('a')); // true
console.log(/[a-z]/i.test('\u212A')) // false
console.log(/[a-z]/iu.test('\u212A')) // true
问题
1. ES5从赋值的数据结构中提取数据是如何做的?
2. ES6有更优雅便捷的方式吗?
ES5
let arr = ['好好', '学习']
let firstWord = arr[0]
let surWord = arr[1]
ES6
let [firstWord, surWord] = arr;
console.log(firstWord, surWord);
let user = {
name: 'ss',
surname: 'wwjj'
}
for (const item in user) {
console.log(item);
}
for (const [key, value] of Object.entries(user)) {
console.log(key, value);
}
let options = {
size: {
width: 100,
height: 200
},
items: ['Cake', 'Donut'],
extra: true
}
let { size: { width: width2, height }, items: [a, b] } = options
console.log(width2, height, a, b); // 100 200 'Cake' 'Donut'
2.ES6 JavaScript Destructuring in Depth
3.解构赋值
练习
Promise
问题
1. ES5中的回调地狱了解吗?
2. ES6是如何通过异步操作来实现的呢?
ES5
/**
* 回调地狱
* 函数执行完之后 调用另一个函数
*
* JS单线程
* 异步操作放进异步队列
* 主线程执行完毕,才会执行异步队列中的内容
*/
/**
* ES5 采用回调解决异步问题,但是会有导致回调地狱的问题
*/
function loadScript(src, callback) {
let script = document.createElement('script')
script.src = src
// onload监听script加载完成
script.onload = () => {
callback(src)
}
script.onerr = (err) => {
console.log('err', err);
callback(err)
}
// callback()
document.head.append(script)
}
function test() {
console.log('test');
}
// loadScript('./1.js', test)
loadScript('./1.js', function (script) {
console.log(script);
if (script.message) {
} else {
loadScript('./2.js', function (script) {
console.log(script);
loadScript('./3.js', function (script) {
console.log(script);
})
})
}
})
ES6
function loadScript(src) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
script.src = src
script.onload = () => resolve(src) // fulfilled, result
script.onerr = (err) => reject(err) // rejected, error
document.head.append(script)
})
}
// loadScript('./1.js')
// .then(loadScript('./2.js'))
loadScript('./1.js').then(res => {
console.log(res);
loadScript('./2.js')
})
then
Promise.then(onFulfilled, onRejected)
catch
function loadScript(src) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
script.src = src;
script.onload = () => resolve(src);
script.onerror = (err) => reject(err);
document.head.append(script)
})
}
loadScript('./1.js')
.then(() => {
return loadScript('./2.js')
})
.then(() => {
return loadScript('./3.js')
})
.catch(err => {
/*
promise对象上的方法
用来捕获链式操作上reject的异常
*/
// 可以捕获所有错误
/*
catch 捕获改变promise状态的error
不要用throw new Error去触发错误
*/
console.log(err);
})
Promise.resolve,Promise.reject
function test(bool) {
if(bool) {
return new Promise((resolve, reject) => {
resolve(30)
})
} else {
// return Promise.resolve(12)
return Promise.reject(new Error('ss'))
}
}
test().then(val => {
console.log(val);
}, err => {
console.log(err);
})
Promise.all
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then(val => {
console.log(val)
})
Promise.race
先到先得
const p1 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
}
const p2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000)
})
}
Promise.race([p1(), p2()]).then(val => {
console.log(val)
})
实例方法、静态方法
练习
Reflect
反射
什么是反射机制?
/**
* apply 动态指向作用域
* 指定是哪个对象的哪个方法
* */
console.log(Math.floor.apply(null, [33, 72]))
/**
* Relect
* 不需要指定,先用apply,再去指定函数,执行的时候再去找所要执行的方法
*/
console.log(Reflect.apply(Math.floor, null, [4, 72]))
// Math.ceil
let price = 91.5
if (price > 100) {
price = Math.floor.apply(null, [price])
} else {
price = Math.ceil.apply(null, [price])
}
console.log(Reflect.apply(price > 100 ? Math.floor : Math.ceil, null, [price]))
// 类的实例
let d = new Date()
console.log(d.getTime())
/**
* Reflect.construct
* 调用不同的类,实现实例化对象
*/
let d1 = Reflect.construct(Date, [])
console.log(d1.getTime(), d1 instanceof Date)
/**
* Reflect
* 两种的返回值不同
*/
const student = {}
let res1 = Object.defineProperty(student, 'name', {
value: 'Mike'
})
console.log(student)
let res2 = Reflect.defineProperty(student, 'age', { value: 'ss' })
console.log(student, res1, res2)
Reflect.deleteProperty(student, 'age')
console.log(student)
// 读数据 Reflect.get(obj, property)
console.log(Reflect.get(student, 'name'))
console.log(Reflect.get([3, 4], 1))
/**
* getOwnPropertyDescriptor 属性描述符
*/
const obj = { x: 1, y: 2 }
console.log(Object.getOwnPropertyDescriptor(obj, 'y'))
console.log(Reflect.getOwnPropertyDescriptor(obj, 'x'))
console.log(Reflect.has(obj, 'x')) // Object上面没有has
// Reflect.isExtensible是否事可扩展的
console.log(Reflect.isExtensible(obj))
// Object.freeze冻结
// Object.freeze(obj)
// Reflect.preventExtensions
Reflect.preventExtensions(obj)
console.log(Reflect.isExtensible(obj))
// 判断自身的属性,不包含原型链
console.log(Reflect.ownKeys(obj))
console.log(Reflect.ownKeys([1, 3])) // ['0', '1', 'length']
Reflect.set(obj, 'z', 4)
console.log(obj)
let date = new Date()
console.log(Reflect.getPrototypeOf(date)) // 获取原型对象
const arr = ['duck', 'duck']
Reflect.set(arr, 2, 'goose')
console.log(arr)
Reflect.setPrototypeOf(arr, String.prototype)
arr.sort()
console.log(Reflect.getPrototypeOf(arr))
Proxy
/**
* Proxy
* 屏蔽原始信息
* 类
* 参数一:代理谁
* 参数二:干什么
*/
let o = {
name: 'xiaoming',
price: 190
}
let d = new Proxy(o, {
get (target, key) {
if (key === 'price') {
return target[key] + 20
} else {
return target[key]
}
}
})
console.log(d.price)
console.log(d.name)
es6
let o = {
name: 'xiaoming',
price: 190
}
let d = new Proxy(o, {
get (target, key) {
return target[key]
},
set (target, key, value) {
return false
}
})
d.price = 300
console.log(d.price)
console.log(d.name)
es5
let o = {
name: 'xiaoming',
price: 190
}
for (const [key] of Object.entries(o)) {
Object.defineProperty(o, key, {
writable: false
})
}
console.log(o.name, o.price)
使用
let o = {
name: 'xiaoming',
price: 190
}
let validator = (target, key, value) => {
if (Reflect.has(target, key)) {
if (key === 'price') {
if (value > 300) {
return false
} else {
target[key] = value
}
} else {
target[key] = value
}
} else {
return false
}
}
let dd = new Proxy(o, {
get (target, key) {
return target[key] || '' // 将undefined的情况优雅设置为空
},
set: validator
})
dd.price = 289
console.log(dd)
/**
* Proxy
* 屏蔽原始信息
* 类
* 参数一:代理谁
* 参数二:干什么
*/
// 上报逻辑
window.addEventListener('error', (e) => {
console.log(e.message)
// 触发逻辑
}, true)
let o = {
name: 'xiaoming',
price: 190
}
// 校验
let validator = (target, key, value) => {
if (Reflect.has(target, key)) {
if (key === 'price') {
if (value > 300) {
// 不满足规则就要触发错误
// throw new TypeError('price exceed 300')
// return false
} else {
target[key] = value
}
} else {
target[key] = value
}
} else {
return false
}
}
let dd = new Proxy(o, {
get (target, key) {
return target[key] || '' // 将undefined的情况优雅设置为空
},
set: validator
})
dd.price = 301
console.log(dd)
/**
* 监控和上报
* 提升用户体验,产品质量
* 上报到服务端
* 错误触发
*/
/**
* 场景
*/
// class Component {
// constructor() {
// this.id = Math.random().toString(36).slice(-8) // 外面可以直接修改id
// }
// }
// class Component {
// get id() {
// return Math.random().toString(36).slice(-8) // 重复调用会出现多个不同的值
// }
// }
class Component {
constructor () {
this.proxy = new Proxy({
id: Math.random().toString(36).slice(-8)
}, {})
}
get id () {
return this.proxy.id
}
}
let com = new Component()
let com2 = new Component()
for (let i = 0; i < 10; i++) {
console.log(com.id, com2.id)
}
com.id = 'abc'
console.log(com.id, com2.id)
撤销代理
let o = {
name: 'xiaoming',
price: 190
}
// let d = new Proxy(o, {
// get (target, key) {
// return target[key]
// },
// set (target, key, value) {
// return false
// }
// })
/**
* 想要撤销,创建使用Proxy.revocable
* 代理数据和撤销操作
* 代理数据 d.proxy
* 撤销代理 d.revoke
*/
let d = Proxy.revocable(o, {
get (target, key) {
return target[key]
},
set (target, key, value) {
return false
}
})
console.log(d)
console.log(d.proxy)
setTimeout(() => {
// 撤销代理
d.revoke()
setTimeout(() => {
console.log(d.proxy.price)
}, 1000)
}, 1000)
练习
Generator
问题:ES6如何让循环停下来
ES5
无法控制暂停开启
// es5
function loop () {
for (let i = 0; i < 5; i++) {
console.log(i)
}
}
loop()
ES6
可自定义遍历器
// es6
/**
* 定义:
* function * 函数名
* 需要暂停的地方前写 yield
* 执行:next
*/
function * loop () {
for (let i = 0; i < 5; i++) {
yield console.log(i)
}
}
const l = loop() // 不执行
l.next() // 0
l.next() // 1
l.next() // 2
l.next() // 3
l.next() // 4
l.next()
l.next()
l.next()
function * gen () {
let val
val = yield 1
console.log(val)
}
const l = gen()
l.next() // 执行到yield暂停,没有输出
l.next() // 执行到赋值,找下一个yield,没有yield,所以undefined
yeild,return
// function * gen () {
// let val
// val = yield 1
// console.log(val)
// }
// const l = gen()
// l.next() // 执行到yield暂停,没有输出
// l.next() // 执行到赋值,找下一个yield,没有yield,所以undefined
/**
* 程序暂停和恢复执行
* function * 函数名
* 打断的地方加 yield
* 函数可以嵌套,在嵌套之前加 *
* next执行
* 返回 value是数据,done表示是否执行
*/
// function * gen () {
// let val
// val = yield * [1, 2, 3] // * 星号后面是 可迭代的对象、generator对象
// // val = yield [1, 2, 3]
// console.log(val)
// }
// const l = gen()
// console.log(l.next())
// console.log(l.next())
/**
* 问题
* generator是用来干什么用的
* yield有没有返回值
* 和es5比,是怎么控制程序的暂停和启动
*/
/**
* 控制函数暂停,中途传递数据,能不能在函数外部传递数据给内部
* 使用next传参,改变yield返回值
* 提前终止:
* 1.return
* 2.抛出异常
*/
function * gen () {
let val
val = (yield [1, 2, 3]) + 7
console.log(val)
}
const l = gen()
console.log(l.next(10))
// console.log(l.return())
console.log(l.return(100))
console.log(l.next(20))
function * gen () {
while (true) {
try {
yield 1
} catch (e) {
console.log(e.message)
}
}
}
const g = gen()
console.log(g.next())
console.log(g.next())
console.log(g.next())
g.throw(new Error('ss')) // 外部向内部捕获异常
console.log(g.next())
抽奖
es5
// es5
function draw (first = 1, second = 3, third = 5) {
let firstPrize = ['1A', '1B', '1C', '1D', '1E']
let secondPrize = ['2A', '2B', '2C', '2D', '2E', '2F', '2G', '2H', '2I']
let thirdPrize = ['3A', '3B', '3C', '3D', '3E', '3F', '3G', '3H', '3I']
let result = []
let random // 随机索引
// 抽一等奖
for (let i = 0; i < first; i++) {
random = Math.floor(Math.random() * firstPrize.length)
result = result.concat(firstPrize.splice(random, 1)) // 找到并删除,避免同一个拿到多个奖项
}
// 抽二等奖
for (let i = 0; i < second; i++) {
random = Math.floor(Math.random() * secondPrize.length)
result = result.concat(secondPrize.splice(random, 1)) // 找到并删除,避免同一个拿到多个奖项
}
// 抽三等奖
for (let i = 0; i < third; i++) {
random = Math.floor(Math.random() * thirdPrize.length)
result = result.concat(thirdPrize.splice(random, 1)) // 找到并删除,避免同一个拿到多个奖项
}
return result
}
let t = draw()
for (let value of t) {
console.log(value)
}
es6
function * draw (first = 1, second = 3, third = 5) {
let firstPrize = ['1A', '1B', '1C', '1D', '1E']
let secondPrize = ['2A', '2B', '2C', '2D', '2E', '2F', '2G', '2H', '2I']
let thirdPrize = ['3A', '3B', '3C', '3D', '3E', '3F', '3G', '3H', '3I']
let count = 0
// let result = []
let random // 随机索引
while (1) {
if (count < first) {
random = Math.floor(Math.random() * firstPrize.length)
yield firstPrize[random]
count++
firstPrize.splice(random, 1)
} else if (count < first + second) {
random = Math.floor(Math.random() * secondPrize.length)
yield secondPrize[random]
count++
secondPrize.splice(random, 1)
} else if (count < first + second + third) {
random = Math.floor(Math.random() * thirdPrize.length)
yield thirdPrize[random]
count++
thirdPrize.splice(random, 1)
} else {
return false
}
}
}
let d = draw()
不断输出3的倍数
function * count (x = 1) {
while (1) {
if (x % 3 === 0) {
yield x
}
x++
}
}
let num = count()
console.log(num.next().value)
console.log(num.next().value)
console.log(num.next().value)
console.log(num.next().value)
练习
iterator generator
iterator
可迭代协议、迭代器协议
let authors = {
allauthors: {
fiction: ['章六', '沼气', '李鹏'],
scienceFiction: ['章三', '里斯', '王武'],
fantacy: ['agla', 'gaga', 'lisa']
}
}
for (let [k, v] of Object.entries(authors.allauthors)) {
console.log(k, v)
}
// let res = []
// for (const v of authors) {
// console.log(v) // Uncaught TypeError: authors is not iterable
// res.push(v)
// }
/**
* 对象要可遍历,需要部署可遍历接口
* 接口---方法,挂载方法去写
* 可遍历的接口
* 和对象做关联
* 自定义遍历器:
* key: Symbol.iterator (可迭代协议)
* 规范:
* 输入:this---对象本身
* 输出:返回值,输出有约束格式(迭代器协议)
* return {
* // this
* next (){
* return {
* done: true/false, // 表示遍历是否结束
* value: 值 // 遍历项的值
* }
* }
* }
* 迭代器协议
* 可迭代协议
*/
authors[Symbol.iterator] = function () {
// this // 对象本身
let allAuthors = this.allauthors
let keys = Reflect.ownKeys(allAuthors)
let values = []
// 输出:返回,输出有约束格式
return {
next () {
if (!values.length) {
if (keys.length) {
values = allAuthors[keys[0]]
keys.shift()
}
}
return {
done: !values.length,
value: values.shift() // shift删除第一个,返回值为删除的内容,然后values重新置空
}
}
}
}
let res = []
for (const v of authors) {
console.log(v)
res.push(v)
}
console.log(res)
console.log(authors)
generator
let authors = {
allauthors: {
fiction: ['章六', '沼气', '李鹏'],
scienceFiction: ['章三', '里斯', '王武'],
fantacy: ['agla', 'gaga', 'lisa']
}
}
/**
* funciton * 函数名
*/
authors[Symbol.iterator] = function * () {
let allAuthors = this.allauthors
let keys = Reflect.ownKeys(allAuthors)
let values = []
while (1) {
if (!values.length) {
if (keys.length) {
values = allAuthors[keys[0]]
keys.shift()
yield values.shift()
} else {
return false
}
} else {
yield values.shift()
}
}
}
let res = []
for (const v of authors) {
console.log(v)
res.push(v)
}
console.log(res)
console.log(authors)
练习
ES6如何进行模块化设计
导入导出
// export const name = 'hello'
// export let age = 18
const name = 'hello'
let age = 18
let def = '123'
export default def
export const add = () => {
console.log(1211)
}
export {
name,
age
}
// import def from './lesson7.0' // 默认的导出
// import def, { name, age } from './lesson7.0'
import { add as addFn } from './lesson7.0'
addFn()
// console.log(name)
// console.log(age)
// console.log(def)
导入导出类
导出
class Test {
constructor (id) {
this.id = id
}
}
export default Test
export default class Test {
constructor (id) {
this.id = id
}
}
导入
import Test from './lesson7.0'
let test = new Test('123')
console.log(test.id)
导出
export class Test {
constructor (id) {
this.id = id
}
}
导入
import { Test } from './lesson7.0'
let test = new Test('12300')
console.log(test.id)
export class Test {
constructor (id) {
this.id = id
}
}
export class Animal {
constructor (type) {
this.type = type
}
}
import * as Mode from './lesson7.0' // 导入所有
console.log(Mode)
let test = new Mode.Test('12300')
console.log(test.id)
let animal = new Mode.Animal('dog')
console.log(animal.type)
export function say () {
console.log('say')
hello()
}
export function hello () {
console.log('hello')
}
import { say } from './lesson7.0'
say()
学习笔记