Es6 | 青训营笔记

109 阅读8分钟

Es6 | 青训营笔记

这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天

基础语法

let

ES6新增了 let 命令用来声明变量,它的用法类似于 var,区别在于它只在 let 声明的代码块内有效,并且 let 在同一作用域内不能重复声明,我们建议在平时的开发中使用 let 替代 var

function varTest() {
  var x = 1

  if (x === 1) {
    var x = 2 // 两个x是同一个变量
    console.log(x)
    // 输出:2
  }

  console.log(x)
  // 输出:2
}
function letTest() {
  let x = 1

  if (x === 1) {
    let x = 2 // 两个x是不同的变量
    console.log(x)
    // 输出:2
  }

  console.log(x)
  // 输出:1
}

另外,在程序和方法的最顶端,var 声明的变量会给全局对象增加属性,但是 let 不会,例如:

var x = 'hello'
let y = 'hello'

console.log(this.x) // "hello"
console.log(this.y) // undefined

上面的 this 取决于执行环境,如在浏览器中执行,指向的是 window 对象,如在Node.js中执行,指向的则是 global 对象。

另外需要注意的是,let 在同一块作用域中不允许重复声明,例如:

if (flag) {
  let test = 1
  let test = 2  // SyntaxError
}

使用大括号 {} 可以用来定义一个块级作用域。

const

const 常量也是块级范围的,这一点与 let 相似,但 const 声明的值是无法被改变的,也不能被重新声明,全局常量也不会变为 window 对象的属性,所以,常量必须在声明的时候就指定它的值。

const number = 123

number = 456  // Uncaught TypeError: Assignment to constant variable

一般我们建议在声明常量的时候全部采用大写字母,例如:

const MY_NUMBER = 123

需要注意的是,如果 const 用来定义数组或对象,那么对于他们子项或者属性的修改是允许的,但是不能够对自身重新赋值,例如:

const MY_ARRAY = [1, 2, 3]
MY_ARRAY.push(4)  // 允许
MY_ARRAY = [] // 不允许

const MY_OBJECT = { key: 123 }
MY_OBJECT.key = 456 // 允许
MY_OBJECT = { key: 456 }  // 不允许

如果想要冻结对象属性的修改,可以使用 Object.freeze() 来时对象不可变,例如

const MY_OBJECT = { key: 123 }
Object.freeze(MY_OBJECT)
MY_OBJECT.key = 456 // 常规模式下该语句不会起作用,但是也不会报错,严格模式下会报错

解构赋值

解构赋值语法是一种JavaScript表达式,通过解构赋值,可以将属性、值从对象、数组中取出,赋值给其他变量,以前如果我们想要给变量赋值,只能直接指定变量的值,例如:

let a = 1
let b = 2
let c = 3

如果我们想要从数组中取出值,需要这么来写:

let array = [1, 2, 3]
let a = array[0]
let b = array[1]
let c = array[2]

如果我们想要从对象中取出属性和值,需要这么来写:

let object = { a: 1, b: 2, c: 3 }
let a = object.a
let b = object.b
let c = object.c

通过上面的例子我们可以看到,书写起来非常的繁琐,不够简洁,现在通过ES6的解构赋值语法,我们就可以轻松实现。

解构数组

基本语法如下,左侧的变量名和右侧数组中的元素一一对应

let [a, b, c] = [1, 2, 3]
console.log(a)  // 1
console.log(b)  // 2
console.log(c)  // 3

可以给左边数组中的变量设置默认值,例如

let [a = 1, b = 2] = [5]
console.log(a)  // 5
console.log(b)  // 2

可以通过解构直接交换两个变量,在没有解构的时候,我们需要一个中间临时变量

let a = 1
let b = 2

// 不使用解构来交换
let c = a
a = b
b = c

// 使用解构来交换
[a, b] = [b, a]
console.log(a)  // 2
console.log(b)  // 1

解构数组一个常用的场景是解析函数返回值,例如

function foo() {
  return [1, 2]
}

let [a, b] = foo()
console.log(a)  // 1
console.log(b)  // 2

可以忽略掉某些值。例如

let [a,,,b] = [1, 2, 3, 4]
console.log(a)  // 1
console.log(b)  // 4

剩余模式,将剩余部分的数组赋值给一个变量

let [a, ...b] = [1, 2, 3]
console.log(a)  // 1
console.log(b)  // [2, 3]

解构对象

基本语法如下:

let object = { a: 1, b: 2, c: 3 }
let { a, b } = object

console.log(a)  // 1
console.log(b)  // 2

可以从一个对象中提取变量并赋值给和属性名不同的新变量名

