ES6相关

124 阅读9分钟

一、let const var区别

var

在ES5中,顶层对象的属性和全局变量是等价的,用var生命的变量既是全局变量,也是顶层变量,在浏览器中顶层对象就是window对象,在node环境中就是global对象

 var a = 10
 console.log(window.a) //10

使用var声明的变量存在变量提升的情况

console.log(a) //undefined
var a = 20

在编译阶段,编译器会将其转变成以下执行

var a
console.log(a)
a = 20

使用var可以对一个变量多次声明,后面的声明会覆盖前面的变量声明

var a = 20
var a = 30
console.log(a) // 30

在函数中使用var声明的变量是局部的,如果在函数中不使用var是声明变量,该变量就是全局的

var a=20
function change(){
   var a=30
}
change()
console.log(a)  //20
var a=20
function change(){
  a = 30
}
change()
console.log(a)  //30

let

let 是ES6新增的指令,用来声明变量的,所声明的变量只有在let命令所在的代码块内有效

 {
  let a=20
 }
 console.log(a) // a is not defined

let 声明的变量不存在变量提升

 console.log(a)
 let a = 20 //Cannot access 'a' before initialization

只要块级作用域内存在let命令,这个区域就不再受外部的影响

var a = 20
if(true){
  a  = 30 //Cannot access 'a' before initialization
  let a
}

使用let声明变量前,该变量都不可用,这就是经常说的''暂时性死区''

let不允许在相同的作用域内重复声明

 let a = 20
 let a = 30
 //Identifier 'a' has already been declared

因此,我们不能在函数内部重复声明变量

function func(arg){
   let arg
}
func()

const

const 声明一个只读的常量,一旦声明了,就不能改变其值

 const a = 1
  a = 3
  //Assignment to constant variable.

这意味着const 一旦声明了变量,就必须初始化,不能留到以后赋值

 const a
 // Missing initializer in const declaration

如果用var或者let声明的变量,再用const声明会报错

     var a = 20
     let b = 30
     const a = 30
     const b  = 80
     // Identifier 'a' has already been declared

const实际上不是声明的变量的值不能改变,而是变量指向的内存地址所保存的那个那个数据不能改变,对于简单类型的数据,值就保存在变量指向的那个内存地址,因此等于常量 对于复杂类型的数据,变量指向的是内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的,并不能确保改变量的结构是固定的

const foo = {}
 foo.a = '成功'
 
 foo = {} // foo指向另一个内存地址就会报错
 //Assignment to constant variable.

三者区别

var const let 三者可以可以围绕下面几点展开

  • 变量提升
  • 暂时性死区
  • 块级作用域
  • 重复声明
  • 修改声明的变量
  • 使用

变量提升

var存在变量提升,const和let是不存在变量提升的

// var
console.log(a)  // undefined
var a = 10

// let 
console.log(b)  // Cannot access 'b' before initialization
let b = 10

// const
console.log(c)  // Cannot access 'c' before initialization
const c = 10

暂时性死区

var 不存在暂时性死区;let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用改变量

// var
console.log(a)  // undefined
var a = 10

// let
console.log(b)  // Cannot access 'b' before initialization
let b = 10

// const
console.log(c)  // Cannot access 'c' before initialization
const c = 10

块级作用域

var不存在块级作用域,let和const存在块级作用域

// var
{
    var a = 20
}
console.log(a)  // 20

// let
{
    let b = 20
}
console.log(b)  // Uncaught ReferenceError: b is not defined

// const
{
    const c = 20
}
console.log(c)  // Uncaught ReferenceError: c is not defined

重复声明

var允许重复声明,let和const在同一个作用域中不允许重复声明

// var
var a = 10
var a = 20 // 20

// let
let b = 10
let b = 20 // Identifier 'b' has already been declared

// const
const c = 10
const c = 20 // Identifier 'c' has already been declared

修改声明的变量

var和let可以修改 const只能声明常量,值不能改变

// var
var a = 10
a = 20
console.log(a)  // 20

//let
let b = 10
b = 20
console.log(b)  // 20

