最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 快2021年了,必须掌握的ECMAScript 新特性

    正文概述 掘金(幽蝉)   2020-12-15   611

    2020年伊始,我们经历了艰难和恐慌;

    转眼间,2020年已接近尾声,继续努力,莫负时光!

    回顾过往来不及细细思量,

    我们曾为梦想拼搏,也曾为挫折痛哭;

    时间匆匆再不说岁月漫长,

    酸甜苦辣,悲喜交加,

    经历的、错过的,让人对现在的生活倍感珍惜。

    历数2020年的前11个月,

    有收获有遗憾,有喜悦有不足,

    最后的一个月,用努力对抗焦虑,

    让2020充实收尾!

    let & const

    在之前JS只有两种作用域,全局作用域和函数作用,是没有块级作用域的

    通过let声明的变量,只能在其所在的作用域中访问。

    var elements = [{}, {}, {}]
    for (var i = 0; i < elements.length; i++) {
    	elements[i].onclick = function() {
        	console.log(i)  //这里的i其实打印的都是全局作用域的i
        }
    }
    elements[0].onclick() // 3  循环结束后,i已经累加到了3
    

    这也是闭包的典型案例

    var elements = [{}, {}, {}]
    for (var i = 0; i < elements.length; i++) {
    	elements[i].onclick = (function(i) {
        	console.log(i)  //这里的i其实打印的都是全局作用域的i
        })(i)
    }
    elements[0].onclick() // 3  循环结束后,i已经累加到了3
    

    其实闭包也是借助函数作用域摆脱全局作用域产生的影响

    现在有了let后就简单了

    var elements = [{}, {}, {}]
    for (let i = 0; i < elements.length; i++) {
    	elements[i].onclick = function() {
        	console.log(i)  //这里的i其实打印的都是全局作用域的i
        }
    }
    elements[0].onclick()
    
    

    让我们在看一下例子

    for (let i = 0; i < 3; i++) {
    	let i = 'foo';
        console.log(i)
    }
    正常输出3次foo, 这两个i是互相不影响的
    

    代码进行拆解

    let i = 0;
    if (i < 3) {
      let i = 'foo'
      console.log(i)
    }
    i++
    if (i < 3) {
      let i = 'foo'
      console.log(i)
    }
    i++
    if (i < 3) {
      let i = 'foo'
      console.log(i)
    }
    i++
    

    另外const/let不存在变量提升,所以在代码块里必须先声明然后才可以使用,这叫暂时性死区;

    let name = 'test'
    if (true) {
        name = '测试'
        let name
    }
    console.log(name)
    

    const/let 不允许在同一个作用域内,重复声明;

    const 声明时必须初始化,且后期不能被修改,但如果初始化的是一个对象,那么不能修改的是该对象的内存地址;

    const person = {
        name: '测试'
    }
    person.name = 'test'  
    console.log(person.name)  // 'test' 只是修改了内存空间的数据,没有改变内存地址
    person = ''  // TypeError 这改变了内存的指向
    

    const/let 在全局作用域中声明的常量/变量不会挂到顶层对象(浏览器中是 window )的属性中;

    var name = '测试'
    let age = 12
    console.log(window.name)  // '测试'
    console.log(window.age)  // undefined
    

    最佳实践:不用var ,主用const,配合let,这种会使你的代码的质量会有明显的提高。

    解构

    数组解构

    // 解构不成功的变量值为 undefined
    const [a, b, c] = [1, 2]
    console.log(a, b, c)  // 1, 2, undefined
    
    // 获取余下的变量
    const arr = [100, 200, 300]
    const [foo, ...reset] = arr
    console.log(reset)
    
    // 赋值
    const [foo, bar, baz = 123, more = 'test' ] = arr;
    console.log(more)
    

    对象解构

    const obj = { name: '123', age: 12 }
    const { name } = obj
    console.log(name)
    
    但是如果有个重名的name 
    const name = 'memo'
    const { name: objName = 'jack' } = obj  //这样就不会重名了
    console.log(objName)
    
    场景:
    如果代码中出现了大量的console.log
    const { log } = console
    log('123')
    这样我们代码的整体的体积会减少很多
    

    函数参数解构:其实就是运用上面的对象解构和数组解构规则;

    function move({x = 0, y = 0} = {}) {
        console.log([x, y])
        return [x, y];
    }
    move({x: 1, y: 2})  // [3, 8]
    move({x: 3})        // [3, 0]
    move({})            // [0, 0]
    move()              // [0, 0]
    

    模板字符串

    模板字符串使用两个反引号标识(``),可以用来定义多行字符串,或者使用它在字符串中插入变量:

    let name = 'hero'
    let tips = `Hello ${name} ---- ${ 1 + 2 }, 
        welcome to my world.`
    console.log( tips )
    

    标签模板

    const str = console.log`hello world`
    
    const name = 'element'
    const gender = true
    function myFunc(strings, name, gender) {
    	const sex = gender ? 'man' : 'woman'
    	return strings[0] + name + string[1] + gender + strings[2]
    }
    const result = myFunc`hey, ${ name } is a ${ gender }.`
    

    场景:这种模板字符串可以实现多语言(如:中英文切换)

    参数默认值

    之前的方式

    function foo(enable) {
    	enable = enable === undefined ? true : ''
    	console.log(enable)
    }
    foo(false)
    

    有了参数默认值

    function foo(enable = true) {
    	console.log(enable)
    }
    foo(false)
    
    
    多个参数
    带参数默认值的要放到最后
    function foo(bar, enable = true) {
    	console.log(enable)
    }
    foo(false)
    

    剩余参数

    之前的方式我们利用伪数组,arguments

    function foo() {
    	console.log(arguments) // Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    }
    foo(1,2,3,4)
    

    现在

    function foo(...args) {
    	console.log(args)
    }
    foo(1,2,3,4)
    

    展开数组

    const arr = ['foo', 'bar', 'baz']
    console.log.apply(console, arr)
    
    
    现在简化了操作
    console.log(...arr) 
    

    箭头函数

    箭头函数语法比函数表达式更简洁,并且没有自己的 this、arguments,不能用作构造函数和用作生成器。

    const arr = [1, 2, 3, 4, 5, 6]
    arr.filter(i => i % 2)
    
    
    const person = {
    	name: 'tom',
        sayHi: () => {
        	console.log(`hi, my name is ${this.name}`)  //undefined
        },
        sayHiName: () => {
        	setTimeout(() => {
            	console.log(this.name)
            }, 1000)
        }
    }
    person.sayHiName()
    

    对象字面量增强

    const bar = 345
    const obj = {
    	//bar: bar,
        bar,
        methond() {
        	console.log(this)
        },
        [Math.random()]: 123, // 计算属性名 Math.random()的执行结果将作为属性名
        [bar]: '23ew'
    }
    obj[Math.random()] = 123
    

    对象扩展方法

    Object.assign()

    const source = {
    	a: 123,
        b: 345
    }
    const source1 = {
    	a: 789,
        b: 789
    }
    const target = {
    	a: 678,
        c: 908
    }
    const result = Object.assign(target, source, source1)
    
    
    在看一个例子
    function func(obj) {
    	obj.name = 'tom'
       	console.log(obj)
    }
    const obj = { name: 'lili' }
    func(obj)
    console.log(obj)
    这样外面的obj也改变了
    
    改进:
    function func(obj) {
    	const objNew = Object.assign({}, obj)
    	objNew.name = 'tom'
       	console.log(objNew)
    }
    const obj = { name: 'lili' }
    func(obj)
    console.log(obj)
    

    Object.is() // 判断两个值是否相等 这个用的少,一般还是要用严格意义的相等===

    Object.is(+0, -0)
    Object.is(NaN, NaN)
    

    proxy 代理对象

    object.defineProperty

    const person = {
    	name: 'li',
        age: 25
    }
    cosnt personProxy = new Proxy(person, {
    	get(target, property) {
        	return property in target ? target[property] : 'defalut'
        },
        set(target, property, value) {
        	if(property === 'age') {
            	if(!Number.isInteger(value)) {
                	throw new TypeError(`${value} is not an int`)
                }
            }
        	target[property] = value
        }
    })
    personProxy.gender = true
    console.log(personProxy.name)
    

    proxy优势 defineProperty只能监听到对象属性的读写

    proxy优势能监视到更多对象操作例如:delete操作,

    const person = {
    	name: 'li',
        age: 25
    }
    cosnt personProxy = new Proxy(person, {
    	deleteProperty(target, property) {
        	delete target[property]
        }
    })
    

    proxy是以非侵入方式监管了对象的读写

    Reflect (静态类)

    Reflect内部封装了一系列对对象的底层操作

    const person = {
    	name: 'li',
        age: 25
    }
    console.log('name' in person)
    console.log(delete obj['age'])
    console.log(Object.keys())
    
    console.log(Reflect.has(obj, 'name'))
    console.log(Reflect.deleteProperty(obj, 'age'))
    console.log(Reflect.ownKeys(obj))
    

    Set数据结构

    const s = new Set()
    s.add(1).add(2).add(3).add(4).add(2)
    console.log(s)
    
    console.log(s.size)
    
    console.log(s.has(100)) // 是否包含某个值
    console.log(s.delete(3)) // 删除某个值
    s.clear() //全部请求
    
    场景:
    数组去重
    const arr = [1,2,3,4,1,3]
    const result = Array.from(new Set(arr))
    
    const result = [...new Set(arr)]
    console.log(result)
    

    Map 数据结构 (严格意义上的键值对集合)

    const obj = {}
    obj[true] = 'value'
    obj[123] = 'value'
    obj[{ a: 1 }] = 'value'
    console.log(Object.keys(obj))  // ['123', 'true', '[object, objecct]']
    
    
    
    
    const m = new Map()
    const tom = { name: 'tom' }
    m.set(tom, 90)  // 存数据
    console.log(m)
    
    m.get(tom)
    m.has()
    m.delete()
    m.clear()
    
    m.forEach((vlaue, key) => {
    	console.log(value, key)
    })
    

    Symbol

    shared.js
    
    const cache = {}
    
    a.js
    cache['foo'] = Math.random()
    
    b.js
    cache['foo'] = '124'
    
    解决这种问题
    约定一下
    b.js
    cache['b_foo'] = '123'
    
    如果有人不遵守这种约定怎么办?
    
    Symbol() 创建的每一个都是独一无二的
    console.log(Symbol() === Symbol()) // false
    
    
    场景: 模拟对象的私有成员
    const name = Symbol()
    const person = {
    	[name]: '234',
        say() {
        	console.log(this[name])
        }
    }
    person[Symbol] // 不能通过这种方式调用,每个Symbol() 都是不一样的
    person.say()
    
    补充
    
    const s1 = Symbol.for('foo')
    const s2 = Symbol.for('foo')
    console.log(s1 === s2) // true
    console.log(Symbol.for(true) === Symbol.for('true'))  // true
    
    

    Promise (一种更优的异步编程)

    快2021年了,必须掌握的ECMAScript 新特性

    const promise = new Promise(function (resolve, reject) {
    	resolve(100)
        
        reject(new Error('promise rejected'))
    })
    promise.then(function (value) {
    	console.log('resolve', value)
    }, function(error) {
    	console.log('rejected', error)
    })
    
    基本应用:
    function ajax(url) {
    	return new Promise(function (resolve, reject) {
        	let xhr = new XMLHttoRequest()
            xhr.open('GET', url)
            xhr.responseType = 'json'
            xhr.onload = function() {
            	if(this.status === 200) {
                	resolve(this.response)
                } else {
                	reject(new Error(this.statusText))
                }
            }
            xhr.send()
        })
    }
    let promise = ajax('www.www.com')
    let pronise2 = promise.then(
    	function onFullfilled(value) {
        	console.log('onFullfilled', value)
        },
        function onRejected(error) {
        	console.log('onRejected', error)
        }
    )
    解决回调地狱
    ajax('api')
    	.then(function (value)) {
        	console.log(111)
            return ajax('/api/a')
        })
        .then(function (value)) {
        	console.log(222)
            return ajax('/api/b')
        })
        .then(function (value)) {
        	console.log(333)
            return ajax('/api/c')
        })
        .catch(function (error) {
        	console.log(error)
        })
    
    

    并行执行

    const urls = [
        'https://api.github.com',
        'https://api.github.com/users',
        'https://api.github.com/uasdfs/asdfad'
    ]
    const promises = urls.map(item => { axios(item).catch(e => ({})) })
    
    const promise = Promise.allSettled(urls.map(item => axios(item)))
    promise.then(res => console.log(res))
    
    

    Async/Await

    async function main () {
      try {
          const users = await ajax('/api')
          console.log(users)
      } catch (e) {
          console.log(e)
      }
    }
    main()
    

    迭代器(Iterator)

    const set = new Set(['foo', 'bar', 'baz'])
    const iterator = set[Symbol.iterator]()
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())
    
    其实这就是for of的实现原理
    
    实现可迭代接口
    const obj = {
    	[Symbol.iterator]: function() {
        	return {
            	next: function() {
                	return{
                    	value: 'test',
                        done: true
                    }
                }
            }
        }
    }
    
    for(const item of obj) {
    	console.log('循环体') // 不会被执行,写死了next的返回方法,第一次done就是true
    }
    改进一下:
    const obj = {
    	store: ['foo' , 'bar', 'baz'],
    	[Symbol.iterator]: function() {
        	let index = 0
            const self = this
        	return {
            	next: function() {
                	const reult = {
                    	value: self.store[index],
                        done: index >= self.store.length
                    }
                    index++
                    return result
                }
            }
        }
    }
    for(const item of obj) {
    	console.log('循环体', item) 
    }
    
    

    迭代器设计模式 场景: 你我谢童开发一个任务清单应用

    // 我的代码 ==================

    设计一个存放所有任务的对象
    const todos = {
    	life: ['eat', 'sleep', 'play doung'],
        learn: ['chinese', 'math', 'english']
    }
    

    // you的代码

    呈现对象到界面上
    for(const item of todos.life) {
    	console.log(item)
    }
    等等
    如果我的对象结构变了怎么办呢?
    
    对外提供一个统一遍历接口
    const todos = {
      life: ['eat', 'sleep', 'play doung'],
      learn: ['chinese', 'math', 'english'],
      each: function (callback) {
          const all = [].concat(this.life, this.learn)
          for(const item of all) {
            callback(item)
          }
      }
    }
    
    todos.each(function(item) {
    	console.log(item)
    })
    
    用迭代器的方式
    const todos = {
      life: ['eat', 'sleep', 'play doung'],
      learn: ['chinese', 'math', 'english'],
      [Symbol.iterator]: function() {
          const all = [...this.life, ...this.learn]
          let index = 0
          return {
              next: function() {
                  value: all[index],
                  done: index++ >= all.length
              }
          }
      }
    }
    for(const item of todos) {
    	console.log(item)
    }
    
    

    生成器(Generator)

    避免异步编程中回调嵌套过深,提供更好的异步编程解决方案

    function *foo() {
      console.log('test')
      return 200
    }
    const result = foo()
    console.log(result)
    console.log(result.next())
    
    
    function *foo() {
      console.log('111')
      yield 100
      console.log('222')
      yield 200
    }
    
    
    场景
    自增id
    function *createIdMaker() {
      let id = 1
      while(true) {
          yield id++
      }
    }
    const idMaker = createIdMaker()
    console.log(idMaker.next().value)
    console.log(idMaker.next().value)
    console.log(idMaker.next().value)
    console.log(idMaker.next().value)
    

    Array.includes

    const arr = ['foo', 1, NaN, fale]
    console.log(arr.indexof(NaN)) // -1 不能查找NaN
    console.log(arr.includes) // true
    
    

    指数运算符

    console.log(Math.pow(2, 10))
    
    console.log(2 ** 10)
    

    Object.values

    const obj = {
    	foo: 'value1',
        bar: 'value2'
    }
    
    console.log(Object.values()) // ['value1', 'value2' ]
    

    Object.entries

    console.log(Object.entries()) // [['foo', 'value1'], ['bar', 'value2']]
    
    for(const [key, value]) of Object.entries(obj) {
    	console.log(key, value)
    }
    

    Object.getOwnPropertyDescriptors

    const p1 = {
      firstName: 'li',
      lastName: 'xiang',
      get fullName(){
          return this.firstName + ' ' + this.lastName
      }
     }
     console.log(p1.fullName)
     
     const p2 = Object.assign({}, p1)// 把fullName当成了一个普通的属性进行复制
     p2.firstName = 'wang'
     console.log(p2.fullName) 	// li xiang
    
    const descriptors = Object.getOwnPropertyDescriptors(p1)
    const p2 = Object.defineProperties({}, descriptors)
    p2.firstName = 'wang'
    console.log(p2.fullName) // wang xiang
    

    String.prototype.padStart / String.prototype.padEnd

    const obj = {
    	html: 5,
        css: 23,
        javascript: 234
    }
    
    for(const [name, count] of Object.entries(obj)) {
    	console.log(`${name.padEnd(16, '-')} | ${count.padStart(3, '0')}`)
    }
    
    

    起源地下载网 » 快2021年了,必须掌握的ECMAScript 新特性

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元