let object = { a: 1, b: 2, c: 3 }
let { a: aa, b: bb } = object

console.log(aa) // 1
console.log(bb) // 2

解构对象也可以指定默认值,例如:

let object = { a: 5 }
let { a = 1, b = 2 } = object

console.log(a)  // 5
console.log(b)  // 2

解构对象也支持剩余模式,例如

let { a, ...b } = { a: 1, b: 2, c: 3 }
console.log(a)  // 1
console.log(b)  // { b: 2, c: 3 }

解构对象的一个典型应用场景是从函数参数对象中提取数据,例如下面的代码,函数接收的参数是一个对象,如果不使用解构,需要专门去读取参数对象中的属性值

function test(user) {
  console.log(user.id, user.name)
}

let user = {
  id: 1,
  name: 'test'
}

test(user)

如果我们使用解构对象,接可以直接将属性取出来

function test({id, name}) {
  console.log(id, name)
}

let user = {
  id: 1,
  name: 'test'
}

test(user)

模板字符串

模板字符串是增强版的字符串,用反引号 (`)来标识,它可当作普通字符串来使用,也可以用来定义多行文本,或者通过 ${} 在字符串中嵌入变量或表达式。

let a = `template string` // 普通字符串

let name = 'Frank'
let b = `Hello ${name}!`
console.log(b)  // Hello Frank!

模板字符串的另外一大作用就是多行文本定义变得更加方便,对比例子:

let name = 'Frank'

let a = `<div>
  <p>Hello ${name}!</p>
</div>`

let b = '<div>\n  <p>Hello ' + name + '!</p>\n</div>'

console.log(a === b)  // true

字符串操作

字符串处理是我们在编程的时候非常常用的功能,这里介绍一些基本的字符串处理方法,有的是ES6之前就存在的。

substring

该方法返回一个字符串在开始索引到结束索引之间的一个子集,或者从开始索引到字符串末尾的一个子集。

语法

参数

  • indexStart:需要截取的第一个字符的索引,该索引位置的字符作为返回的字符串的首字母。
  • indexEnd:可选参数,一个0到字符串长度之间的整数,以该数字为索引的字符串不包括在截取的字符串内。

返回值

包括给定字符串的指定部分的新字符串

下面查看一些示例:

let str = '0123456789'
console.log(str.substring(0, 3))  // '012'
console.log(str.substring(3, 6))  // '345'
console.log(str.substring(0, -3)) // 相当于 str.substring(0, 0),输出为空字符串

slice

slice 的作用和 substring 非常类似,不同的是,slice 的参数可以为负数,表示倒数第几个字符

let str = '0123456789'
console.log(str.slice(0, 3))  // '012'
console.log(str.slice(3, 6))  // '345'
console.log(str.slice(0, -3)) // '0123456',表示从第0各字符提取到倒数第三个字符
console.log(str.slice(-3, -1))  // '78'

includes

方法用于判断一个字符串是否包含在另一个字符串中,根据情况返回 truefalse

参数

  • searchString:要搜索的字符串
  • position:开始搜索的索引位置,默认为0,可选

示例:

let str = '0123456789'
console.log(str.includes('123'))  // true
console.log(str.includes('123', 4)) // false

startsWith

startsWith 方法用来判断当前字符串是否以另外一个给定的子字符串开头,并根据判断结果返回 truefalse

参数

  • searchString:要搜索的字符串
  • position:开始搜索的索引位置,默认为0,可选

示例:

let str = '0123456789'
console.log(str.startsWith('0123')) // true
console.log(str.startsWith('1234'))  // false
console.log(str.startsWith('1234', 1))  // true

endsWith

endsWithstartsWith 作用类似,用来判断当前字符串是否以另外一个给定的子字符串结尾,endsWith 的第二个参数是可选的 str 长度,示例如下:

let str = '0123456789'
console.log(str.endsWith('789'))  // true
console.log(str.endsWith('456', 7)) // true,相当于判断 '0123456'.endsWith('456)

repeat

该方法返回一个新字符串,表示将原字符串重复 n 次,示例如下:

'abc'.repeat(2) // 'abcabc'

padStart、padEnd

这两个方法提供了字符串补全长度的功能,如果某个字符串不够指定的长度,会在头部或者尾部补全,padStart 用于头部补全,padEnd 用于尾部补全,这个在格式化字符串的时候非常有用,示例如下:

'5'.padStart(5, '0')  // '00005'
'123'.padEnd(5) // '123  ',默认使用空格补全
'12345'.padStart(4) // '12345',超出长度,不会变化

trim、trimStart、trimEnd

这三个方法的作用类似,trim 用来消除字符串首尾的空格,trimStart 用来消除字符串头部的空格,trimEnd 用来消除字符串尾部的空格,他们返回的都是新字符串,不会改变原值,示例如下:

let str = '  abc  '
str.trim()  // 'abc'
str.trimStart() // 'abc  '
str.trimEnd() // '  abc'

replaceAll

以前js的字符串替换方法 replace() 只会替换第一个匹配,如果想要替换所有的匹配,需要写正则表达式,例如:

'aabbcc'.replace('b', '_')  // 'aa_bcc'
'aabbcc'.replace(/b/g, '_') // 'aa__cc'

写正则增加了复杂度,现在新增的 replaceAll() 方法,可以一次性替换所有匹配

'aabbcc'.replaceAll('b', '_') // 'aa__cc'

split

该方法使用指定的分割字符将一个字符串分割成子字符串数组,以一个指定的分割字符串来决定每个拆分的位置

console.log('Hello JavaScript'.split(' '))  // [ 'Hello', 'JavaScript' ]
console.log('Hello'.split(''))  // [ 'H', 'e', 'l', 'l', 'o' ]

数学计算

Math 是一个内置对象,它拥有一些数学常数和数学函数方法,这里我们介绍几个常用的API。

PI

console.log(Math.PI)  // 3.141592653589793

圆周率常数,可以直接引用

floor

返回小于一个数的最大整数

console.log(Math.floor(3.1))  // 3
console.log(Math.floor(3))  // 3

ceil

返回大于一个数的最小整数

console.log(Math.ceil(3)) // 3
console.log(Math.ceil(3.1)) // 4

round

返回四舍五入后的整数,需要注意,js的round有点不同,它并不总是舍入到远离0的方向,特别是负数的小数部分恰好等于0.5的情况下。

Math.round(3.49)  // 3
Math.round(3.5) // 4
Math.round(-3.5)  // -3
Math.round(-3.51) // -4

trunc

返回一个数的整数部分,直接去除小数点之后的部分,传入的参数会被隐式转换为数字类型

Math.trunc(3.1) // 3
Math.trunc(0.5) // 0
Math.trunc('-1.2')  // -1

random

该函数返回一个浮点数,伪随机数的范围从0到1,也就是说大于等于0,小于1,我们可以以此为种子,扩展到自己想要生成的随机数的范围,比如下面的例子,可以让我们生成指定范围的随机整数。

function random(min, max) {
  return Math.floor(Math.random() * (max - min) + min)
}

数组操作

developer.mozilla.org/zh-CN/docs/…

数组是js中非常重要的数据结构,需要熟练掌握基本的数组操作方法。

遍历数组

JS里面有很多种遍历数组的方法,这里我们介绍几种常用的,有一些方法只是对数组进行读取,还有一些会直接改变原数组,这个需要注意区分。

let arr = [1, 2, 3, 4, 5]

for (let item of arr) {
  console.log(item) // item为正在处理的当前元素
}

arr.forEach((item, index) => {
  console.log(item, index)  // item为正在处理的当前元素,index为索引值
})

需要注意的是,forEachfor..of 不同,除了抛出异常之外,没有办法中止或者跳出 forEach 循环。

filter

该方法创建一个新数组,将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。

let words = ['spray', 'limit', 'elite', 'exuberant', 'destruction']
let result = words.filter(word => word.length > 6)
console.log(result) // [ 'exuberant', 'destruction' ]

map

返回一个由回调函数的返回值组成的新数组。

let arr = [1, 2, 3]
let tpl = arr.map(item => `<span>${item}</span>`)
console.log(tpl)  // [ '<span>1</span>', '<span>2</span>', '<span>3</span>' ]

reduce

从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次的回调函数,并返回最后一次回调函数的返回值。

let arr = [1, 2, 3]
let sum = arr.reduce((previous, current, index) => {
  console.log(previous, current, index)
  return previous + current
})
console.log(sum)  // 6

上面的代码中的回调函数会执行两次,让我们看一下 reduce 是如何运行的

callbackpreviouscurrentindexreturn
第1次1213
第2次3326

previous 是上一次回调函数的返回值,current 是当前要处理的数值,我们可以利用 reduce 来改写上一个 map 中的例子,将返回的html数组拼接起来合并成一个字符串

let arr = [1, 2, 3]
let tpl = arr.reduce((prev, curr) => prev + `<span>${curr}</span>`, '')
console.log(tpl)  // '<span>1</span><span>2</span><span>3</span>'

注意,上面的代码我们给 reduce 添加了第二个可选参数,是一个空字符串,作为第一次迭代的初始数值

find

找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined

let arr = [1, 2, 3, 4, 5]
let found = arr.find(item => item > 3)
console.log(found)  // 4

findIndex

找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1

let arr = [1, 2, 3, 4, 5]
let index = arr.findIndex(item => item > 3)
console.log(index)  // 3

includes

判断当前数组是否包含某指定的值,如果是返回 true,否则返回 false

let arr = [1, 2, 3, 4, 5]
console.log(arr.includes(3))  // true
console.log(arr.includes('2'))  // false

indexOf

返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1

let arr = [1, 2, 3, 4, 5]
let index = arr.indexOf(4)
console.log(index)  // 3

join

连接所有数组元素组成一个字符串。

let arr = [1, 2, 3, 4, 5]
console.log(arr.join('')) // '12345'
console.log(arr.join('-'))  // '1-2-3-4-5'

concat

用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

let arr1 = [1, 2, 3]
let arr2 = [4, 5]
let arr3 = arr1.concat(arr2)
console.log(arr3) // [ 1, 2, 3, 4, 5 ]

slice

抽取当前数组中的一段元素组合成一个新数组,这是一个原数组的浅拷贝,原始数组不会被改变。

let arr = [1, 2, 3, 4, 5]
console.log(arr.slice(2)) // [ 3, 4, 5 ]
console.log(arr.slice(1, 3))  // [ 2, 3 ]

splice

在任意的位置给数组添加或删除任意个元素。这个很容易和 slice 搞混,此方法会改变原数组。

插入元素:

let arr = [1, 2, 3, 4, 5]
arr.splice(2, 0, 6)
console.log(arr)  // [ 1, 2, 6, 3, 4, 5 ]

删除元素:

let arr = [1, 2, 3, 4, 5]
let item = arr.splice(1, 2)
console.log(item) // [ 2, 3 ]
console.log(arr)  // [ 1, 4, 5 ]

删除元素的同时插入两个元素:

let arr = [1, 2, 3, 4, 5]
let item = arr.splice(1, 2, 6, 7)
console.log(item) // [ 2, 3 ]
console.log(arr)  // [ 1, 6, 7, 4, 5 ]

reverse

颠倒数组中元素的排列顺序,即原先的第一个变为最后一个,原先的最后一个变为第一个,该方法会改变原数组。

let arr = [1, 2, 3, 4, 5]
arr.reverse()
console.log(arr)  // [ 5, 4, 3, 2, 1 ],原数组被改变

push

在数组的末尾增加一个或多个元素,并返回数组的新长度。

let arr = [1, 2, 3, 4, 5]
console.log(arr.push(6))  // 6
console.log(arr.push(7, 8)) // 8
console.log(arr)  // [ 1, 2, 3, 4, 5, 6, 7, 8 ]

pop

删除数组的最后一个元素,并返回这个元素。

let arr = [1, 2, 3, 4, 5]
let item = arr.pop()
console.log(item) // 5
console.log(arr)  // [ 1, 2, 3, 4 ]

unshift

在数组的开头增加一个或多个元素,并返回数组的新长度,与 push 对应

let arr = [1, 2, 3, 4, 5]
console.log(arr.unshift(6))  // 6
console.log(arr.unshift(7, 8)) // 8
console.log(arr)  // [ 7, 8, 6, 1, 2, 3, 4, 5 ]

shift

删除数组的第一个元素,并返回这个元素,与 pop 对应

let arr = [1, 2, 3, 4, 5]
console.log(arr.shift())  // 1
console.log(arr)  // [ 2, 3, 4, 5 ]

pushpopunshiftshift 这几个方法组合起来,可以用来实现栈、队列的功能

sort

方法用用于对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列时构建的

let arr = ['b', 'd', 'a', 'c']
arr.sort()
console.log(arr)

我们可以通过传入比较函数,来自定义排序逻辑,比较函数会每次传入两个要比较的值 ab,如果函数返回小于0,那么 a 会排列到 b 的前面,称为升序排列,如果大于0,则会排到后面,称为降序排列,如果等于0,则相对位置不变(并非标准行为)

let arr = [3, 5, 1, 4, 2]
arr.sort((a, b) => {
  if (a < b) return -1
  if (a > b) return 1
  return 0
})
console.log(arr)  // [ 1, 2, 3, 4, 5 ],升序

arr.sort((a, b) => {
  if (a < b) return 1
  if (a > b) return -1
  return 0
})
console.log(arr)  // [ 5, 4, 3, 2, 1 ],降序