// const
const c = 10
c = 20
console.log(c) // Uncaught TypeError: Assignment to constant variable

总结:我们在使用的时候,能用const尽量使用const,其他情况下尽量使用let,避免使用var

二、Set和Map数据结构

set是一种叫做集合的数据结构,Map是一种叫做字典的数据结构

那什么叫集合,什么叫字典呢?

  • 集合

是由一堆无序的、相关联的,且不重复的内存结构【数学中称为元素】组成的组合

  • 字典 是一些元素的集合,每个元素有一个称作key的域,不同元素的key各不相同

相同点:集合和字典都可以存储不重复的值

不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储元素

Set

Set是es6新增的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的值,我们一般称为集合

Set本身是一个构造函数,用来生成Set数据结构

const s = new Set()

增删改查

Set的实例关于增删改查的方法

  • add()
  • delete()
  • has()
  • clear()

add()

添加某个值,返回Set结构本身,当添加实例中已经存在的元素,set不会进行添加处理

s.add(1).add(2).add(2) // 2只被添加了一次

delete()

删除某个值,返回一个布尔值,表示删除成功

s.delete(1)

has()

返回一个布尔值,判断该值是否为Set的成员

s.has(2)

clear()

清除所有成员,没有返回值

s.clear()

遍历

Set实例遍历的方法有如下:

  • keys(): 返回键名的遍历器
  • values: 返回键值的遍历器
  • entries: 返回键值对的遍历器
  • forEach(): 使用回调函数遍历每个成员

Set的遍历顺序就是插入顺序keys方法、values方法、entries方法返回的都是遍历器对象

 let set = new Set(['red','green','blue'])
 for(let item of set.keys()){
   console.log(item)
 }
 // red
 // green
 // blue
  for(let item of set.values()){
   console.log(item)
 }
 // red
 // green
 // blue
  for(let item of set.entries()){
   console.log(item)
 }
 // ['red', 'red']
 // ['green', 'green']
 // ['blue', 'blue']

forEach()用于对每个成员执行某种操作,没有返回值,键值、键名都相等,同样的forEach方法有第二个参数,用于绑定处理函数的this

let set = new Set([1,4,9])
set.forEach((value, key) => console.log(key + ':' + value))
// 1:1
// 4:4
// 9:9

扩展运算符和set结构相结合实现数组或字符串去重

// 数组
let arr = [3,5,2,2,5,5]
let unique = [...new Set(arr)]
// [3,5,2]

// 字符串
let str = '352255'
let unique = [...new Set(str)].join('')
// '352'

实现并集、交集、和差集

let a = new Set([1,2,3])
let b = new Set([4,3,2])

// 并集
let union = new Set([...a, ...b])
// Set {1,2,3,4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)))
// Set {2,3,}

//差集
let intersect = new Set([...a].filter(x => !b.has(x)))
// Set {1}

Map

Map类型是键值对的有序数列表,而键和值都可以是任意类型

Map本身是一个构造函数,用来生成Map的数据结构

增删改查

Map结构的实例针对增删改查有以下属性和操作方法

  • size属性
  • set()
  • get()
  • has()
  • delete()
  • clear()

size

size 属性返回Map结构的成员总数

const map = new Map()
map.set('foo', true)
map.set('bar', false)
map.size //2

set()

设置键名key对应的键值为value,然后返回整个Map结构,如果key已经有值,则键值会被更新,否则就生成该键,同时返回的是当前Map对象,可采用链式写法

 const m = new Map()
 m.set('string', '6')
 m.set(262, 'standard')
 m.set(undefined, 'nah')
 m.set(1, 'a').set(2, 'b').set(3, 'c')

get()

get方法读取key对应的键值,如果找不到key,返回undefined

 const m = new Map()
 const hello = function(){console.log('hello')}
 m.set(hello, 'Hello ES6')
 m.get(hello) // Hello ES6

has()

has方法返回一个布尔值,表示某个键是否在当前Map对象之中

