ECMAScript新特性-学习笔记

一. ECMAScript概述

1. ECMAScript与Javascript直接的关系

ECMAScript只提供最基本的语法,而Javascirpt还包含运行平台提供的API。
在浏览器环境中的 Javascript相当于:ECMAScript 加上 Web APIs(BOM、DOM)。
在node环境中的 Javascript相当于: ECMAScript 加上 Node APIs(fs net etc.)。

2. ECMAScript(ES)版本迭代

从2015年开始ES开始保持每年一个版本的迭代。
ES2015就是ES6,后面为了方便都以新版本的发布年份命名,如ES2016、ES2017、ES2109等。

3. ECMAScript2015这个版本,相比之前的版本更新非常大,其新特性主要分为如下几类:

解决原有语法上的问题或不足
对原有语法进行增强
全新的对象 方法 功能
全新的数据类型和数据结构

二. ECMAScript2015新特性

1. let、const 的块级作用域

ES2015以前,变量的作用域只有全局和函数内部。
在ES2015中新增的let、const变量声明关键字,其作用域新增了块级作用域(就是{}括起来的范围)。

if(true){
    let foo = 'abc'
}
console.log(foo) // 会报错 ReferenceError: foo is not defined

最佳实践:不用var 主用const 配合let。

2. 数组和对象的解构

数组的解构:


const arr = [100, 200, 300]

const [a, b, c] = arr
console.log(a, b, c) // 100, 200, 300

const [, e] = arr
console.log(e) // 200

const [foo, ...rest] = arr
console.log(foo)  // 100
console.log(rest) // [200, 300]

const [a1 = 123, b1, c1, d1 = 'four'] = arr // 解构中设置的默认值仅在取不到相应数据时生效
console.log(a1, b1, c1 ,d1) // 100 200 300 four

对象的解构:

const obj = { name: 'jack', age: 18 }

const { name: objName } = obj
console.log(objName) // jack

const { age = 10, sex = 'male', remark } = obj
console.log(age, sex, remark) // 18 male undefined

3. 模板字符串、字符串的拓展方法

传统的字符串不支持换行,如果有换行的话需要手动拼入 /n

而模板字符串直接敲换行,就会生成换行, 对于输出html字符串非常方便。

const str = `hello,
es2015 ${1 + 5}`
console.log(str)
/*
hello,
es2015 6
*/

带标签的模板字符串,可对拼接内容进行处理:

const name = 'tom'
const gender = false

function myTag1(strings, name, gender){
    console.log('strings: ', strings) // strings:  [ 'Hey, ', ' is a ', '.' ]
    const sex = gender ? 'man' : 'woman'
    return strings[0] + name + strings[1] + sex + strings[2]
}

const result = myTag1`Hey, ${name} is a ${gender}.`
console.log(result) // Hey, tom is a woman.

字符串的拓展方法(startsWith、endsWith、includes):

const message = 'Error: foo is not defined.'
console.log(message.startsWith('Error'))
console.log(message.endsWith('.'))
console.log(message.endsWith('defined.'))
console.log(message.includes('foo'))

4. 参数默认值、剩余参数、数组展开

如果函数有多个参数,则有默认值的参数需要排在最后。

参数默认值:

function foo(arg1 = 10){
    console.log(arg1)
}
foo()   // 10
foo(20) //20


function bar(a, b, c = 3){
    console.log(a, b, c)
}
bar(10, 20) // 10 20 3

剩余参数:

function f2(...args){
    console.log(args) // args是存放了所有实参的数组
}
f2(1,2,3,4) // [ 1, 2, 3, 4 ]


function f3(first, ...args){ // ...args是剩余所有参数 所以只能用在形参最末尾
    console.log(first) 
    console.log(args)
}
f3(1,2,3,4)
/*
1
[ 2, 3, 4 ]
*/

数组展开:

const arr = ['foo', 'bar', 'baz']

console.log(arr[0], arr[1], arr[2])
console.log.apply(console, arr) // 这里log方法是console调用的 所以首个参数传console

console.log(...arr) // foo bar baz

5. 箭头函数

箭头函数内的this就是函数外部的this。

const person = {
    name: 'Lilei',
    sayHiAsync: function () {
        setTimeout(() => {
            console.log(this.name)
        }, 1000)
    },
}
person.sayHiAsync() // Lilei

6. 对象字面量增强

