let 与块级作用域
作用域-某个成员能够起作用的范围, 在 ES5 之前只有两种作用域:
- 全局作用域
- 函数作用域
ES6 中新增了块级作用域。
在此之前是没有块级作用域的概念,导致在类似 if/for 语句中定义的变量,在全局作用域中也可以获取到,例如:
if(true) {
var a = 'hello'
}
console.log(a) // 'hello'
ES6 中新增了let 关键字,通过let声明的变量只能在声明它的作用域中使用。即,块级作用域内的成员,外部无法获取到。
let 不存在变量提升;
除此之外,还有一个const 关键词, 他用来定义一个衡量或者是一个常量,用来定义一个只读参数;
数组解构
const arr = [1, 2, 4]
// 单独获取成员
const [test1, test2, test3] = arr
// 获取指定位置成员
const [, , test3] = arr
// 提取当前位置开始的所有成员, 只能在最后一个成员上使用
const [test, ...test2] = arr
// 当结构成员长度小于数组长度, 从前到后提取; 结构大于数组长度, 则会返回undefined
const [test] = arr
// 设置解构默认值
const [test1, test2, test3, test4 = 4] = arr
对象解构
const obj = {name: 'heihei', age: 18}
// 获取指定属性
const { name } = obj
// 解构变量重复,使用变量重命名可以解决变量重复的问题, 同时可以设置默认值
const { name: asName, age: asAge = 10 } = obj
console.log(asName === name) // true
模版字符串
- 相较传统字符串, 模板字符串支持换行;
const str = `hello
world !`
- 模板字符串支持插值表达式的方式,嵌入变量|语句
console.log(`ooooooooh! ${str}`)
console.log(`ooooooooh! ${Math.random()}`)
- 标签函数 定义模板字符串之前定义一个标签,这个标签就是一个特殊的函数,添加这个标签,就可以调用这个函数。
作用:对字符串进行加工
const name = "tom"
const gender = '男'
// string 是模板字符串的静态内容,类型为数组
// 模版字符串中出现的剩余变量
// return 返回的字符串终值
const tagFunc = (string, name, gender) => {
return string[0] + name + string[1] + gender + string[2]
}
const result = tagFunc`hello, ${name} is a ${gender}!`
字符串的拓展方法
includes | startWidth | endWidth
参数默认值
es5 中默认值的处理方式:
// 有缺陷的设置,当enable 为false, 值会为true
const foo = (enable) => {
enable = enable || true
console.log('doSomeThing)
}
// 更科学的设置方法, 用undefined 来判断是否有入参
const foo = (enable) => {
enable = enable === undefined ? true : enable
console.log('doSomeThing)
}
es6 设置方式, 注意:默认值传参一定要从最后一个参数开始设置。
const foo = (enable = true) => {
console.log('doSomeThing)
}
剩余参数
ES6 中用 ...操作符来设置接收剩余参数。
const foo = (...args) => {
console.log(args) // [1, 2, 3, 4]
}
const foo1 = (args1, ...args) => {
console.log(args) // [2, 3, 4]
}
foo(1, 2, 3, 4)
foo1(1, 2, 3, 4)
展开数组
ES6 中用 ...操作符来展开数组。
const arr = [1, 2, 3, 4]
// 依次打印出arr 的参数
// es5中的方法
console.log.apply(console, arr)
// 展开数组, 将数组中的每个成员依次展开到新的对象中
console.log(...arr)
箭头函数
箭头函数不会改变 this 的指向;
对象字面量的增强
要添加的对象名与对象中的属性名一致时,只需要简写变量名即可:
const bar = 'foo';
const obj = {
bar
}
为对象添加属性方法时, 也可以直接用属性名跟方法体设置:
const obj = {
method() {
console.log('doSomeThing')
}
}
为对象添加动态属性名时,还可以使用 计算属性名, 语法: [activeKey]: value:
const obj = {
[Math.random()]: value
}
Proxy 对象代理
Proxy 可以监视 对象的源数据的任何修改, 使用 Proxy:
const person = {
name: 'hello',
age: 20
}
const personProxy = new Proxy(person, {
get (target, property) {
// doSomething
return property in target ? target[property] : 'default'
},
set (target, property, value) {
// doSomething
target[property] = value
}
})
// 使用
personProxy.gender = 'male'
Proxy 对比 defineProperty
- defineProperty 只能监视对象的读写, Proxy 能够见识到更多对象操作,例如 delete 操作或者方法的调用;
delete 方法监控:
const person = {
name: 'hello',
age: 20
}
const personProxy = new Proxy(person, {
deleteProperty (target, property) {
// doSomething
delete target[property]
}
})
//
delete personProxy.age
其它属性: 见图
- Proxy 更好的支持数组对象的监视;
监视数组:
const list = []
const listProxy = new Proxy(list, {
set (target, property, value) {
console.log('set', property, value)
target[property] = value
return true // 表示设置成功
}
})
listProxy.push(100) // 'set' 0 100
- Proxy 是以非侵入的方式监管了对象的读写
defineProperty 需要单独定义被监视的属性,例如:
const person = {
name: 'hello',
age: 20
}
// 定义需要被监视的属性
Object.defineProperty(person, 'name', {
get() {},
set() {}
})
而 Proxy 可以监管整个需要被监视的对象。
Reflect
Reflect 是一个静态类, 无法被实例化。内部封装了一系列对对象的底层操作。Reflect 成员方法就是 Proxy 处理对象的默认实现
const person = {
name: 'hello',
age: 20
}
const personProxy = new Proxy(person, {
// 自定义get,用来处理自定义逻辑
get(target, property) {
// doSomeThing
return Reflect.get(target, property)
}
})
Reflect 的意义在于,统一提供了一套用于操作对象的 API。例如:
const person = {
name: 'hello',
age: 20
}
console.log( 'name' in person)
console.log(delete person.name)
console.log(Object.keys(obj))
// ...
// Reflect 统一用法
console.log(Reflect.has(person, 'name'))
console.log(Reflect.deleteProperty(person, 'name'))
console.log(Reflect.ownKeys(obj))
// ...
Reflect 目前提供了十三种底层的操作方法。见文档:
Set 数据结构
Set 与传统数组类似,是一个数据集合。
const s = new Set()
// add 方法返回实例本身,所以可以链式调用,set可以对元素去重;
s.add(1).add(2).add(3).add(1)
// 遍历
// forEach
// for of
// 获取数据长度
s.has()
// 删除
s.delete()
// 清空
s.clear()
常见用法, 去重:
const arr = [1,2,3,4,3,2,1]
console.log(Array.from(new Set(arr)))
console.log([...new Set(arr)])
Map 数据结构
Map 是一个键值对集合,用来映射两个任意数据类型对对应关系:
Map 对应的方法:
- set
- get
- has
- delete
- clear
- 遍历:forEach
Symbol
Symbol 用来表示一个独一无二的数据类型,通过 Symbol 函数创建的值永远不相等:
Symbol() === Symbol() // false
Symbol 允许传入描述文本, 对 Symbol 值进行区分:
Symbol('1')
Symbol('2')
Symbol 最主要作用就是为对象添加独一无二的属性名:
const name = Symbol()
const person = {
[name]: 'test',
say() {
console.log(this.[name])
}
}
// 无法直接通过Symbol 获取对象属性
person[Symbol()] // undefined
// 可以通过调用方法来实现读取
person.say() // test
Symbol 提供了一个静态方法for, for 方法默认传入 String 变量,如果传入的是非字符串,则会隐式转换为字符串类型:
Symbol('foo') == Symbol('foo') // false
Symbol.for('foo') === Symbol.for('foo') // true
Symbol.for(true) === Symbol.for('true') // true
Symbol 属性的用途 1: 为对象添加 toString 属性:
const obj = {}
console.log(obj.toString()) // [object, Object]
//
const objSymbol = {
[Symbol.toStringTag]: 'XObject'
}
console.log(objSymbol.toString()) // [object, XObject]
Symbol 作为对象的属性名,传统方法无法拿到此属性名, 例如:
const obj = {
[Symbol()]: 'new Symbol',
foo: 'test'
}
for(let key in obj) {
console.log(key) // foo
}
console.log(Object.keys(obj)) // ['foo']
console.log(JSON.stringify(obj)) // ["foo": "test]
以上的特性使得 Symbol 适合做对象的私有属性。如果要拿到对象的 Symbol 属性,可以使用Object.getOwnPropertySymbols:
console.log(Object.getOwnPropertySymbols(obj))