const m = new Map()
 m.set('string', '6')
 m.set(262, 'standard')
 m.set(undefined, 'nah')
 m.set(1, 'a').set(2, 'b').set(3, 'c')
 
 m.has('string') // true
 m.has('ss') // false
 m.has('undefined') // false

delete()

delete方法删除某个键,返回true,如果删除失败,返回false

const m = new Map()
m.set(undefined, 'nah')
m.has(undefined) // true

m.delete(undefined)
m.has(undefined) // false

clear()

clear方法清除所有成员,没有返回值

let map = new Map()
map.set('foo', true)
map.set('bar', false)

map.size() // 2
map.clear()
map.size() // 0

遍历

Map结构原生提供三个遍历器生成函数和一个遍历方法

  • keys(): 返回键名的遍历器
  • values: 返回键值的遍历器
  • entries(): 返回所有成员的遍历器
  • forEach(): 遍历Map的所有成员

遍历的顺序就是插入的顺序

const map = new Map([['F', 'no'], ['T', 'yes']])

for(let key of map.keys()){
 console.log(key)
}
// "F"
// "T"

for(let key of map.values()){
 console.log(key)
}
// "no"
// "yes"

for(let key of map.entries()){
 console.log(key)
}
// "F" "no"
// "T" "yes"

或者
for(let [key, value] of map.entries()){
 console.log(key, value)
}
// "F" "no"
// "T" "yes"

map.forEach(function(value, key, map){
 console.log(value)
 console.log(key)
})

WeakSet 和 WeakMap

WeakSet

创建WeakSet实例

const ws = new WeakSet()

WeakSet可以接受一个具有Iterable接口的对象作为参数

const a = [[1,2], [3,4]]
const ws = new WeakSet(a)
// WeakSet {[1,2], [3,4]}

在API中WeakSet与Set有两个区别

  • 没有遍历操作的API
  • 没有size属性

WeakSet成员只能是引用类型,而不能是其他类型的值

let ws  = new WeakSet()

// 成员不是引用类型
let ws  = new WeakSet([2,3])
console.log(ws) // 报错Invalid value used in weak set

// 成员为引用类型
let obj1 = {name: 1}
let obj2 = {name: 1 }
let ws = new WeakSet([obj1, obj2])
consle.log(ws) // WeakSet {{…}, {…}}

WeakSet里面的引用只要在外部消失,它在WeakSet里面的引用就会自动消失

WeakMap

WeakMap结构和Map结构类似,也是用于生成键值对的集合

在API中WeakMap与Map有两个区别

  • 没有遍历操作的API
  • 没有clear清空的方法
// WeakMap可以使用set方法添加成员
const wm1 = new WeakMap()
const key = {foo: 1}
wm1.set(key, 2)
wm1.get(key) // 2

// WeakMap 也可以接受一个数组
// 作为构造函数的参数
const k1 = [1,2,3]
const k1 = [4,5,6]
const wm1 = new WeakMap([[k1, 1], [k2, 2]])
wm1.get(k2) //2

WeakMap 只接受对象作为键名(null除外),不接受其他类型的值作为键名

const map = new WeakMap()
    map.set(1,2) // Invalid value used as weak map key
    map.set(Symbol(), 2)//  Invalid value used as weak map key

    map.set(null, 2)//  Invalid value used as weak map key

WeakMap的键名所指向的对象,一旦不再需要,里面的键名对象和所对应的键值对会自动消失,不用手动删除引用

举个场景的例子:

在网页的DOM元素上添加数据,就可以使用WeakMap结构,当该DOM元素被清除,其所对应的WeakMap记录就会自动被移除

const wm = new WeakMap()
const element = document.getElementById('example')
wm.set(element, 'some information')
wm.get(element) // 'some information'

注意:WeakMap弱引用的只是键名,而不是键值。键值依然是正常的引用,下面的代码中,键值obj会在WeakMap产生新的引用,当你修改obj不会影响到内部

const wm = new WeakMap()
    let key = {}
    let obj = {foo: 1}
    wm.set(key, obj)
    obj = null;

    console.log(wm.get(key)) // Object {foo: 1}

Proxy