美文网首页
ECMAScript新特性(一)

ECMAScript新特性(一)

作者: 洲行 | 来源:发表于2020-10-05 02:33 被阅读0次
ECMAScript是什么

ECMAScript(ES),也是一门脚本语言,通常把它看作为JavaScript(JS)的标准化规范,实际上JS是ES的扩展语言,因为ES只提供了最基本的语法(如定义变量,for循环等),JS实现了ES语言的标准,并且又做了扩展,使得我们可以在浏览器环境中操作BOM和DOM,在Node环境中,可以去读写文件这种操作


浏览器环境中

这是在浏览器环境中,js包含es和浏览器提供的API,这些API又包含DOM和BOM


Node环境中
这是在node环境中,js包含es和node提供的API,这些API又包含fs(用于操作系统中的文件),net(用于底层的网络通信),etc.
所以ES就是JS语言本身

ES版本

2015年开始,ES保持每年一个版本的迭代,所以现在变得越来越高级,便捷


目前的所有版本

从2009年发布的5版本到其中的ES2015,经历了6年时间才被完全标准化,这6年时间,也是web发展的黄金时间,所以这个版本包含了很多颠覆式的新功能,由于ES2015迭代的时间太长,发布的内容过多,所以从之后的版本开始,es的发布就变得更加频繁,也符合当下前端发展小步快跑的现状。
自从ES2015开始,ES2015开始按照发行年份命名,不再按照版本号命名,由于这个决定是在2015年才开始,所以很多人也把ES2015叫做ES6。
目前市面上主流的运行环境,也都纷纷跟进,也都开始逐步支持这些最新的特性,所以对于我们这些开发者而言,学习这些新特性很有必要。

ES2015

也就是ES6,新时代ES的版本标准,相比之前变化较大。目前很多人又将ES6泛指ES5.1后所有的新标准,比如我们在很多资料中看到“使用ES6的async和await”,实际上呢async函数是ES2017中制定的标准,所以以后逛论坛得注意分辨,它说的ES6是泛指还是特指。
ES2015规范链接
这里就整理ES5.1之后的,比较重要的且值得了解的新特性,这些变化可以分为四大类

  • 解决原有语法上的问题与不足(let,const等)
  • 对原有语法进行增强,使其变得更为便捷易用(解构,模版字符串等)
  • 全新的对象,全新的的方法,全新的功能(Promise等)
  • 全新的数据类型和数据结构(symbol,set,map等)

let与块级作用域

作用域:代码中某个成员能够起作用的范围。在ES2015之前,只有全局作用域和函数作用域,ES2015中又新增块级作用域。
块:指的就是代码中,用一对{ }所包裹起来的范围,例如if,for语句中的{},都会产生我们所说的块。以前块是没有独立作用域的。

if(true) {
    var aa = 'aaa';
    let bb = 'bbb';
}
console.log(aa) // => 'aaa'
console.log(bb) // => 'ReferenceError: bb is not defined'

let与var还有一个区别是,let没有变量提升

// 变量提升
console.log(a) // => undefined,说明在我们打印的时候,a就已经被声明了,只是还没赋值,这种现象就叫变量提升
var a = 123

console.log(b) // => Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 123

其实let,const是有变量提升的,var在提升后的初始化阶段会赋值undefined,let与const不会赋值,所以此时访问没有任何赋值的变量时,会抛出错误,称为暂时性死区
JS变量提升与时间死区

const

const:用来声明一个只读的常量。
它的特点就是在let的特点上多了一个只读,只读的意思是声明后不允许再被修改

const a = 123
a = 456 // => Uncaught TypeError: Assignment to constant variable.

const b = {name: 123}
b.name = 456
console.log(b) // => {name: 456}

注意,不能被修改,只是说我们用const声明过后,不允许重新指向一个新的内存地址,并不是说不能修改常量中的属性成员。所以b.name修改成功

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

数组的解构

// 普通方式获得数组成员
const arr = ['aa', 'bb', 'cc']
let a = arr[0];
let b = arr[1];
let c = arr[2];
console.log(a, b, c)

// 解构
let [a, b, c] = arr
console.log(a, b, c) // => aa bb cc
// 只获取第一个
let [a] = arr
console.log(a) // =>  aa
// 只获取第三个,把变量名称删除,要保留逗号
let [, , c] = arr
console.log(c) // =>  cc
// 只获取后两个,用...表示,这种用法只能放在最后
let [a, ...rest] = arr
console.log(rest) // => ["bb", "cc"]
// 使用默认值
let [a, b, c, more = 'dd'] = arr // 没有第四项,本来是undefined
console.log(a, b, c, more) // => aa bb cc dd