const bar = 123
const obj = {
    foo: 10,
    bar,
    // method1: function(){

    // },
    method1(){ // 省略function关键字 同用function声明等价

    },
    [1 + 2]: 456
}
console.log(obj) // { '3': 456, foo: 10, bar: 123, method1: [Function: method1] }

7. 对象的拓展方法(Object.assign、Object.is)

Object.assign:

const target = { b: 0, c: 0 }
const source1 = { a: 1, b: 2 }
const source2 = { a: 11, b: 22, c: 33 }

Object.assign(target, source1)
console.log(target) // { b: 2, c: 0, a: 1 }

const r = Object.assign(target, source1, source2)
console.log(r) // { b: 22, c: 33, a: 11 } 排在后面的参数会覆盖排在前面参数的值

Object.is:


const o1 = {id: 1}
const o2 = o1
console.log(o1 === o2)          // true
console.log(Object.is(o1, o2))  // true

console.log(Object.is(NaN, NaN)) //true
console.log(Object.is(+0, -0))   // false


8. Proxy类

Proxy类的基本用法:

const person = {
    name: 'Lily',
    age: 18
}
const personProxy = new Proxy(person, {
    get(target, property){
        return property in target ? target[property] : 'default value'
    },
    set(target, property, value){
        if(property === 'age'){
            if(!Number.isInteger(value)){
                throw new TypeError(`${value} is not an int`)
            }
        }
        target[property] = value
    }
})

console.log(personProxy.name)
personProxy.age = 20
console.log(personProxy)
console.log(personProxy.age)
/*
Lily
{ name: 'Lily', age: 20 }
20
*/

personProxy.age = 'heheda' // 报错 TypeError: heheda is not an int

ES2015 Proxy 对比 Object.defineProperty:

defineProperty 只能监视对象属性的读取和写入。

Proxy更强大 还能监视更多对对象的操作,如属性的删除 对象方法的调用等。

Proxy可监听对象属性的删除,可更好地监听数组对象,是以非侵入的方式监管了对象读写(不需要对要监视的对象本身做任何操作 就可以监视到对象内部成员的读写)。

Proxy 对比 Object.defineProperty:
可监听对象属性的删除:

const person = {
    name: 'Lily',
    age: 18
}
const personProxy = new Proxy(person, {
    deleteProperty(target, property) {
        console.log('in delete: ', property)
        delete target[property]
    }
})
delete personProxy.age
console.log(personProxy)
console.log(person)
/*
in delete:  age
{ name: 'Lily' }
{ name: 'Lily' }
*/

可更好地监听数组对象
vue2.0中想监听数组 是通过重写数组的操作方法 如push shift等
vue3.0中使用了Proxy 就不用重写数组操作方法了:

const list = []
const listProxy = new Proxy(list, {
    set(target, property, value){
        console.log('in set: ', property, value)
        target[property] = value
        return true // 表示设置成功
    }
})
listProxy.push(100)
setTimeout(() => {
    listProxy.push(200)
}, 2000)
/*
in set:  0 100
in set:  length 1
in set:  1 200
in set:  length 2
*/

9. Relect 统一的对象操作API

Refelct是一个静态类,封装了一些对对象的底层操作,推荐用它操作对象。

const obj = {
    name: 'abec',
    age: 18
}

console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
/*
true
true
[ 'name' ]
*/

10. Promise

ES2015 Promise类,详见:异步编程-学习笔记 https://mp.csdn.net/editor/html/109552446

11. class类、类的静态方法、类的继承

class Person {
    constructor(name){
        this.name = name
    }
    say(){
        console.log(`hi, my name is ${this.name}`)
    }
    static create(name){
        console.log(this) // [class Person] 静态方法里的this指向类型而不是实例
        return new Person(name)
    }
}
var p2 = new Person('Judy')
p2.say()

const p3 = Person.create('Mom')
p3.say()

class Student extends Person{
    constructor(name, number){
        super(name) // super始终指向父类 通过super调用父类的方法
        this.number = number
    }
    hello(){
        super.say() // 调用父类的say()
        console.log('my school number is: ', this.number)
    }
}
const s = new Student('jack', '100')
s.hello()

12. Set、Map

Set是一个无重复值的集合:

const s = new Set()
s.add(1).add(2).add(2)
console.log(s) // Set(2) { 1, 2 }

s.forEach(i => console.log(i))

for(let i of s){
    console.log(i)
}

