学习笔记:ECMAScript新特性

157 阅读12分钟

前言:该内容为学习笔记如有错误及不清晰欢迎指出讨论,谢谢

ECMAScript新特性

理解语言和平台之间的关系

  • ECMAScript与JavaScript
    • 实际上JavaScript是ECMAScript的扩展语言
    • ECMAScript只提供了最基本的语法

一、ECMAScript2015新特性

1.let与块级作用域

  • 作用域:某个成员能够起左右的范围
    • 全局作用域
    • 函数作用域
    • 块级作用域
      • {}所包括的范围
  • let声明不会变量提升

2.CONST

  • const:常量、恒量
    • 就是在let中多了一个只读
    • 变量声明后就不能修
      • 注意的是,这里不能修改是指不能指向一个新的内存地址,是可以修改内存数据
      • 不用
  • 最佳实践:不用var,主用const,配合let

3.数组、对象解构

const obj = { name : 'zce' , age : 28 }
const name = 'tom'
const {name:objname  = 'jack'} = obj //如果写name会冲突,可以使用重命名,且默认值是jack
console.log(objname);

4.模板字符串

5.字符串扩展方法

  • includes() //是否判断包含某字符串
  • startsWith() //判断字符串是否以XXX开头
  • endsWith() //判断字符串是否以XXX结尾

6.参数默认值

function foo (enable = true) {   //传入函数默认值,如果是多个参数的话就要在形参的最后一位
    console.log(enable);
}
foo() // true 

7.剩余参数

function foo (first , ...args) {  //注意放到形参最末端,且只能使用一次
    console.log(args);
}
foo(1,2,3,4)

8.展开数组

  • 处理数组不定参方法
const arr = ['foo','bar','baz'] 
console.log(...arr);  //'foo','bar','baz'  

9.箭头函数

  • 箭头函数不会改变this指向

10.对象字母字面量加强

  • 属性值、属性名相同可略
  • 方法可以直接写
  • 计算属性名 [Math.random()] :123 ,[]内可以执行任何函数,该表达是结果可以作为属性名

11.对象扩展方法

  • object.assign

    • 将多个源对象中的属性复制到一个目标对象中
const sourcel = {
    a:123,
    b:123
}
const sourcel2 = {
    b:789,
    d:789
}
const target = {
    a:456,
    c:456
}
const res =  Object.assign(target,sourcel,sourcel2) // 目标对象存在就一次覆盖,没有就新增
console.log(target);   //{ a: 123, c: 456, b: 789, d: 789 }
console.log(res  === target); //true
  • Object.is

    • 判断两个值是否相等
Object.is(+0,-0) //false
Object.is(NaN,NaN) //true
  • Object.defineProperty

    • 用来监听某个对象的属性读写(数据劫持)(Vue3.0 以前版本)
  • Proxy

    • 是指对象访问代理器
    • 可以类比门卫,你存放都需要经过这个Proxy
const person = {
    name :'wyc',
    age:18
}

const personProxy = new Proxy(person,{
    get(target,property){
         //target :被访问的目标,property:被访问的属性
        // console.log(target,property); 
        // return 100
        return property in target ?  target[property] : 'default'
    },
    //代理目标 target,属性名和属性值
    //这里可以做一个数据校验
    set(target,property,value){
        // console.log(target,property,value);
        if(property=== 'age'){
            if(!Number.isInteger(value)){
                throw new TypeError(`${value} is not a Number`)
            }
            target[property] = value
        }
    }
})
  • 两者对比

    • defineProperty只能监视属性的读写,Proxy更加强大,更多拦截操作,13中可以自己查查
      • handler.getPrototypeOf() 在读取代理对象的原型时触发该操作,比如在执行 Object.getPrototypeOf(proxy) 时。
      • handler.setPrototypeOf() 在设置代理对象的原型时触发该操作,比如在执行 Object.setPrototypeOf(proxy, null) 时。
      • handler.isExtensible() 在判断一个代理对象是否是可扩展时触发该操作,比如在执行 Object.isExtensible(proxy) 时。
      • handler.preventExtensions() 在让一个代理对象不可扩展时触发该操作,比如在执行 Object.preventExtensions(proxy) 时。
      • handler.getOwnPropertyDescriptor() 在获取代理对象某个属性的属性描述时触发该操作,比如在执行 Object.getOwnPropertyDescriptor(proxy, “foo”) 时。
      • handler.defineProperty() 在定义代理对象某个属性时的属性描述时触发该操作,比如在执行 Object.defineProperty(proxy, “foo”, {}) 时。
      • handler.has() 在判断代理对象是否拥有某个属性时触发该操作,比如在执行 “foo” in proxy 时。
      • handler.get() 在读取代理对象的某个属性时触发该操作,比如在执行 proxy.foo 时。
      • handler.set() 在给代理对象的某个属性赋值时触发该操作,比如在执行 proxy.foo = 1 时。
      • handler.deleteProperty() 在删除代理对象的某个属性时触发该操作,比如在执行 delete proxy.foo 时。
      • handler.ownKeys() 在获取代理对象的所有属性键时触发该操作,比如在执行 Object.getOwnPropertyNames(proxy) 时。
      • handler.apply() 当目标对象为函数,且被调用时触发。
      • handler.construct() 在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy() 时。
    • Proxy更好的对数组监视,defineProperty去实现就要重写一些放法(vue2原理)
    • Proxy是以非侵入的方式监管了对象的读写
  • Reflect

    • Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers (en-US)的方法相同。Reflect不是一个函数对象,因此它是不可构造的。
    • 目的就是让调用格式更加统一(趋势)有一些静态方法可以网上看看