对象的解构

// 与数组的解构类似
const obj = {name: 123, age: 18}
let {name, sex = "female"} = obj
console.log(name, sex) // => 123 "female"
// 变量重命名
const obj = {name: 123, age: 18}
let {name : objName} = obj
console.log(objName) // => 123

模版字符串

const name = 'Tom';
let str1 = 'hey,' + name // 传统方式
let str2 = `hey,${name}` // 反引号方式

// ${}中不仅可以加入变量,任何规范的js语句都可以
function getAge(n) {
    return n + 1;
}
let str3 = `hey ${name}, i am ${getAge(10)} years old`

新方式还有一个好处是支持换行,以前要是想在字符串中换行需要\n

带标签的模版字符串

const gender = 1;
function tag(string, gender) {
    console.log(string)
    gender = gender === 1 ? '男' : '女';
    return string[0] + string[1] + gender;
}
let result = tag`我的性别是${gender}`
console.log(result) // => 我的性别是男

字符串的扩展方法

const message = 'hello, i love you';

message.startsWith('hello') // => true  是否以hello开头
message.endsWith('he') // => false  是否以he结束
message.includes('love') // => true  是否包含love

函数参数默认值

// 以前赋默认值
function f1 (value) {
    // value = value || 100;  // 其实这样是错的,如果传进去false ,也变成 内容是100了
    value = value === undefined ? 100 : value; // 这样最标准
    return '内容是' + value;
}
console.log(f1()) // => 内容是100

// 新方式
function f1 (value = 100) {
    return '内容是' + value;
}
console.log(f1()) // => 内容是100

剩余参数

// 传统方式
function f1 () {
    console.log(arguments) // 伪数组 => 1,2,3,4
}
f1(1,2,3,4)

// ES2015
function f1 (a, ...args) {
    console.log(a) // => 1
    console.log(args) // => [2,3,4] 正常数组
}
f1(1,2,3,4)

展开数组参数

const arr = [1,2,3]
// 传统方式
console.log(arr[0], arr[1], arr[2]) // => 1 2 3
// 如果参数不固定
console.log.apply(this, arr) // => 1 2 3
// ES2015
console.log(...arr) // => 1 2 3

箭头函数

function add(a, b) {
    return a + b
}
// 箭头函数    更简短易读
const add = (a, b) => a + b

箭头函数与this

const obj = {
    name: 'Tom',
    sayHi1: function () {
        console.log(this)
    },
    sayHi2: () => {
        console.log(this)
    }
}
obj.sayHi1() // this指向person
obj.sayHi2() // this指向window
  • 普通函数,谁调用了这个普通函数,this 指向谁。是在执行时绑定。
  • 箭头函数,this指向定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。
    比如这里箭头函数本身所在的作用域为全局作用域(对象的这个花括号没有作用域),而全局作用域的this指向window。

如果你是懒人,只需要记住这一句话就好了:不管在什么情况下,箭头函数的this跟外层function的this一致,外层function的this指向谁,箭头函数的this就指向谁,如果外层不是function则指向window。

对象字面量增强

const name = 'Tom';
const obj = {
    // name: name 以前必须要接冒号,现在变量名相同可省略
    name,

    // sayHi: function f () {  函数也是,可精简为以下方式
    //     console.log(this)
    // },
    sayHi () {  // 此方式和上面的function一样,不是箭头函数
        console.log(this)
    }
    ['ha' + 'ha']: 123 // 计算属性名,不用再像以前一样 obj['ha' + 'ha'] = 123
}

Object.assign

将多个源对象中的属性,复制到一个目标对象中
如果有相同的属性,源对象的属性会覆盖掉目标对象的属性

const source1 = {
  a: 123,
  b: 123
}
const target = {
  a: 456,
  c: 456
}
const result = Object.assign(target, source1) // 第一个入参是目标对象,后续都是源对象

console.log(target) // => {a: 123, b: 456, c: 456}
console.log(result === target) // => true 返回值就是目标对象

经常用Object.assign({}, 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: 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) // => 目标对象,属性名,值
  }
})

person.age = 100 // 可以更改person,但是不会经过set方法
personProxy.age = 100 // 可以更改person,也会经过set方法

Proxy VS Object.defineproperty

defineproperty只能监视对象的读取和写入
proxy能监视到更多的对象操作,比如delete,调用一个函数,都能监视到
proxy支持数组的监视