// Set数据转换成Array
console.log(Array.from(set)) // [1, 2]
console.log([...set])        // [1, 2]

Map 数据结构是一个键值对对象,可用任意类型的数据作为键。

而普通对象只能用字符串作为键(传入非字符串的键会被转化为字符串作为键)。

const map = new Map()
const tom = { name: 'tom' }
map.set(tom, 90)
console.log(map) // Map(1) { { name: 'tom' } => 90 }

console.log(map.has(tom))    // true
console.log(map.delete(tom)) // true

13. Symbol

Symbol一个独一无二的符号。

字符串 和 Symbol符号 都可以作为对象的属性名。

目前Symbol最主要的作用就是为对象添加一个独一无二的属性名。


console.log(Symbol('a') === Symbol('a')) // false
const name = Symbol('name')
const obj = {
    [name]: 'Jack'
}
console.log(obj) // { [Symbol(name)]: 'Jack' }
console.log('name: ' + obj[name]) // name: Jack

Symbol.for 维护一个全局内唯一的以字符串为标识的Symbol:

const s1 = Symbol.for('name') // Symbol.for 只认字符串
const s2 = Symbol.for('name')
console.log(s1 === s2) // true

Symbol.toStringTag 修改对象toString()方法的返回值:


const obj1 = {}
console.log(obj1.toString()) // [object Object]

const obj2 = { [Symbol.toStringTag]: 'MyObject' }
console.log(obj2.toString()) // [object MyObject]

Symbol类型作为对象属性名时不可被枚举,用 for...in、Object.keys 和 JSON.stringify都访问不到,
可以用 Object.getOwnPropertySymbols 获取对象内部的Symbol的属性们:

const obj3 = {
    [Symbol('id')]: 'symbol-value',
    age: 20,
    memo: 'memo'
}
for(var key in obj3){
    console.log(key)
}
/*
age
memo
*/
console.log(Object.keys(obj3)) // [ 'age', 'memo' ]
console.log(JSON.stringify(obj3)) // {"age":20,"memo":"memo"}

// 可以用 Object.getOwnPropertySymbols 获取对象内部的Symbol的属性们
const symborProp = Object.getOwnPropertySymbols(obj3)[0]
console.log(obj3[symborProp]) // symbol-value

14. for...of循环

作为遍历所有(实现了Iterable接口的)数据结构的统一方式,还可以用break终止遍历。

可以遍历Array、Set、Map、arguments、节点列表等。
但不可遍历对象,因为对象未实现Iterable接口。

const arr = [10, 20, 30, 40]
for(const item of arr){
    console.log(item)
    if(item > 20){
        break; // 终止遍历
    }
}
/*
10
20
30
*/

15. ES2015可迭代接口、迭代器模式

实现可迭代接口Iterable:

const obj = {
    store: ['a', 'b', 'c'],
    
    [Symbol.iterator]: function(){
        let index = 0
        const self = this
        return {
            next: function(){
                const result = {
                    value: self.store[index],
                    done: index >= self.store.length
                }
                index++
                return result
            }
        }
    }
}
for(const item of obj){
    console.log('循环 ', item)
}
/*
循环  a
循环  b
循环  c
*/

迭代器模式:对外提供统一遍历接口 不用关心数据内部的结构:

// 迭代器模式: 对外提供统一遍历接口 不用关心数据内部的结构
const todos = {
    life: ['吃饭', '睡觉'],
    learn: ['Java', 'Php'],
    work: ['喝茶'],

    [Symbol.iterator]: function(){
        const all = [...this.life, ...this.learn, ...this.work]
        let index = 0
        return {
            next: function(){
                return {
                    value: all[index],
                    done: index++ >= all.length
                }
            }
        }
    }
}
for(const item of todos){
    console.log('todo: ', item)
}
/*
todo:  吃饭
todo:  睡觉
todo:  Java
todo:  Php
todo:  喝茶
*/

16. Generator生成器函数

Generator生成器函数,目的是为了避免异步编程中回调嵌套过深的问题。
调用一个生成器函数 会返回一个生成器对象。

调用生成器对象的next方法,生成器函数内部代码才开始执行,

执行到遇到的下一个yield语句则中断执行。

再次次调用生成器对象的next方法,生成器函数内部代码继续往下执行,

执行到遇到的下一个yield语句。

直到函数执行完毕。

yield 关键字后面的数据将会返回给引发它执行的next。
 

