ES6

252 阅读6分钟

let const

作用域问题

  1. 内层变量可能会覆盖外层变量
var tmp = new Date()
function f() {
    console.log(tmp) // undefined
    if(false) {
        var tmp = 'iu' // 变量声明提升
    }
}
f()
  1. 用于计数的循环变量i泄露为全局变量
var str = 'hello'
for(var i = 0;i < str.length;i ++) {
    console.log(str[i])
}
console.log(i) // 5

a.经典错误

var a = []
for(var i = 0;i < 3;i ++) {
    a[i] = function() {
        console.log(i)
    }
}
a[0]() // 3

传统闭包解决

var a = []
for(var i = 0;i < 3;i ++) {
    (function(j) { // 利用了函数作用域的特点
        a[j] = function() {
            console.log(j)
        }
    }(i))
}

let解决

let a = []
for(let i = 0;i < 3;i ++) {
    a[i] = function() {
        console.log(i)
    }
}

b.经典错误

<button>1</button>
<button>2</button>
<button>3</button>
<script>
	var btns = document.getElementsByTagName('button')
	for(var i = 0;i < btns.length;i ++) {
        // btns[i].onclick = function() {
        //     console.log(i)
        // }
        
        btns[i].addEventListener('click', function() {
            console.log(i) // 不管点击哪个按钮,始终打印3
        })
    }
</script>

传统闭包解决

<button>1</button>
<button>2</button>
<button>3</button>
<script>
    var btns = document.getElementsByTagName('button')
    for(var i = 0;i < btns.length;i ++) {
        // (function(j) {
        //     btns[j].onclick = function() {
        //         console.log(j)
        //     }
        // })(i)
        
        (function(j) {
            btns[j].addEventListener('click', function() {
                console.log(j)
            })
        }(i))
    }
</script>

let解决

<button>1</button>
<button>2</button>
<button>3</button>
<script>
	let btns = document.getElementsByTagName('button')
	for(let i = 0;i < btns.length;i ++) {
        // btns[i].onclick = function() {
        //     console.log(i + 1)
        // }
        
        btns[i].addEventListener('click', function() {
            console.log(i + 1)
        })
    }
</script>

const

  1. 声明一个只读(仅是内存地址不能修改)的常量
  2. 声明和赋值同时进行
  3. 同一作用域中不可重复声明同一变量
const obj = {
    name: 'sxw',
    age: 18
}
obj = {} // 报错,修改了内存地址
obj.name = 'iu' // 成功

块级作用域的特点

不存在变量声明提升

TDZ暂时性死区

如果区块中存在letconst命令,此区块对这些命令所声明的变量从一开始就会形成封闭的块级作用域,凡在声明之前使用变量,立即报错。

if(true) {
    // TDZ开始
    tmp = 'a' // 报错
    console.log(tmp) // 报错
    
    // TDZ结束,在这行代码之前,都属于暂时性死区
    let tmp
    console.log(tmp) // undefined
    tmp = 1
    console.log(tmp) // 1
}
typeof x // 报错
let x

不允许重复声明同一变量

声明变量的方式总结:

  • ES5提供了varfunction
  • ES6提供了letconstimportclass

解构赋值

数组的解构赋值

只要数据结构具备Iterator接口,都可以采用数组形式的解构赋值。

let [a,b] = [1,2]
let [,b] = [1,2]
let x = 1;
let y = 2;
[x,y] = [y,x] // 互换两个变量的值

默认值形式

当且仅当数组成员严格等于undefined时,生效。

let [x=1] = [undefined]
x // 1

对象的解构赋值

function fn(value) {
    console.log(value) // {name: 'iu',age: 18}
    console.log(name,age) // iu 18 
}

let obj = {
    name: 'iu',
    age: 18
}

fn({name,age}=obj) 
// 对于对象的属性没有次序之分
let {bar,foo} = {foo: 'a',bar: 'b'}
foo // 'a'
bar // 'b'

默认值形式

var {x=3} = {}
x // 3 
var {x: y=3} = {x: 5}
y // 5

字符串的解构赋值

