美文网首页程序员
ECMAScript和JavaScript

ECMAScript和JavaScript

作者: 翔子丶 | 来源:发表于2021-01-09 18:45 被阅读0次
    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 <b>${count}</b> items
         in your basket, <em>${onSale}</em>
        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

        本文链接:https://www.haomeiwen.com/subject/lyzcaktx.html