12.promise

13.class

  • 静态方法,继承那些

14.Set数据结构(可以参考后盾人)

  • Set 是 ES6 提供的新的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的值。

  • Set本身是一个构造函数

    • 通过new Set()来创建
  • Set.add()用来给set添加值

    • 可以链式调用
    • 遇到已经存在的值就忽略
  • 可以使用forEashfor of进行遍历

  • Set.size可以获取set的长度

  • Set.has()可以判断Set里面有没有该值,返回一个Boolean

  • Set。delete()用来删除内部值

    • 删除成功返回true,失败false
  • s.clear清除说有元素

    const s = new Set()
    //add方法,遇到可以链式调用,遇到重复就忽略
    s.add(1).add(2).add(3).add(4).add(2)
    console.log(s); //{1,2,3,4}
    
    s.forEach( i  => { console.log(i);})  //1、2、3、4
    for (let i of s ){console.log(i);} //1、2、3、4
    console.log(s.size);  // 4
    console.log(s.has(100)); //false
    console.log(s.delete(1)); //true
    s.clear()
    console.log(s); //{}
    
  • 使用场景数组去重

let arr = [3, 5, 2, 2, 5, 5];
let setArr = new Set(arr)     // 返回set数据结构  Set(3) {3, 5, 2}

//方法一   es6的...解构
let unique1 =  [...setArr ];   //去重转数组后  [3,5,2]
//方法二  Array.from()解析类数组为数组
let unique2 = Array.from(setArr ) //去重转数组后  [3,5,2]

15.Map数据结构

  • Map也是键值对的形式存放数据
    • Map键名可以是各种类,object只能是字符串
  • Map本身是一个构造函数
    • 通过new Map()来创建
  • Map.set来添加数据
  • 可以使用forEash进行遍历
    • 传入的第一个参数是值,第二个参数是key
  • Set.size可以获取set的长度
  • Map.has()可以判断Set里面有没有该值,返回一个Boolean
  • Map.delete()用来删除内部值
    • 删除成功返回true,失败false
  • Map.clear清除说有元素

16.Symbol

  • ES6 之前的对象属性都是字符串类型的,这样会容易引起冲突,Est6可以使用一种新的数据类型来作为属性名了
    • Symbol 是独一无二的,不会重复。
    • Symbol 不能使用 new 生成,直接 Symbol() 即可。
    • 基本上,它是一种类似于字符串的数据类型。
    • Symbol 值不能跟其它类型的数据运算,会报错
  • Symbol(‘这里面就是描述’)
  • Symbol私有成员
// 对象私有化成员
const name = Symbol()
const person = {
    [name] :'zce',
    say(){
        console.log(this.name);
    }
}