const list = []
const listProxy = new Proxy(list, {
  set (target, property, value) {
    console.log('set', property, value) // => [], 0, 100
    target[property] = value
    return true // 表示设置成功
  }
})

listProxy.push(100)

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

const person = {}

Object.defineProperty(person, 'name', {
  get () {
    console.log('name 被访问')
    return person._name
  },
  set (value) {
    console.log('name 被设置')
    person._name = value
  }
})
Object.defineProperty(person, 'age', {
  get () {
    console.log('age 被访问')
    return person._age
  },
  set (value) {
    console.log('age 被设置')
    person._age = value
  }
})

person.name = 'jack'

console.log(person.name)

// Proxy 方式更为合理 不需要对每个属性都绑定
const person2 = {
  name: 'zce',
  age: 20
}

const personProxy = new Proxy(person2, {
  get (target, property) {
    console.log('get', property)
    return target[property]
  },
  set (target, property, value) {
    console.log('set', property, value)
    target[property] = value
  }
})

personProxy.name = 'jack'

console.log(personProxy.name)

Reflect

Reflect最大的意义在于统一对象的操作

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

console.log('name' in obj)  // 判断是否有某个属性
console.log(delete obj['age']) // 删除某个属性
console.log(Object.keys(obj)) // 枚举属性的key
// 由上可以看出,对于对象的操作,既有操作符,又有对象的某个方法,并不统一

// Reflect最大的意义在于统一对象的操作
console.log(Reflect.has(obj, 'name')) // 判断是否有某个属性
console.log(Reflect.deleteProperty(obj, 'age')) // 删除某个属性
console.log(Reflect.ownKeys(obj)) // 枚举属性的key

class类

// 传统的创建类的方式
function Person(name) { // 构造函数
    this.name = name;
}
Person.prototype.sayHi = function () { // 原型
    console.log(`my name is ${this.name}`);
}
// class
class Person {
    constructor(name) { // 构造函数
        this.name = name;
    }
    sayHi() { // 实例方法
        console.log(`my name is ${this.name}`);
    }
    static create (name) { // 静态方法
        return new Person(name)
    }
}
const p = new Person('Eric');
const p2 = Person.create('Eric);

类的继承

class Person {
  constructor (name) {
    this.name = name
  }
  say () {
    console.log(`hi, my name is ${this.name}`)
  }
}
class Student extends Person {
  constructor (name, number) {
    super(name) // 执行父类构造函数,必须有
//子类构造器中,this是由 super()调用所产生的(即所谓「父类的this」)。在super()调用之前,你是不能在子类构造器中使用this的,如果访问之,会产生ReferenceError
    this.number = number
  }
  hello () {
    super.say() // 调用父类方法
    console.log(`my school number is ${this.number}`)
  }
}
const s = new Student('jack', '100')
s.hello()
s.say()

相关文章

  • ECMAScript新特性(一)

    ECMAScript是什么 ECMAScript(ES),也是一门脚本语言,通常把它看作为JavaScript(J...

  • ECMAScript 新特性

    ECMAScript 与 JavaScript ECMAScript 的发展过程 ECMAScript 2015 ...

  • ECMAScript新特性

    ECMAScript通常看作JavaScipt的标准化规范,实际上JavaScipt是ECMAScript的扩展语...

  • ES7及ES8新特性

    ES7新特性 (ECMAScript2016 新特性) 一、Array 1、Array.prototype.inc...

  • ECMAScript的新特性

    ECMAScript 5相对于ECMAScript 3的新特性: Getter/setters Trailing ...

  • ECMAScript Decorator

    ECMAScript 7 网上查到的资料都说 Decorator 是 ECMAScript 7 的新特性。然而查找...

  • ECMAScript 2016 新特性

    ECMAScript 2016 仅仅只有两个新特性: Array.prototype.includesExpone...

  • ECMAScript新特性(二)

    Set数据结构 Set与Array是十分相似的,不过Set不允许值重复 Map数据结构 Map与对象是十分相似的,...

  • 浅谈ECMAScript新特性

    1.新的标准规范 ECMAScript2015 是 js 的一种的新的标准规范,就是对 js 的写法上提出了新的语...

  • ECMAScript 新特性 - 笔记

    文章内容输出来源:拉勾教育Java高薪训练营 概述 ECMAScript 定义了语言层面的规范,包括变量定义、函数...

网友评论

      本文标题:ECMAScript新特性(一)

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