ES6 新增功能

(一)字面量增强

let name = 'test'
let age = 18

let obj = {
  // 1. property shorthand(属性简写)
  name,
  age,

  // 2. method shorthand (方法简写)
  bar() {
    // ...
  }
  // 原来写法
  foo: function() {
    // ...
  },
  // 3. computed property name(计算属性名:对象属性使用变量或常量)
  [name + '123']: 'value',
}

(二)对象、数组的解构,解构的默认值

// # 数组解构
let names = ['tom', 'jack', 'batman']
// 1. 数组解构
let [name1, name2, name3] = names
// 打印出:'tom', 'jack', 'batman'
console.log(name1, name2, name3)

// 2. 解构后面元素,前后省略
let [, name2, ] = names
// 打印出 'jack'
console.log(name2)

// 3. 后面的元素放入新数组
let [name1, ...newNames] = names
// 打印出 'tom', ['jack', 'batman']
console.log(name1, newNames)

// 4. 解构默认值
let [name1, name2, name3, name4 = 'test'] = names
// 打印出 'test'
console.log(name4)
// # 对象解构
let obj = {
  name: 'tom',
  age: 18,
  weight: 50,
}
// 1. 对象解构:对象无顺序,必须使用相同的key名称
let {name} = obj
// 打印出 'tom'
console.log(name)

// 2. 解构别名
let {name: newName} = obj
// 打印出 'tom'
console.log(newName)

// 3. 默认值
let {test = '测试'} = obj
// obj中没有test,所有打印出 '测试'
console.log(test)

// 4. function 中的使用和默认值方式1
function bar({name, age} = {name: 'tom', age: 18}) {
  // 打印出 'tom', 18
  console.log(name, age)
}
// 默认值方式2
function bar({name = 'tom', age = 18} = {}) {
  // 打印出 'tom', 18
  console.log(name, age)
}
bar(obj)

// 5. 

(三)let、const 基本使用

const

const只能赋值一次,不能多次赋值,当值为Object对象时,可更改属性。

const name = 'tom'
// 报错:TypeError: Assignment to constant variable
name = 'jack'

const obj = {
  name: 'tom',
  age: 18
}
// 可正常修改,不报错
obj.name = 'jack'
// 报错,不可直接替换整个对象
obj = {}

let

let 不可多次赋值,var可以。

let a = 1
// 报错:不可多次赋值
let a = 2

var b = 1
// 不会报错
var b = 2

var 作用域提升,let、const会报错

var 可在创建之前被访问,letconst会报错。

// 报错:fname is not defined
console.log(fname)
let fname = 'tom'

// 报错:age is not defined
console.log(age)
const age = 18

// 不报错,但打印:undefined
console.log(weight)
var weight = 50

块级作用域

{
  var title = 'tom'
  let keyword = 'kw value'
  const time = '2022'
  function demo() {}
}
// 正常打印 'tom'
console.log(title)
// 报错:keyword is not defined
console.log(keyword)
// 报错:time is not defined
console.log(time)
// 为兼容旧版,也可读取
demo()

if(true) {
  // if 括号内也是块作用域
}
switch(true) {
  case true:
    // 这里也是块作用域
    break
}
for (var i = 0; i < 10; i++) {
  // for 内也是块作用域,for外部可访问 i
}
for (let i = 0; i < 10; i++) {
  // for 内也是块作用域,for外部无法访问 i
}

块作用域的应用场景

如下代码,由于 var 声明的 i 属于全局作用域,当 for 执行完毕后,i 始终等于 2,所以每次点击输出都是 2,使用let声明即可解决。

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
const btns = document.getElementsByTagName('button')
for(var i = 0; i < btns.length; i++) {
  btns[i].onClick = function() {
    // 不管点击哪个按钮始终输出 '点击了按钮2'
    console.log('点击了按钮' + i)
  }
}
</script>

var、let、const 的选择

在开发中,我们到底要选择使用哪一种方式来定义我们的变量呢?

  1. 对于var的使用:
    • 作用域提升、window全局对象、没有块作用域都是历史遗留问题;
    • 其实是 JavaScript 在设计之初的一种语言缺陷;
    • 尽管面试中可能问及相关问题,但实际工作中可以使用最新规范编写,就是不再使用var来定义变量。
  2. 对于let、const:
    • 目前开发推荐使用 let const
    • 优先推荐使用 const ,这样可以保证数据的安全不会被随意修改;
    • 当我们明确知道变量后续会需要重新赋值时,再使用 let
    • 这种再很多其它语言里面也都是一种约定俗成的规范,我们尽量也遵守这种规范。