function * foo(){
    console.log('11')
    yield 100

    console.log('22')
    yield 200

    console.log('33')
    yield 300
}
const generatorObj = foo()
console.log(generatorObj.next())
/*
11
{ value: 100, done: false }
*/

console.log(generatorObj.next())
/*
22
{ value: 200, done: false }
*/

console.log(generatorObj.next())
/*
33
{ value: 300, done: false }
*/

Generator生成器函数应用:
案例1 发号器:

// 案例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 使用Generator函数实现 iterator 方法:

const todos = {
    life: ['吃饭', '睡觉'],
    learn: ['Java', 'Php'],
    work: ['喝茶'],

    // [Symbol.iterator]: function(){
    //     const all = [...this.life, ...this.learn, ...this.work]
    //     let index = 0
    //     return {
    //         next: function(){
    //             return {
    //                 value: all[index],
    //                 done: index++ >= all.length
    //             }
    //         }
    //     }
    // }

    [Symbol.iterator]: function * () {
        const all = [...this.life, ...this.learn, ...this.work]
        for(const item of all){
            yield item
        }
    }
}
for(const item of todos){
    console.log('todo: ', item)
}
/*
todo:  吃饭
todo:  睡觉
todo:  Java
todo:  Php
todo:  喝茶
*/


17. ES Modules - 语言层面的模块语法

ES Modules 语言层面的模块化规范,此处暂不详说。


三. ECMAScript2016新特性

ECMAScript2016新增了两个小功能 数组的includes方法、指数运算符 ** 

const arr = ['a', 11, NaN]
console.log(arr.includes('a')) //true
console.log(arr.indexOf(NaN))  // -1 indexOf无法检测NaN的存在
console.log(arr.includes(NaN)) // true includes可以检测NaN

// 求2的10次方 2是底数 10是指数
console.log(Math.pow(2, 10)) // 1024
console.log(2 ** 10)         // 1024

四. ECMAScript2017新特性

ECMAScript2017新增了如下新特性:

Object.values 和 Object.entries

Object.getOwnPropertyDescriptors

String.prototype.padStart / String.prototype.padEnd(为字符串前后填充字符)

允许在函数参数中添加尾逗号

标准化了 Async / Await (使用promise的语法糖)。

 

示例:

Object.values 和 Object.entries:

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

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

console.log(new Map(Object.entries(obj)))
/*
Map(2) { 'foo' => 'value1', 'bar' => 'value2' }
*/

Object.getOwnPropertyDescriptors:

const p1 = {
    firstName: 'Lei',
    lastName: 'Wang',
    get fullName(){
        return this.firstName + ' ' + this.lastName
    }
}
console.log(p1.fullName) // Lei Wang

// 不能正确拷贝带有 get 方法的对象
const p2 = Object.assign({}, p1)
console.log(p2) // { firstName: 'Lei', lastName: 'Wang', fullName: 'Lei Wang' }
p2.firstName = 'Abc'
console.log(p2) // { firstName: 'Abc', lastName: 'Wang', fullName: 'Lei Wang' }

// 用Object.getOwnPropertyDescriptors 配合 Object.defineProperties正确拷贝
const descriptors = Object.getOwnPropertyDescriptors(p1)
const p22 = Object.defineProperties({}, descriptors)
console.log(p22) // { firstName: 'Lei', lastName: 'Wang', fullName: [Getter] }
p22.firstName = 'Bbb'
console.log(p22, p22.fullName) // { firstName: 'Bbb', lastName: 'Wang', fullName: [Getter] } Bbb Wang

String.prototype.padStart / String.prototype.padEnd:

const obj = {
    name: 'Juddy',
    age: 22,
    number: 123456
}
for(const [key, value] of Object.entries(obj)){
    const text = `${key} | ${value}`
    console.log(text)
}
/* 打印效果不整齐
name | Juddy
age | 22
number | 123456
*/

for(const [key, value] of Object.entries(obj)){
    const text = `${key.padEnd(10, '-')} | ${value.toString().padStart(10, ' ')}`
    console.log(text)
}
/* 打印效果整齐
name------ |      Juddy
age------- |         22
number---- |     123456
*/

允许在函数参数中添加尾逗号:

function add (a, b,){
    console.log('arguments: ', arguments.length) // arguments:  2
}
add(1, 2)
console.log('length: ', add.length) // length:  2

本文 完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值