ES5-ES6笔记记录

199 阅读15分钟

ES6-2015.png 各大浏览器、平台支持ES的情况

环境准备

image.png

安装环境

安装node

    npx es10-cli create projectName
    cd projectName
    npm start

配置插件

image.png

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
  1. 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}

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);

  • 遍历
  • 转换
  • 生成
  • 查找

练习一

image.png

构造函数 初始化 传参 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. 默认值
    1. 不确定参数
    1. 箭头函数

问题

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箭头函数

  1. 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

image.png

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);

练习三

image.png

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

image.png

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 四节点的单个字符

image.png

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'

1.Destructuring Assignment

2.ES6 JavaScript Destructuring in Depth

3.解构赋值

练习

image.png

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)
})

实例方法、静态方法

image.png

练习

image.png

Reflect

反射

什么是反射机制?

image.png

/**
 * 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))

image.png

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)

练习

image.png

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)

练习

image.png

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)

练习

image.png

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()

image.png

学习笔记