(四)模板字符串

const name1 = 'tom'
const age = 18
const weight = 50

// es6之前字符串拼接写法复杂
console.log("my name is " + name + ", age is " + age + ", weight is " + weight)
// es6的模板字符串,{}内部还可以写表达式、使用函数
console.log(`my name is ${name}, age is ${age}, weight is ${weight + 1}`)

标签模板字符串(了解)

比较少用,但在 react 框架中有个叫 styled-components 库使用这种方式编写。

function foo(a, b, c) {
  console.log(a, b, c)
}
const title = 'tom'
const age = 18
// 打印出:['Hello', 'Wo', 'rld'], 'tom', 18
foo`Hello${title}Wo${age}rld`

(五)箭头函数

在 es6 中新增了箭头函数:

  1. 语法更简洁清晰,快捷;
  2. 没有自己的 thisthis 不能被修改,call 等都不能修改,只能间接修改被继承的 this
  3. this 在定义时就定了,继承外一层的普通函数;
  4. 没有 prototype
  5. this 继承自外层函数,如果没有外层函数则指向 window
  6. 不能构造函数,不能new.target,不能new,没有constructor;
  7. 箭头函数不支持重复命名参数,普通函数可以重复命名参数;
    const func = () => {
    // this 指向全局 window
    console.log(this)
    }

(六)展开语法

展开语法(Spread syntax)

  • 可以在函数调用/数组构造时,将数组表达式或者string在语法层面展开;
  • 还可以在构造字面量对象时,将对象表达式按key-value的方式展开;

    展开语法的场景

  • 在函数调用时使用;
  • 在数组构造时使用;
  • 在构建对象字面量时,也可以使用展开运算符,这个是在ES2018( ES9)中添加的新特性;

    注意:展开合并时,是一个浅拷贝

    const titles = ['t1', 't2', 't3']
    const title = 'title'
    const info = {title: 'title1', age: 18}
    // 1. 函数调用
    function foo(x, y, z) {
    console.log(x, y, z)
    }
    // 相当于:foo(titles[0], titles[1], titles[2])
    foo(...titles)
    // 相当于:foo('t', 'i', 't', 'l', 'e')
    foo(...title)
    // 2. 构造数组时
    const newTitles = [...titles, ...title]
    console.log(newTitles)
    // 3. ES2018(ES9) 构建对象字面量时
    const obj = {...info, address: '厦门市'}
    // {title: 'title1', age: 18, address: '厦门市'}
    console.log(obj)

(七)数值的表示

const numl = 100 // 十进制
// b 表示binary
const num2 = 0b100 // 二进制
// o 表示octonary
const num3 = 0o100 // 八进制
// x 表示 hex(hexadecimal)
const num4 = 0x100 // 十六讲制
// 100, 4, 64, 256
console. log (numl, num2, num3, num4)

// 大的数值的连接符(ES2021 ES12),方便阅读
const num = 10_000_000_000_000
// 10000000000000
console.log(num)

(八)Symbol的基本使用

Symbol 是什么呢? Symbol 是 ES6 中新增的一个基本数据类型,翻译为符号。

那么为什么需要Symbol呢?

  • 在ES6之前,对象的属性名都是字符串形式,那么很容易造成属性名的冲突;
  • 比如原来有一个对象,我们希望在其中添加一个新的属性和值,但是我们在不确定它原来内部有什么内容的情况下,很容易造成冲突,从而覆盖掉它内部的某个属性;
  • 比如 applycallbind 实现时 ,我们给其中添加一个属性 ,那么如果它内部原来已经有了fn属性了呢?
  • 比如开发中我们使用混入,那么混入中出现了同名的属性,必然有一个会被覆盖掉;

    Symbol就是为了解决上面的问题,用来生成一个独一无二的值

  • Symbol 值是通过 Symbol 函数来生成的,生成后可以作为属性名;
  • 也就是在ES6中,对象的属性名可以使用字符串,也可以使用 Symbol 值;

    Symbol即使多次创建值,它们也是不同的:Symbol函数执行后每次创建出来的值都是独一无二的

    我们也可以在创建Symbol值的时候传入一个描述description:这个是ES2019( ES10)新增的特性

    // 1. ES6 Symbol 声明
    const s1 = Symbol()
    const s2 = Symbol()
    // 打印:false
    console. log(s1 === s2)
    // ES2019 (ES10),Symbol 还有一个描述(description)
    const s3 = Symbol ("aaa")
    // 打印:'aaa'
    console. log(s3.description)
    // 2.Symbol 值作为key
    // 2.1 新增属性方式1
    const obj = {  [s1]: 'abc',  [s2]: 'cba' }
    // 2.2 新增属性方式2
    obj[s3] = 'test'
    // 2.3 新增属性方式3,使用 Object.defineProperty
    const s4 = Symbol()
    Object.defineProperty (obj, s4, {
    enumerable: true,
    configurable: true,
    writable: true,
    value: 'test',
    })
    // 读取
    console.log(obj[s1], obj[s2], obj[s3])
    // 注意:不能使用.语法读取
    console.log(obj.s1)
    //
    // 遍历:先获取key,再通过key读取
    const sKeys = Object.getOwnPropertySymbols(obj)
    for (const sKey of sKeys) {
    console.log(obj[sKey])
    }
    // 创建相等的Symbol,Symbol.for(key)/Symbol.keyFor(symbol)
    const sa = Symbol.for("aaa")
    const sb = Symbol.for("aaa")
    // 打印:true
    console.log(sa === sb)
    // 获取key
    const key = Symbol.keyFor(sa)
    // 打印:'aaa'
    console.log(key)
    const sc = Symbol.for(key)
    // 打印:true
    console.log(sa === sc)

