ECMAScript新特性

JavaScript是ECMAScript的扩展语言,ECMAScript只提供了基础的语法。
在浏览器环境中:js = Bom + Dom + ECMAScript在浏览器环境
在浏览器环境中:js = fs + net+ ECMAScript +etc.
在nodejs环境中
JavaScript语言本身指的就是ECMAScript。

ES2015新特性:

  • 解决原有语法上的一些问题或者不足(例如let,const所提供的块级作用域)
  • 对原有语法进行增强(例如解构,展开,模板字符串)
  • 全新的对象,全新的方法,全新的功能(例如promise)
  • 全新的数据类型和数据结构(例如symbol,set,map)
let与块级作用域
// let 声明的成员只会在所声明的块中生效
if (true) {
  // var foo = 'zce'
  let foo = 'zce'
  console.log(foo)
}
// let 应用场景:循环绑定事件,事件处理函数中获取正确索引
// 使用var 只能配合闭包保留当前变量值
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
  elements[i].onclick = (function (i) {
    return function () {
      console.log(i)
    }
  })(i)
}
elements[0].onclick()
var elements = [{}, {}, {}]
for (let i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
}
elements[0].onclick()
// for 循环会产生两层作用域 ----------------------------------
for (let i = 0; i < 3; i++) { // 第一层
  let i = 'foo' // 第二层
  console.log(i)
}
// let 修复了变量声明提升现象 --------------------------------------------
console.log(foo)
var foo = 'zce'
console.log(foo)
let foo = 'zce'
const
const name = 'zce'
// 恒量声明过后不允许重新赋值
name = 'jack'
// 恒量要求声明同时赋值
const name
name = 'zce'
// 恒量只是要求内层指向不允许被修改
const obj = {}
// 对于数据成员的修改是没有问题的
obj.name = 'zce'
数组的解构
const arr = [100, 200, 300]
const [foo, bar, baz] = arr
console.log(foo, bar, baz)
// 没有匹配到的变量名返回undefined
对象的解构
const obj = { name: 'zce', age: 18 }
const { name } = obj
console.log(name);
// 解构重命名
const { name: objName } = obj
console.log(objName)
// 解构赋默认值
const { name: objName = 'jack' } = obj
// 解构console.log
const { log } = console
log('foo')
模板字符串
// 反引号包裹
const str = `hello es2015, this is a string`
// 允许换行
const str = `hello es2015,
this is a \`string\``
console.log(str)
const name = 'tom'
// 可以通过 ${} 插入表达式,表达式的执行结果将会输出到对应位置
const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}`
// 带标签的模板字符串
const str = console.log`hello world` // [ 'hello world' ]
const name = 'tom'
const gender = true
function myTagFunc (strings, name, gender) {
    console.log(strings, name, gender) // [ 'hey, ', ' is a ', '.' ] tom true
    const sex = gender ? '男人' : '女人'
    return strings[0] + name + strings[1] + sex + strings[2];
}
const result = myTagFunc`hey, ${name} is a ${gender}.`
console.log(result);
字符串的扩展方法
const message = 'Error: foo is not defined.'
console.log(
  message.startsWith('Error'), // 字符串是否以Error开头
  message.endsWith('.'), // 字符串是否以.结尾
  message.includes('foo') // 字符串是否包含foo
)
参数默认值
// 默认参数一定是在形参列表的最后,没有传入实参,或者实参为undefind
function foo (enable = true) {
  console.log('foo invoked - enable: ')
  console.log(enable)
}
foo(false)
剩余参数
// function foo () {
//   console.log(arguments) // 之前用arguments数组接收
// }
// 只能放在形参的最后一位,且只能使用一次
function foo (first, ...args) {
  console.log(args)
}
foo(1, 2, 3, 4)
展开数组
const arr = ['foo', 'bar', 'baz']
// console.log.apply(console, arr) // 之前用apply
console.log(...arr)
箭头函数
const inc = n => n + 1
// 完整参数列表,函数体多条语句,返回值仍需 return
const inc = (n, m) => {
  console.log('inc invoked')
  return n + 1
}
console.log(inc(100))
const arr = [1, 2, 3, 4, 5, 6, 7]
// 常用场景,回调函数
arr.filter(i => i % 2)
// ------------------箭头函数与this----------------
// 箭头函数没有this的机制,所以不会改变this的指向
const person = {
  name: 'tom',
  // sayHi: function () {
  //   console.log(`hi, my name is ${this.name}`)
  // }
  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.sayHiAsync()
对象字面量的增强
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)
  },
  // Math.random(): 123 // 不允许
  // 通过 [] 让表达式的结果作为属性名
  [bar]: 123
}
// obj[Math.random()] = 123 // 计算属性名
console.log(obj)
obj.method1()
Object.assign

将多个源对象中的属性复制到一个目标对象中(用后面对象中的属性覆盖第一个对象)

const source1 = {
    a: 123,
    b: 321,
    c: 333
}
const source2 = {
    a: 789,
    b: 987
}
const target = {
    a: 456,
    b: 654
}
const result = Object.assign(target, source1, source2)
console.log(target); // { a: 789, b: 987, c: 333 }
console.log(result); // { a: 789, b: 987, c: 333 }
console.log(result === target); // true
// 应用场景
function func (obj) {
    // obj.name = 'fun obj'
    // console.log(obj) // 会影响外部对象
    const funcObj = Object.assign({}, obj);
    funcObj.name = 'func obj'
    console.log(funcObj)

}
const obj = {name: 'global obj'}
func(obj)
console.log(obj)
Object.is
console.log(
   0 == false,             // => true
   0 === false,             // => false
   +0 === -0,             // => true
   NaN === NaN,             // => false
   Object.is(+0, -0),      // => false
   Object.is(NaN, NaN)     // => true
)
proxy
// 基本用法
const person = {
    name: 'zce',
    age: 9
}
const personProxy = new Proxy(person, {
    get (target, Property) {
        console.log(target)
        console.log(Property)
        return Property in target ? target[Property] : 'defult'
    },
    set (target, property, value) {
        if (property === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError(`${value} is not an int`)
            }
        }
        target[property] = value
    }
})
personProxy.age = 10
personProxy.age = 'aaa'
console.log(personProxy.age)
console.log(personProxy.aaa)

proxy和defineProperty对比
definedProperty只能监听属性的读写
proxy能够监听到更多对象的操作(如对对象方法的调用等)
proxy可以更好的监听数组对象的监视
proxy是以非侵入的方式监管了对象的读写,而defineProperty是需要单独定义对象当中需要被监视的属性

const person = {
    name: 'zce',
    age: 9
}
const personProxy = new Proxy(person, {
    deleteProperty (target, property) {
        console.log('delete', property)
        delete target[property]
    }
})
delete personProxy.age // delete age可以监听到对象的delete操作
// ----------监听数组对象的操作-------------------
const list = []
const listProxy = new Proxy(list, {
    set (target, property, value) {
        console.log('set', property, value); // set 0 100 set length 1
        target[property] = value
        return true
    }
})
listProxy.push(100)
Reflect(静态类)

Reflect内部封装了一系列对对象的底层操作
Reflect成员方法就是Proxy处理对象的默认实现,统一提供了对象的操作方式

const person = {
    name: 'zce',
    age: 9
}
// console.log('name' in person);
// console.log(delete person['age'])
// console.log(Object.keys(person))
console.log(Reflect.has(person, 'name'))
console.log(Reflect.deleteProperty(person, 'age'))
console.log(Reflect.ownKeys(person))
promise

解决了传统异步编程中回调函数嵌套过深的问题,详见异步编程

class类
// 之前使用
// function Person (name) {
//   this.name = name
// }
// Person.prototype.say = function () {
//   console.log(`hi, my name is ${this.name}`)
// }
// es6中
class Person {
  constructor (name) {
    this.name = name
  }
  say () {
    console.log(`hi, my name is ${this.name}`)
  }
  // 静态方法(注意:静态方法是挂载到类型上面的,所以在静态方法内部,他的this就不会去指向某一个实例对象,而是当前的类型
  static create (name) {
    return new Person(name)
  }
}
// ----------类的继承----------------
class Student extends Person {
  constructor (name, number) {
    super(name) // 父类构造函数(调用它就等于调用父类的构造函数)
    this.number = number
  }
  hello () {
    super.say() // 调用父类成员
    console.log(`my school number is ${this.number}`)
  }
}
//const p = new Person('tom')
const tom = Person.create('tom')
//p.say()
tom.say()
const s = new Student('jack', '100')
s.hello()
Set
const s = new Set()
s.add(1).add(2).add(3).add(4).add(2)
s.forEach(i => console.log(i))
for (let i of s) {
  console.log(i)
}
console.log(s.size)
console.log(s.has(100))
console.log(s.delete(3))
console.log(s)
s.clear() // 清除当前集合当中的全部内容
console.log(s)
// 应用场景:数组去重
const arr = [1, 2, 1, 3, 4, 1]
// 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))
// console.log(obj['[object Object]']) 所有的key都会被toString
// Map和对象最大的不同就是map可以以任意类型的值作键,而对象只能以String类型的值作为键
const m = new Map()
const tom = { name: 'tom' }
m.set(tom, 90)
console.log(m)
console.log(m.get(tom))
// m.has()
// m.delete()
// m.clear()
m.forEach((value, key) => {
  console.log(value, key)
})
Symbol

一种全新的原始数据类型,独一无二的值,两个 Symbol 永远不会相等
对象的键除了支持string还支持symbol,因为可以用symbol来避免对象属性名重复的问题

// Symbol 描述文本
// console.log(Symbol('foo'))
// console.log(Symbol('bar'))
// console.log(Symbol('baz'))
// 使用 Symbol 为对象添加用不重复的键
const obj = {}
obj[Symbol()] = '123'
obj[Symbol()] = '456'
console.log(obj)
// 案例2:Symbol 模拟实现私有成员
const name = Symbol()
const person = {
  [name]: 'zce',
  say () {
    console.log(this[name])
  }
}
// 由于无法创建出一样的 Symbol 值,
// 所以无法直接访问到 person 中的「私有」成员
// person[Symbol()]
person.say()
// Symbol 全局注册表 ---------------------------------------
const s1 = Symbol.for('foo') // (如果要全局复用symbol类型的值,可以使用for,相同的字符串一定会返回相同的symbol的值)
const s2 = Symbol.for('foo')
console.log(s1 === s2)
console.log(
  Symbol.for(true) === Symbol.for('true') // 如果传入的不是字符串会变成字符串
)
// -------------
const obj = {
  [Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString()) // [object Xobject]
// Symbol 属性名获取 -----------------------------------------
const obj = {
  [Symbol()]: 'symbol value',
  foo: 'normal value'
}
// for (var key in obj) {
//   console.log(key) // 获取不到Symbol属性名
// }
// console.log(Object.keys(obj)) // 获取不到Symbol属性名
// console.log(JSON.stringify(obj)) // 获取不到Symbol属性名
console.log(Object.getOwnPropertySymbols(obj))// 获取到的全是symbol类型的属性名
for…of

forEach 无法跳出循环,必须使用 some 或者 every 方法
for…of可以遍历set,map

const arr = [100, 200, 300, 400]
for (const item of arr) {
   console.log(item)
}
// for...of 循环可以替代 数组对象的 forEach 方法
// arr.forEach(item => {
//   console.log(item)
// })
 for (const item of arr) {
   console.log(item)
   if (item > 100) {
     break
   }
 }
// forEach 无法跳出循环,必须使用 some 或者 every 方法
// arr.forEach() // 不能跳出循环
// 遍历 Set 与遍历数组相同
const s = new Set(['foo', 'bar'])
for (const item of s) {
   console.log(item)
}
// 遍历 Map 可以配合数组结构语法,直接获取键值
 const m = new Map()
 m.set('foo', '123')
 m.set('bar', '345')
 for (const [key, value] of m) {
   console.log(key, value)
 }
// 普通对象不能被直接 for...of 遍历
 const obj = { foo: 123, bar: 456 }
 for (const item of obj) {
	console.log(item)
 }
迭代器(Iterator)

所有可以被for…of…遍历的数据类型都必须实现interator的接口,也就是在内部必须挂载一个iterator的方法,该方法返回一个next方法,然后通过不断的调用next方法,实现对内部所有数据的遍历。

// 迭代器(Iterator)
// 所可以被for..of循环的对象内部都有迭代器(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())
while (true) {
  const current = iterator.next()
  if (current.done) {
    break // 迭代已经结束了,没必要继续了
  }
  console.log(current.value)
}

实现可迭代接口

const obj = {
  store: ['foo', 'bar', 'baz'],
  [Symbol.iterator]: function () { // iterable
    let index = 0
    const self = this
    return {
      next: function () { // iterator
        const result = { // iterationResult
          value: self.store[index],
          done: index >= self.store.length
        }
        index++
        return result
      }
    }
  }
}
for (const item of obj) {
  console.log('循环体', item)
}

迭代器模式

const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],
  // 提供统一遍历访问接口
  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for (const item of all) {
      callback(item)
    }
  },
  // 提供迭代器(ES2015 统一遍历访问接口)
  [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
        }
      }
    }
  }
}
todos.each(function (item) {
  console.log(item)
})
console.log('-------------------------------')
for (const item of todos) {
  console.log(item)
}

迭代器的意义就是对外部提供统一的接口,让外部不再去关心内部的数据结构。

Generator

解决异步编程中回调嵌套过深的问题。详见异步编程

// 案例1:发号器
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)

// 案例2:使用 Generator 函数实现 iterator 方法
const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],
  [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(item)
}
ES2016
  • 新增includes代替indexOf,解决了indexOf找不到NAN的问题
  • 新增指数运算符2**10,代替原本的Math.pow(2,10)
ES2017
  • Object.values, Object.entries=>以数组的形式遍历对象当中的键值对
for (const [name, count] of Object.entries(books)) {
  console.log(name, count)
}
  • Object.getOwnPropertyDescriptors 获取对象当中属性的完整描述信息,可以配合get、set使用
const p1 = {
  firstName: 'Lei',
  lastName: 'Wang',
  get fullName () {
    return this.firstName + ' ' + this.lastName
  }
}
// console.log(p1.fullName)
// const p2 = Object.assign({}, p1)
// p2.firstName = 'zce'
// console.log(p2)
const descriptors = Object.getOwnPropertyDescriptors(p1)
// console.log(descriptors)
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'zce'
console.log(p2.fullName)
  • String.prototype.padStart,String.prototype.padEnd
  • 用指定字符串填充目标字符串开始或结束的位置,知道字符串长度达到指定长度。
for (const [name, count] of Object.entries(books)) {
  console.log(`${name.padEnd(16, '-')}|${count.toString().padStart(3, '0')}`)
}
  • 尾逗号
    给函数参数添加尾逗号,在结束的位置去添加一个逗号,可以让源代码管理工具更精确的定位到代码当中实际发生变化的位置。
function foo (bar,baz,) {
}
const arr = [
  100,
  200,
  300,
]

以上就是ECMAScript新特性的笔记啦,收货很多,继续加油呀~~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值