17.for...of循环

  • 作为遍历所有数据的统一方式
    • for...of循环可以拿到每一元素,而非下标
    • 可以在循环内随时使用break去终止循环
    • 在数组循环中,只能通过arr.some()、或arr.every()去终止这个循环
    • 伪数组也可以被遍历(以下是常用为伪数组)
      • 函数内部的 arguments
      • DOM 对象列表(比如通过 document.getElementsByTags 得到的列表
    • 不能遍历对象
const arr = [100, 222, 333, 400];

for (const item of arr) {
    console.log(item);
    if (item > 100 ){
        break
    }   
}

const s  = new Set(['foo','zzcc','hhaha'])
for (const item of s){
    console.log(item);
}

const m = new Map()
m.set('foo','123')
m.set('hjah','223')
//直接遍历就是item是数组,这里可以配合使用数组的
//解构的方法去拿到健和值
// for (const item of m){
//     console.log(item);
// }

for (const [key , value] of m){
    console.log(key, value);
}  

const obj = { foo:123, bar:456 }
for (const item of obj){
    console.log(item);
} //报错

18.Iterable(可迭代接口)

  • 统一遍历的方式,而提供的接口
    • 可以理解为就是一种统一规格标准
    • 实现Iterable接口就是for...of的前提
    • 可以被遍历的数据类型实际在内部已经实现了这个数据接口
      • 在原型内都存在Symbol.iterator这个方法
      • 调用Symbol.iterator()返回一个对象, Iterator {}
      • Iterator是一个可迭代对象,内部由一个next()方法
      • 调用next方法返回一个对象{value: "bar", done: false}value是指向的是值,而done指向是否完成
//Iterator迭代器 
const arr = ['foo','bar','baz']
console.log(arr[Symbol.iterator]()); 
//返回一个迭代对象,对象,里由next这个方法 
const iterator = arr[Symbol.iterator]()
console.log(iterator.next()); 
//{value: "for", done: false}
console.log(iterator.next());
//{value: "bar", done: false}
console.log(iterator.next());
//{value: "baz", done: false}
console.log(iterator.next());
//{value: undefined, done: true}
  • 概念:以操作目标为程序本身的行为特性的编程,我们称为元编程。

  • symbol的内置符号symbol.Iterator,有什么用?

    • 这个符号可以是任意对象上的一个专门属性(确保是唯一)
    • 语言机制会自动的在这个属性上寻找一个方法这个方法会构造一个迭代器来迭代这个对象的值,这个方法就是next方法
    • ...展开和for/of循环会自动使用它
    • 我们可以自定义symbol.iterator属性为任意对象值定义自己的迭代器逻辑
    • 他将覆盖默认的迭代器。相当于是定义了一种元编程行为,提供给Javascript其他部分(也就是运算符和循环结构)在处理定义的对象时使用。
      • 在Js中迭代器对象实现了可迭代协议,迭代器对象由
      • Symbol.iterator属性的值返回。
      • Symbol.iterator属性的值是一个函数,它返回一个迭代器对象。
      • 迭代器指的是拥有next方法的对象。该next方法必须返回一个带有value和done的对象。

19.Iterator迭代器模式

  • 核心就是对外提供这个[symbol.Iterator]接口,不需要关心内部怎么使用,
  • 代码中的each方法只适用于当前对象结构,而Es6中的迭代器是语言层面去实现这个功能
  • 这个适用于任何数据结构,只需要我们设计这个迭代逻辑就行了
    • for...of读取就是value值(可以是数组等其他类型)
const todos = {
    life: ['吃饭', '睡觉', '打豆豆'],
    learn: ['语文', '数学', '英语'],
    day: ['周一', '周二', '周日'],

    each: function (calllback) {
        const all = [].concat(this.life, this.learn, this.day)
        for (const item of all) {
            calllback(item)
        }
    },
	//创建自己的跌倒逻辑
    [Symbol.iterator]: function () {
        const all = [...this.life, ...this.learn, ...this.day];
        let index = 0 ;
        return {
            next: function(){
            return {
                value:all[index],
                done: index++ >= all.length
            	}
            }
        }
    }
}

todos.each(i => console.log(i))
console.log("_______________________________________"); 
for (const item of todos){
    console.log(item);
}

20.生成器Generator

  • blog.csdn.net/baidu_33438…

  • 避免异步编程中回调嵌套过深,提供更好的异步编程规范

  • generator对象是由generator function返回的,符合可迭代协议迭代器协议

    • generator由function*定义(注意多出的*号)
    • 惰性执行
    • yield类似return但是会逐步执行
  • generator function 可以在JS单线程的背景下,使JS的执行权与数据自由的游走在多个执行栈之间,实现协同开发编程,当项目调用generator function时,会在内部开辟一个单独的执行栈,在执行一个generator function 中,可以暂停执行,或去执行另一个generator function,而当前generator function并不会销毁,而是处于一种被暂停的状态,当执行权回来的时候,再继续执行

img

function * foo1() {
    console.log('11111');
    yield 100 
    console.log('22222');
    yield 200
    console.log('3333');
    yield 300
}

const gen = foo1()
console.log(gen.next()); 
//11111 { value: 100, done: false }
console.log(gen.next());
//22222 { value: 200, done: false }
console.log(gen.next());
//33333 { value: 300, done: false }
console.log(gen.next());
//{ value: undefined, done: true }
  • 实际小应用

    (1)发号器

    // 发号器
    function * createIdMaker (){
        let id = 1 
        while (true) {
            yield id++  
        }
    }
    const idMaker = createIdMaker()
    console.log(idMaker.next().value);//1
    console.log(idMaker.next().value);//2
    console.log(idMaker.next().value);//3
    

    (2)对象迭代规则优化

    const todos = {
        life: ['吃饭', '睡觉', '打豆豆'],
        learn: ['语文', '数学', '英语'],
        day: ['周一', '周二', '周日'],
        //生成器自带迭代器
        [Symbol.iterator]: function * () {  
            const all = [...this.life, ...this.learn, ...this.day];
            for (const item of all){
                yield item
            }      
        }
    }
    

21.Generator异步方案

function ajax(url){
    //...这里应该是一个promise
    return new Promise((resolve,reject) =>{
        resolve(url) 
    })
}
function* main(){
    // 这个users是调用next的时候传入的参数
    const users = yield ajax('/api/ha')
    console.log(users);
    const users2 = yield ajax('第二次调用')
    console.log(users2);
}
const g = main()
const res = g.next() //调用next得到一个迭代对象
// value指向一个promise对象
res.value.then( data => {
    //通过调用next把下个参数传入到生成器中执行
    const res2 = g.next(data)
    // 可以再次执行相同动作
    // 再执行前应该加入判断done是否true,
    if (res2.done) return
    res2.value.then( data => {
            const res3 = g.next(data)
        }
        
    )
})
  • 递归实现
function ajax(url) {
    //...这里应该是一个promise
    return new Promise((resolve, reject) => {
        resolve(url)
    })
}

function ajax1(url) {
    //...这里应该是一个promise
    return new Promise((resolve, reject) => {
        reject(url)
    })
}

function* main() {
    try {
        // 这个users是调用next的时候传入的参数
        const users = yield ajax('/api/ha')
        console.log(users);

        const users2 = yield ajax('第二次调用')
        console.log(users2);

        const users3 = yield ajax('第三次调用')
        console.log(users3);

        const err = yield ajax1('这里是模拟ajax2错误')
        console.log(err);
    }
    catch (e) {
        console.log(e);
    }
}

// 再次封装 只要传一个生成器进去,就可以使用生成器的异步编程
function co(generator) {
    const g = generator()
    function handleRes(res) {
        if (res.done) return
        res.value.then(data => {
            handleRes(g.next(data))
        }, err => {
            // 如果是错误就调用生成器的throw去抛出错误
            g.throw(err) //抛出之后也会终止
        })
    }
    handleRes(g.next())
}
co(main)

22. Es moudle

二、ECMAScript2016

1.Array.Prototype.includes

  • .Array.Prototype.includes可以判断数组中是否存在该值,返回一个布尔值
    • 注意的是可以查找NaN,而indexOf不行
const arr = ['fpp',1,NaN,false]
console.log(arr.indexOf(NaN)); // -1
console.log(arr.includes(NaN)); // true

2.指数运算符

  • 2**10 输出1024 (很少用)

三、ECMAScript2017

1.object方法

  • Object.values

    • 返回一个对象的值的数组
    • Object.keys返回的是键名
  • Object.entries

    • 以数组方法返回对象的键值和键值对
      • 可以直接用forof去遍历结果
      • 可以直接new map (结果)去转化成一个map对象
  • Object.getOwnPropertyDescriptors(使用再看)

2.String方法

  • string.padEnd &&StringPadStar
    • 可以指定长度并且添加空值的字符

3.函数的参数中添加尾逗号

4.Async/Await

And more