字符串被转成了类似数组的对象,可用于解构赋值。

const [a,b,c] = 'sxw'
a // 's'
b // 'x'
c // 'w'
let {length: len} = 'hello'
len // 5

数值和布尔值的解构赋值

  • 当等号右边不是对象或数组时,会先转成对象形式
  • undefinednull无法转为对象,对其进行解构赋值会报错

函数参数问题

function log(x, y) {
  y = y || 'World'; 
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', '') // Hello World,出问题了,我们明明给y赋值为空字符串了,但是空字符串转为布尔值是false,所以此赋值并没有起到作用
// 优化
function log(x, y) {
  // 先判断参数y是否被赋值,如果没有,再走默认值 
  if(typeof y === 'undefined') {
      y = 'world'
  }
  console.log(x, y);
}

函数参数默认值

function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
function foo({x, y = 5}) {
  console.log(x, y);
}

foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // 模式不匹配
function foo({x, y = 5} = {}) {
  console.log(x, y);
}

foo() // undefined 5

函数参数设定默认值的两种方式:

// 1.函数参数的默认值是空对象,但是设置了对象解构赋值的默认值
function m1({x = 0, y = 0} = {}) {
  return [x, y];
}

// 2.函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值 
function m2({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]

// x和y都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]

// x有值,y无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]

// x和y都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]

m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
function f(x = 1, y) {
  return [x, y];
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]
function fn({start,end}) {
	console.log(start,end)
}
fn({start: 1,end: 2})

// {start,end} = {start: 1,end: 2}
// 函数参数解构赋值给默认值

function fn({start=1,end=2}) {
    console.log(start,end)
}
fn({}) // 实参严格等于undefined,走默认值

// {start=1,end=2} = {}
function fn({start=1,end=2}) {
    console.log(start,end)
}
fn() // 实参什么都不传 => {start=1,end=2} = undefined,导致了左右模式不匹配,报错 

// {start=1,end=2} = undefined
// 参数的默认赋值

function fn({start=1,end=2} = {}) {
    console.log(start,end)
}
fn() // 实参什么都不传,为undefined,即 undefined = {} ,走空对象,空对象的话又走解构赋值的默认值

// {start=1,end=2} = {},因为实参什么都不传,默认为undefined,走默认值 {},空对象再走解构赋值默认值

剩余运算符

把散列的元素变成一个集合(数组或对象),在函数或解构中使用。

function add() {
    console.log(arguments) // 类数组,由元素1和2组成
    let arr = Array.prototype.slice.call(arguments) // 将类数组转为数组
    console.log(arr) // 数组[1,2]
}
add(1,2)
function add(...arr) {
    // ...arr的作用是将散列的元素拼接成一个数组
    console.log(arr) // 数组[1,2]
}
add(1,2)
let [a, ...b] = [1,2,3]
a // 1
b // [2,3]

扩展运算符

function fn(a,b,c) {
    console.log(a,b,c) // 1,2,3
}
fn(...[1,2,3]) // 将数组变成散元素
  • 从函数中返回多个值
// 返回数组
function example() {
    return [1,2,3]
}
let [a,b,c] = example()
// 返回对象
function example() {
    return {
        foo: 1,
        bar: 2
    }
}
let {foo,bar} = example()
  • 函数参数的定义
// 参数是一组有次序的值
function f([x,y,z]) {
    ...
}
f([1,2,3])
  • 提取json数据
let jsonData = {
    id: 3,
    status: 'ok',
    data: [1,2,3]
}
let {id,status,data: arr} = jsonData
  • 函数参数的默认值
var foo = config.foo || 'default foo' // 避免这种形式的写法
  • 遍历map结构
const map = new Map()
map.set('name','sxw')
map.set('age','18')

for(let [key,value] of map) {
    console.log(key + 'is' + value)
}
// name is sxw
// age is 18

// 只获取键名
for(let [key] of map) {
    // ...
}

// 只获取键值
for(let [,value] of map) {
    // ...
}
  • 输入模块的指定方法
// 加载模块时,需要指定输入哪些方法 
const {SourceMapConsumer, SourceNode} = require('source=map')