(九)数据结构 Set

在ES6之前,我们存储数据的结构主要有两种:数组、对象。

  • 在ES6中新增了另外两种数据结构:Set、Map,以及它们的另外形式WeakSet、 WeakMap。
    Set是一个新增的数据结构,可以用来保存数据,类似于数组,但是和数组的区别是元素不能重复。
  • 创建Set我们需要通过Set构造函数(暂时没有字面量创建的方式)
    我们可以发现Set中存放的元素是不会重复的,那么Set有一个非常常用的功能就是给数组去重。

set 方法

  1. add:增加元素;
  2. delete:删除元素;
  3. has:是否包含元素;
  4. clear:清除元素;
  5. forEach:遍历;
  6. 使用 for of 遍历。

属性 size:元素的个数

// 创建set结构
const set = new Set()
set.add(10)
set.add(20)
set.add(40)
set.add(333)
set.add(10)
// 打印:{10, 20, 40, 333}
console.log(set)
set.add({})
set.add({})
// 打印:{10, 20, 40, 333, {}, {}},添加两个不同对象
console.log(set)

const obj = {}
// 下面两次新增obj只会加入一次,obj 引用地址相同
set.add(obj)
set.add(obj)

// 数组去重
const arr = [10, 20, 40, 333, 10]
const arrSet = new Set(arr)
// 打印:{10, 20, 40, 333}
console.log(arrSet)
// set 转数组1
const newArr = Array.from(arrSet)
// set 转数组2
const newArr2 = [...arrSet]
// 打印 4
console.log(newArr.size)

(十)数据结构 WeakSet

WeakSet 只能保存对象,且它是弱引用。
强引用:当变量(对象)赋值给一个对象属性,此时为强引用,当变量变成 null 时,对象不会被回收。因为还有对象属性指向这个对象的内存地址。
弱引用:把变量(对象)放入 WeakSet 后,把变量对象变成 null,对象就会被回收。WeakSet 的指向是弱引用,不影响内存回收。
方法:

  1. add:添加元素
  2. delete:删除元素
  3. has:是否包含元素
  4. 不能遍历:只能添加,无法遍历
let obj ={
  name: 'why',
}
const set = new Set()
const weakset = new WeakSet()
// 建立的是强引用
set.add(obj)
// 建立的是弱引用
weakset.add(obj)

(十一)数据结构 Map

Map 用于存储映射关系,和对象类似。
对象和 Map 的区别:

  • 对象只能用字符串(ES6新增了 Symbol )作为 key(属性名);
  • Map 可以用任何类型作为 key
    方法:

    1. set:设置映射关系;
    2. get:获取元素;
    3. has:是否包含元素;
    4. delete:删除元素;
    5. clear:清除元素;
    6. foreach遍历;
    7. for of 遍历。
      属性:size:元素的个数。
const map = new Map()
map.set(false, 'false的值')
map.get(false)

(十二)数据结构 WeakMap

Map 类型相似的另外一个数据结构称之为 WeakMap ,也是以键值对的形式存在的。
那么和 Map 有什么区别呢?

  • WeakMapkey 只能使用对象,不接受其他的类型作为 key
  • WeakMapkey 对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象。
    方法:

    1. set:设置映射关系;
    2. get:获取元素;
    3. has:是否包含元素;
    4. delete:删除元素;
    5. clear:清除元素;

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注