ECMAScript和JavaScript

ECMAScriptJavaScript

ECMAScriptJavaScript的标准化规范,实际上JavaScriptECMAScript的扩展语言,ECMAScript提供最基本语法,停留在语言层面;而JavaScript在此基础上做扩展,浏览器端的JavaScript等于ECMAScriptWeb所提供的APIDOMBOM);Node环境中的JavaScript等于ECMAScriptNode所提供的API

ECMAScript的发展过程
image-20210108082301205.png
ECMAScript 2015

可以叫做ES6,主要有4个大类的提升

  • 解决原有语法上的一些问题或不足(letconst提供块级作用域)
  • 对原有语法进行增强、更易用(解构、展开运算符、模板字符串、函数默认值等等)
  • 全新的对象、全新的方法、全新的功能 (PromiseProxyObject.assign)
  • 全新的数据类型和数据结构(SetMap
新特性
  • letconst

    解决块没有单独作用域问题,letconst定义的变量只能在当前作用域使用

    if (true) {
      var foo = 'foo'
    }
    console.log(foo)
    if (true) {
      let foo = 'foo'
    }
    console.log(foo) // foo is not undefined
    
    // 循环绑定事件,事件处理函数中获取正确索引
    var ele = [{}, {}, {}]
    for (var i = 0;i < ele.length; i++) {
        // ele[i].onclick = function() {
        //    console.log(i)
        //}
        // 使用闭包创建函数作用域 摆脱全局作用域影响
        elements[i].onclick = (function (i) {
            return function () {
                console.log(i)
            }
        })(i)
    }
    ele[2].onclick() // 3
    ele[2].onclick() // 2
    // 使用let块级作用域
    for (let i = 0;i < ele.length; i++) {
       ele[i].onclick = function() {
          console.log(i)
       } 
    }
    

    let没有变量提升

    console.log(foo)
    var foo = 'sdf'
    
    console.log(foo)
    let foo = 'sdf' // foo is not undefined
    

    const常量,只读的let,声明之后不能再修改(指不能修改指向的内存地址,可以修改其中的属性成员)

    // 恒量只是要求内层指向不允许被修改
    const obj = {}
    // 对于数据成员的修改是没有问题的
    obj.name = 'zce'
    obj = {} // Assignment to constant variable.
    
  • 数组的解构Destructruing

    const arr = [100, 200, 300]
    const [foo, bar, baz] = arr // 100 200 300
    const [foo, ...rest] = arr
    console.log(rest) // [ 200, 300 ]
    
  • 对象的解构Destructuring

    // 简单对象
    const obj = {
        name: 'wang',
        age: 24
    }
    const { name, age } = obj // wang 24
    // 嵌套对象
    const obj2 = {
      worker: {
            name: 'wang',
            sex: '男' 
        },
        address: 'zhognguo'
    }
    const { worker: { name }, address } // wang zhognguo
    // 默认值
    const obj3 = {
      name: 'wang',
        age: 24
    }
    const { name, age, sec = '女' } = obj // wang 24 女
    
  • 模板字符串字面量Template literals

    // 反引号包裹
    const a = `hello world` // hello world
    // 允许换行
    const b = `hello es2015,
    this is a \`string\`` // hello es2015,
    this is a `string`
    // 输出模板
    $('#contioner').append(`
      There are ${count} items
       in your basket, ${onSale}
      are on sale!
    `);
    // 可以使用 ${} 插入表达式
    const fruits = 'apple'
    const c = `this is ${fruits}`  // this is apple
    
  • 模板字符串标签函数Tagged Templates

    // 模板字符串标签是一个特殊的函数 使用这个标签就是调用这个函数
    const str = console.log`hello world` // ["hello world", raw: Array(1)]
    // strings:字符串数组,以${}分隔符分割得到的数组
    // 其余的参数与表达式相关 ${}的值
    function Tag(strings, str,str2,...)
    const name = 'tom'
    const gender = false
    function myTagFunc (strings, name, gender) {
      const sex = gender ? 'man' : 'woman'
      return strings[0] + name + strings[1] + sex + strings[2]
    }
    const result = myTagFunc`hey, ${name} is a ${gender}.` // hey, tom is a woman.
    
  • 字符串的扩展用法

    // includes 返回布尔值,表示是否找到参数字符串
    // startsWith 返回布尔值,表示参数字符串是否在源字符串开头
    // endsWith 返回布尔值,表示参数字符串是否在源字符串尾部
    const message = 'Error: foo is not defined.'
    console.log(
      message.startsWith('Error'), // true
      message.endsWith('.'),  // true
      message.includes('foo') // true
    )
    // 支持两个参数 表示开始搜索位置
    let s = 'Hello world!';
    s.startsWith('world', 6) // true
    s.endsWith('Hello', 5) // true
    s.includes('Hello', 6) // false
    
  • 参数默认值Default parameters

    // ES6之前 函数默认值使用短路运算符
    function log (x, y) {
        // 如果y传入false 则下面赋值无效
        // 需要先判断y是否被赋值
        if (typeof y === 'undefined') y = 'world'
      y = y || 'world'
        console.log(y)
    }
    // 通常情况 函数默认值应该在函数的尾参数
    function foo(a, enable = true) 
    // 非尾部设置 这个参数需要穿undefined
    function foo(a = 'a', enable = true) {
      console.log('foo invoked - enable: ')
      console.log(enable)
      console.log(a)
    }
    // 只有参数值为undefied时才会使用默认值
    foo(undefined, false)
    // 函数length属性 返回没有指定默认值的参数个数
    foo.length // 0
    
  • 剩余参数Rest parameters

    // ...rest剩余参数 用于获取函数的多余参数 代替arguments
    // rest参数之后不能再有其他参数
    function foo (first, ...args) {
      console.log(args)
    }
    foo(1,2 ,3, 4) // [2, 3, 4]
    // 使用rest替代arguments例子
    function sortNumbers () {
      return Array.prototype.slice.call(arguments).sort()
    }
    const sortNumbers = (...numbers) => numbers.sort()
    sortNumbers(1, 2, 4, 6, 2, 3) //  [1, 2, 2, 3, 4, 6]
    
  • 展开数组Spread

    // 扩展运算符... 好比reset参数的逆运算
    console.log(...[1,2,3]) // 1 2 3
    
    // 1.复制数组
    const a1 = [1, 2]
    const a3 = [3]
    // const a2 = a1 // 复制指向底层数据结构的指针 并不是克隆全新的数组
    const a2 = [...a1]
    // const [...a2] = a1
    console.log(a2)
    
    // 2.合并数组
    // ES5的合并
    // let a4 = a1.concat(a3) // [1, 2, 3]
    // ES6的合并
    let a4 = [...a1, ...a3]
    console.log(a4)
    console.log(a2 === a1, a4[0] === a1[0])
    // 上述两种都是浅拷贝
    
    // 3.与解构赋值结合
    const list = [1, 2, 3, 4, 5]
    let [a, ...rest] = list
    console.log(a, rest) // 1 [ 2, 3, 4, 5 ]
    const [first, ...last] = []
    console.log(first, last) // undefined []
    
    // 4.转换字符串为真正数组
    console.log([...'hello'])
    // ...运算符能识别四个字节的Unicode字符 js会将Unicode字符转换为2个字符
    console.log('x\uD83D\uDE80y'.length) // 4
    console.log([...'x\uD83D\uDE80y'].length) // 3
    
    // 5.// 任何实现了Iterator接口的对象 都可以使用扩展运算符
    Number.prototype[Symbol.iterator] = function* () {
      let i = 0
      const num = this.valueOf()
      while (i < num) {
        yield i++
      }
    }
    console.log([...5]) // [ 0, 1, 2, 3, 4 ]
    // Map、Set结构和Generator函数
    let map = new Map([
      [1, 'one'],
      [2, 'two'],
      [3, 'three'],
    ])
    let keys = [...map.keys()]
    console.log(keys) // [ 1, 2, 3 ]
    let set = new Set([1, 2, 3, 4, 5, 2, 21, 1])
    console.log([...set]) // [ 1, 2, 3, 4, 5, 21 ]
    
    const go = function* () {
      yield 1
      yield 2
      yield 3
    }
    console.log([...go()]) // [ 1, 2, 3 ]
    
  • 箭头函数Arrow Functions

    const arr = [1, 2, 3, 4, 5, 6, 7]
    
    // arr.filter(function (item) {
    //   return item % 2
    // })
    
    // 常用场景,回调函数
    console.log(arr.filter((i) => i % 2)) // [ 1, 3, 5, 7 ]
    // 1.函数体内this对象,定义时的对象,而不是使用时的对象
    // 2.arguments对象在函数体内不存在,可以使用rest参数替代
    // 3.不能使用yield命令,箭头函数不能用作Generator对象
    // 箭头函数与 this
    // 箭头函数不会改变 this 指向
    
    const person = {
      name: 'tom',
      // sayHi: function () {
      //   console.log(`hi, my name is ${this.name}`)
      // }
      // 定义对象的方法,且该方法内部包括this 不应该使用箭头函数
      sayHi: () => {
        console.log(`hi, my name is ${this.name}`)
      },
      sayHiAsync: function () {
        // const _this = this
        // setTimeout(function () {
        //   console.log(_this.name)
        // }, 1000)
    
        console.log(this)
        setTimeout(() => {
          // console.log(this.name)
          console.log(this)
        }, 1000)
      }
    }
    person.sayHi() // hi, my name is undefined
    person.sayHiAsync()
    
  • 对象字面量增强Enhanced object literals

    // 对象字面量
    const bar = '345'
    const obj = {
      foo: 123,
      // bar: bar
      // 属性名与变量名相同,可以省略 : bar
      bar,
      // method1: function () {
      //   console.log('method111')
      // }
      // 方法可以省略 : function
      method1 () {
        console.log('method111')
        // 这种方法就是普通的函数,同样影响 this 指向。
        console.log(this)
      },
      // 属性名表达式
      [bar]: 123,
      ['a' + 'bc']: 123
    }
    
  • 对象扩展方法Object.assignObject.is

    // Object.assign() 用于对象的合并 将源对象可枚举属性 复制到目标对象
    const target = { a: 1, b: 1 }
    const source1 = { b: 2, c: 2 }
    const source2 = { c: 3 }
    Object.assign(target, source1, source2)
    target // {a:1, b:2, c:3}
    // 也可用于对象深拷贝
    const obj = { name: 'global obj' }
    const funcObj = Object.assign({}, obj)
    funcObj.name = 'func obj'
    console.log(funcObj) //{ name: 'func obj' } 
    console.log(obj) // { name: 'global obj' }
    // Object.is用于比较两个值严格相等 与===一致
    Object.is('foo', 'foo') // true
    Object.is({}, {}) // false
    // 不同之处
    +0 === -0 //true
    NaN === NaN // false
    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true
    
  • Proxy代理对象

    // 为对象设置访问代理器 可以理解为在目标对象之前架设一层“拦截” 外界访问该对象 都必须通过这层拦截
    // 轻松监视对象的属性读写
    const person = {
      name: 'zce',
      age: 20,
    }
    const personProxy = new Proxy(person, {
      // 监视属性读取
      get(target, property) {
        // 先判断属性是否存在
        return property in target ? target[property] : 'default'
        // console.log(target, property)
        // return 100
      },
      // 监视属性设置
      set(target, property, value) {
        if (property === 'age') {
          if (!Number.isInteger(value)) {
            throw new TypeError(`${value} is not an int`)
          }
        }
        target[property] = value
        // console.log(target, property, value)
      },
    })
    personProxy.age = 100
    personProxy.gender = true
    console.log(personProxy.name) // zce
    console.log(personProxy.xxx) // default
    
  • Proxy vs Object.defineProperty()

    // Object.defineProperty()只能监视对象中的属性读写 Proxy能监视更多对象操作
    const personProxy = new Proxy(person, {
      deleteProperty (target, property) {
        console.log('delete', property)
        delete target[property]
      }
    })
    delete personProxy.age
    console.log(person) // { name: 'zce' }
    // Proxy更好的支持数组对象的监视
    const list = []
    const listProxy = new Proxy(list, {
      set(target, property, value) {
        console.log('set', property, value)
        target[property] = value
        return true // 表示设置成功
      },
    })
    listProxy.push(100) // set 0 100 set length 1
    // Proxy 非侵入方式监管对象读写 已定义好的对象 不需要对对象本身做操作 就可监视对象的操作
    Object.defineProperty(person,'name',{
      get(){
        console.log("name 被访问...");
        return person._name;
      },
      set(newValue){
        console.log("name 被设置 ...");
        person._name = newValue;
      }
    })
    person.name = 'wang'// name 被设置 ...
    console.log(person.name) // name 被访问 ... wang
    const personProxy = new Proxy(person, {
      get(target, property) {
          return target[property];
      },
      set(target, property, value) {
          return (target[property] = value);
      }
    });
    
image-20210109173128182.png
  • Reflect

    // Reflect封装一些列对对象的底层操作
    // 现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上
    // 价值 提供一套用于操作对象的API
    const obj = {
      name: 'zce',
      age: 18
    }
    console.log('name' in obj)
    console.log(delete obj['age'])
    console.log(Object.keys(obj))
    // 让Objkect操作都变成函数行为
    console.log(Reflect.has(obj, 'name'))
    console.log(Reflect.deleteProperty(obj, 'age'))
    console.log(Reflect.ownKeys(obj))、
    
image-20210109174946320.png
  • Class

    // class 关键词
    // 生成实例对象的传统方法是通过构造函数
    function Person(name) {
      this.name = name
    }
    
    Person.prototype.say = function () {
      console.log(`hi, my name is ${this.name}`)
    }
    
    class Person {
      // constructor()方法是类的默认方法 通过new命令生成对象实例时,自动调用该方法
      constructor(name) {
        this.name = name
      }
      // 静态方法 不会被实例继承 直接通过类调用
      static create(name) {
        return new Person(name)
      }
      say() {
        console.log(`hi, my name is ${this.name}`)
      }
    }
    
    const p = new Person('tom')
    p.say()
    
    const xiaoming = Person.create('xiaoming')
    xiaoming.say()
    // 类的继承
    class Student extends Person {
      constructor (name, number) {
        super(name) // 父类构造函数
        this.number = number
      }
      hello () {
        super.say() // 调用父类成员
        console.log(`my school number is ${this.number}`)
      }
    }
    const s = new Student('jack', '100')
    s.hello() // hi, my name is jack my school number is 100
    
  • Set数据结构

    // 类似于数组,但是成员的值都是唯一的
    const s = new Set()
    [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
    for (let i of s) {
      console.log(i)
    }
    // 作用去除数组的重复成员
    const array = [1,2,3,4,2,1,23,4]
    [...new Set(array)] // [1,2,3,4,23]
    
  • Map数据结构

    // Object对象 键值对集合 只能通过字符串作为键
    // Map数据结构 “键”的范围不限于字符串
    const m = new Map()
    const tom = { name: 'tom' }
    m.set(tom, 90)
    console.log(m) // Map(1) { { name: 'tom' } => 90 }
    console.log(m.get(tom)) // 90
    // map操作
    // 1.转为数组
    const arr = [...m]
    console.log(arr) // [ [ { name: 'tom' }, 90 ], [ { name: 'tom' }, 80 ] ]
    // 2.数组转为map
    console.log(new Map([arr])) // Map(1) { [ { name: 'tom' }, 90 ] => [ { name: 'tom' }, 80 ] }
    // 3.Map转为对象 Map的键被转为字符串
    function strMapToObj(strMap) {
      let obj = Object.create(null)
      for (let [k, v] of strMap) {
        obj[k] = v
      }
      return obj
    }
    console.log(strMapToObj(m)) // [Object: null prototype] { '[object Object]': 80 } 同名属性被覆盖
    // 4.对象转Map
    let obj = { a: 1, b: 2 }
    let map = new Map(Object.entries(obj))
    console.log(map) // Map(2) { 'a' => 1, 'b' => 2 }
    
  • Symbol

    // ES6 引入 原始数据类型Symbol,表示独一无二的值
    // 数据类型:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)、Symbol
    // 对象属性名:一种原有字符串 另一种新增的 Symbol 类型(保证不会与其他属性名产生冲突)
    const s = Symbol()
    console.log(s) // Symbol()
    console.log(typeof s) // symbol
    
    // 两个 Symbol 永远不会相等
    console.log(Symbol() === Symbol()) // false
    // 使用 Symbol 为对象添加用不重复的键
    const obj = {}
    obj[Symbol()] = '123'
    obj[Symbol()] = '456'
    console.log(obj) // { Symbol(): "123", Symbol(): "456" }
    
    // 内置 Symbol 常量
    
    console.log(Symbol.iterator)
    console.log(Symbol.hasInstance)
    const obj = {
      [Symbol.toStringTag]: 'XObject'
    }
    console.log(obj.toString())
    
    // Symbol 属性名获取
    const obj = {
      [Symbol()]: 'symbol value',
      [Symbol()]: 'symbol vaasdlue',
      foo: 'normal value',
    }
    console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(), Symbol() ]
    
  • Iterator(遍历器)和for...of循环

    // Iterator是一种接口 为所有不同的数据结构提供统一访问机制 只要部署Iterator接口 就可以完成遍历操作
    // 只要实现Iterator接口 就可以被for...of消费
    // 对象实现可迭代接口
    const obj = {
      store: ['foo','bar','baz'],
        [Symbol.iterator]: function() {
            let index = 0
            const self = this
            return {
                next: function () {
                    const result = {
                        value: self.store[index],
                        down: index >= self.store.length
                    }
                    index++
                    return result
                }
            }
        }
    }
    for (let item of obj) {
      console.log(item)
    } // foo bar baz
    
    // for...of循环内部调用的是数据结构的Symbol.iterator方法
    // for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串
    // 与其他遍历语法比较
    const arr = [1, 2, 3, 4]
    for (var i = 0; o < array.length; i++) {
      console.log(array[i])
    }
    // 写法较麻烦 使用forEach
    array.forEach((value, index) => {
      console.log(value, index)
    })
    // 此时 无法跳出forEach循环 break和return命令都不能奏效
    for (let value of array) {
      console.log(value)
    }
    // 优点:它可以与break、continue和return配合使用
    // 提供了遍历所有数据结构的统一操作接口
    
  • ES Modules

    待补充。。。

你可能感兴趣的:(ECMAScript和JavaScript)