作用域
- 全局作用域
- 函數(shù)作用域
- 塊級(jí)作用域(es6)
let
- let 聲明的變量只在所處的塊級(jí)有效
- let沒有變量聲明提升
需要注意的一點(diǎn):
for (let i = 0;i < 3; i++) {
let i = "xxx"
console.log(i);
}
其實(shí)在此處是兩層的塊級(jí)作用域
const
- const 與 let一樣作用域都是塊級(jí)
- const只用來聲明常量
- const聲明的時(shí)候必須復(fù)制邻吭,否則會(huì)報(bào)錯(cuò)
const obj = {}
obj.name = "sam"
這種情況不會(huì)報(bào)錯(cuò),根本原因是obj
指向的地址是沒變的
數(shù)組的解構(gòu)
const [first, second, third] = [1,2,3]
const [first, second, third, fourth = 4] = [1,2,3]
const [first, ...rest] = [1,2,3,4]
對(duì)象的解構(gòu)
const obj = {name: "xxx", age: 18}
const {name,age} = obj
模板字符串
const param = "world"
const str = `hello, //可換行
${param}` //嵌入變量以及script語句
console.log(str)
還一個(gè)比較特殊的使用方式是帶標(biāo)簽
const name = "sam"
function tagFn (string, name) {
console.log(string, name) // 可以在這部分做對(duì)字符串的修飾
return string[0] + name + string[1]
}
const result = tagFn`hi, ${name}.`
console.log(result)
字符串?dāng)U展方法
includes()
startsWith()
endsWith()
參數(shù)默認(rèn)值
function foo (a = 1) {
console.log(a )
}
foo()
剩余參數(shù)
function foo (...rest) { //只能用在形參的最后一位 并且只能使用一次
console.log(rest)
}
foo(1,2,3,4)
展開數(shù)組
const arr = [1,2,3]
console.log.apply(console, arr)//兩種方式等同
console.log(...arr)
箭頭函數(shù)
const inc = (n,m) => {
return n + m
}
console.log(inc(1,2))
箭頭函數(shù)不會(huì)改變this的指向
對(duì)象字面量的增強(qiáng)
const bar = "2"
const obj = {
foo: 1,
bar,
fn() {
console.log("fnfnfnfnfnfn")
}
}
obj[1+1] = 3
obj.fn()
對(duì)象的擴(kuò)展方法
Object.assign()
const obj1 = {
a:1,
b:2
}
const obj2 = {
a:3,
c:4
}
const obj = Object.assign(obj1, obj2)
console.log(obj)
console.log(obj === obj1)
//{ a: 3, b: 2, c: 4 }
//true
Object.is()
Object.is(+0 === -0)
Proxy
Proxy
在目標(biāo)對(duì)象的外層搭建了一層攔截,外界對(duì)目標(biāo)對(duì)象的某些操作必須通過這層攔截
const person = {
name: "sam",
age: 18
}
const personProxy = new Proxy(person, {
get(target, property) {
return property in target ? target[property] : "default"
},
set(target, property, value) {
if (property === "age") {
if (!Number.isInteger(value)){
throw new TypeError(`${value} is not an int`)
}
}
target[property] = value
}
})
personProxy.age = 12
personProxy.sex = "man"
我們發(fā)現(xiàn)proxy
與Object.defineProperty()
的作用相似糙箍,那么兩者有什么區(qū)別呢筏勒?
-
Object.defineProperty()
只能監(jiān)控到對(duì)象屬性的讀寫 -
proxy
可監(jiān)控更多操作例如屬性刪除衅金,對(duì)象方法調(diào)用
handle | 觸發(fā)條件 |
---|---|
get | 讀取某個(gè)屬性 |
set | 寫入某個(gè)屬性 |
has | in 操作符 |
deleteProperty | delete 操作符 |
getProperty | Object.getPropertypeOf() |
setProperty | Object.setPrototypeOf() |
isExtensible | Object.isExtensible() |
preventExtensions | Object.preventExtensions() |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() |
defineProperty | Object.defineProperty() |
ownKeys | Object.keys() 耙考、Object.getOwnPropertyNames()耘斩、Object.getOwnPropertySymbols() |
apply | 調(diào)用一個(gè)函數(shù) |
construct | 用 new 調(diào)用一個(gè)函數(shù) |
對(duì)數(shù)組監(jiān)視
本質(zhì): 重寫數(shù)組的操作方法合陵。
用proxy
監(jiān)視數(shù)組
const list = []
const listProxy = new Proxy(list, {
set(target, property, value) {
console.log("push", property, value)
target[property] = value
return true
}
})
listProxy.push(100)
非侵入便可監(jiān)視
Reflect
Reflect
是一個(gè)靜態(tài)類枢赔,使用方式類似Math
Reflect
成員方法就是Proxy
處理對(duì)象的默認(rèn)實(shí)現(xiàn)。
Reflect
提供了一套用于操作對(duì)象的API拥知,詳情見以上表格
const obj = {
name: "zicoo",
age: 18
}
// console.log("name" in obj)
console.log(Reflect.has(obj, "name"))
// console.log(delete obj[age])
console.log(Reflect.deleteProperty(obj, "age"))
// console.log(Object.keys(obj))
console.log(Reflect.ownKeys(obj))
Promise
提供了一種更優(yōu)的異步編程方案
具體可見之前的文章《Promise核心功能從原理到實(shí)現(xiàn)》
class 類
class Person {
constructor (name) {
this.name = name
}
say() {
console.log(`my name is ${this.name}`)
}
static create(name) {
return new Person(name)
}
}
const zicoo = new Person("zicoo")
zicoo.say()
const sam = Person.create("sam")
sam.say()
class Student extends Person {
constructor(name, number) {
super(name)
this.number = number
}
hello() {
super.say()
console.log("student say hello")
}
}
const tom = new Student("tom", 10)
tom.hello()
Set 數(shù)據(jù)結(jié)構(gòu)
- 可看做集合
-
Set
中數(shù)據(jù)不重復(fù)
const s = new Set();
s.add(1).add(2).add(1)
console.log(s)
//s.forEach()
//for (let i of s)
// s.zie
// s.has(1)
// s.delete(2)
// s.clear()
Set
有個(gè)常見的場景是用來數(shù)組去重
const arr = [1,2,3,1,3,2]
const result = Array.from(new Set(arr))
// const result = [...new Set(arr)]
console.log(result)
Map數(shù)據(jù)結(jié)構(gòu)
與Object
類似都是存的鍵值對(duì)踏拜,但是Object的key
只能為String
類型
const obj = {}
obj[true] = 123
obj[123] = 123
obj[{a: 1}] = 123
console.log(Reflect.ownKeys(obj))
//[ '123', 'true', '[object Object]' ]
console.log(obj[{}])
// 123
我們發(fā)現(xiàn)真正對(duì)象的key
是字符串, 并且我們發(fā)現(xiàn)這么使用存在的其他問題。
那么Map
的出現(xiàn)解決了這些問題
const m = new Map()
const tom = {name: "tom"}
m.set(tom, 90)
// m.get()
// m.delete()
// m.clear()
// m.forEach()
Symbol 數(shù)據(jù)類型
獨(dú)一無二的值
最主要的作用是給對(duì)象增加一個(gè)獨(dú)一無二的屬性
// const obj = {
// [Symbol("name")]: 123
// }
// console.log(obj)
// 場景 私有成員
const name = Symbol()
const person = {
[name]: 'zicoo',
say() {
console.log(this[name])
}
}
console.log(person.name)
person.say()
以上就是最簡單的使用低剔,那么Symbol
的其實(shí)使用場景呢
console.log(Symbol("foo") === Symbol("foo"))
console.log(Symbol.for("foo") === Symbol.for("foo"))
//false
//true
console.log(Symbol.for(true) === Symbol.for("true"))
// false 證明會(huì)將true 轉(zhuǎn)成字符串
//Symbol 本身有好多方法的標(biāo)識(shí)符
// eg:
//Symbol.hasInstance
//Symbol.iterator
const obj = {}
console.log(obj.toString())
//[object Object]
const objX = {
[Symbol.toStringTag] : "XObject"
}
console.log(objX.toString())
//[object XObject]
// 通過 for 無法不按理 objx中的 [Symbol.toStringTag]速梗, Object.keys 也不可以
console.log(Object.getOwnPropertySymbols(objX))
// [ Symbol(Symbol.toStringTag) ]
for ... of 循環(huán)
可遍歷所有數(shù)據(jù)類型
基本用法
// 遍歷數(shù)組
const arr = [100,200,300,400]
for (const item of arr) {
if (item > 100) {
break
}
console.log(item)
}
// 遍歷map
const m = new Map()
m.set("foo", 123)
m.set("bar", 234)
for(const [key,value] of m) {
console.log(key, value)
}
當(dāng)我們遍歷數(shù)組時(shí)會(huì)出現(xiàn)什么情況
const obj = {name: "sam"}
for (const item of obj) {
console.log(item)
}
//obj is not iterable
問題來了報(bào)錯(cuò)了肮塞! 其實(shí) 數(shù)據(jù)結(jié)構(gòu)的 Iterable
接口是使用 for ... of
的前提
迭代器
我們發(fā)現(xiàn)無論
Array``Set``Map
的_proto_
原型上都有Symbol(Symbol.iterator)
這個(gè)方法。我們發(fā)現(xiàn)這個(gè)iterator
的作用相當(dāng)于一個(gè)指針姻锁,每調(diào)用一次next()
,都會(huì)將value
指向下一個(gè)值枕赵,并且done
來區(qū)分是不是遍歷完成
const set = new Set(["foo","bar","baz"])
const iterator = set[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
//{ value: 'foo', done: false }
//{ value: 'bar', done: false }
//{ value: 'baz', done: false }
//{ value: undefined, done: true }
那我們簡單實(shí)現(xiàn)一下可迭代接口Iterable
// 實(shí)現(xiàn)可迭代接口(Iterable)
const obj = {
store: ["foo", "bar", "baz"],
[Symbol.iterator]: function() { // Iterable
let index = 0
const self = this
return {
next: function() { // Iterator
return { //IterationResult
value: self.store[index],
done: index++ >= self.store.length
}
}
}
}
}
for (const item of obj) {
console.log(item)
}
生成器
function * foo() {
console.log("111")
yield 100
console.log("222")
yield 200
console.log("333")
yield 300
}
const result = foo()
console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())
// 使用generator 改造 iterator 方法
const obj = {
store: ["foo", "bar", "baz"],
[Symbol.iterator]: function * () { // Iterable
for(const item of obj.store) {
yield item
}
}
}
for (const item of obj) {
console.log(item)
}
它還有一個(gè)很重要的使用場景就是 異步編程, 可見之前的文章《異步編程(Promise位隶、Generator拷窜、Async與Await)》
ES Modules
后續(xù)有詳細(xì)介紹。略
ES2016
- Array.propotype.includes
- 指數(shù)運(yùn)算 eg: Math.pow(2, 10) === 2 ** 10
ES2017
- Object.vaules: 返回所有值的數(shù)組
- Object.entries: 返回所有鍵值對(duì)數(shù)組
- Object.getOwnPropertyDescriptors: 獲取所有屬性 eg: get() set()
- String.prototype.padStart/String.prototype.padEnd: 填充字符串前面或后面
- Async/Await