let const var
- var 存在變量提升自赔,變量可以在聲明前被使用妈嘹,值為
undefined
;let
``var不可以在聲明前被使用绍妨,否則會報(bào)錯(cuò)润脸,ES6 明確規(guī)定,如果區(qū)塊中存在
let和
const`命令他去,這個(gè)區(qū)塊對這些命令聲明的變量毙驯,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量灾测,就會報(bào)錯(cuò)爆价。
function test() {
console.log(a); //undefind
var a = 1;
console.log(b); //Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 2;
}
這就引發(fā)了一個(gè)叫暫時(shí)性死區(qū)的現(xiàn)象,所謂暫時(shí)性死區(qū)媳搪,就是只要在同一作用域內(nèi)铭段,包括函數(shù)和塊級全局,let
和const
就會綁定在這個(gè)區(qū)域秦爆,在這之前使用的話序愚,都會報(bào)錯(cuò),直到聲明過后等限。舉個(gè)例子:
var num = 2
function test() {
console.log(num); //Uncaught ReferenceError: Cannot access 'num' before initialization
const num = 1;
}
- var不存在塊級作用域爸吮,
let
和const
存在塊級作用域。ES6前精刷,Javascript只區(qū)分全局作用域(整個(gè)script標(biāo)簽內(nèi)部或者一個(gè)獨(dú)立的js文件)和函數(shù)作用域(局部作用域)拗胜,ES5不存在塊級作用域(凡是用{}包起來的都算),所以對于var
來說:
function test() {
for(var i=0; i<10; i++) {}
console.log(i); //10
}
對于let
來說:
function test() {
for(let i=0; i<10; i++) {}
console.log(i); // Uncaught ReferenceError: i is not defined
}
- 初始值
var
let
可以不設(shè)置初始值怒允,const
必須設(shè)置初始值
const a; // Uncaught SyntaxError: Missing initializer in const declaration
- 重復(fù)聲明
var
可以重復(fù)聲明埂软,let
const
不允許 - 數(shù)據(jù)修改
var
和let
允許修改數(shù)據(jù)或者重新賦值,const
定義的如果是基本數(shù)據(jù)類型,是不允許修改的勘畔,如果是引用數(shù)據(jù)類型所灸,那么保存在棧中的堆地址是不可以修改的,真正的數(shù)據(jù)可以修改炫七。
解構(gòu)
- 數(shù)組解構(gòu)
const [a, b, c, d] = [1,2,3,4]
console.log(a,b,c,d); // 1 2 3 4
- 對象解構(gòu)
const {a, b, c, d} ={a: 1, b: 2, c: 3, d: 4}
console.log(a,b,c,d); // 1 2 3 4
模版字符串
var names = ["Tom", "Jane", "Tim"]
var name = `${names} are coming.`
箭頭函數(shù)
- 3分鐘理解箭頭函數(shù)的this
- 沒有
arguments
function test(){
console.log(arguments)
}
test(1,2,3); // Arguments(3) [1, 2, 3, callee: ?, Symbol(Symbol.iterator): ?]
const test2 = () =>{
console.log(arguments)
}
test2(); // Uncaught ReferenceError: arguments is not definedat test2
- 不能通過
new
關(guān)鍵字調(diào)用, 根據(jù)new的原理來看爬立,箭頭函數(shù)不具備調(diào)用的條件:
test.prototype
// {constructor: ?}
test2.prototype
// undefined
這里簡單描述一下new Test('abc')的調(diào)用過程:
- 創(chuàng)建一個(gè)空對象
obj
- 把
obj
的__proto__
指向Test
的原型對象prototype
,此時(shí)便建立了obj
對象的原型鏈:obj->Animal.prototype->Object.prototype->null
- 在
obj
對象的執(zhí)行環(huán)境調(diào)用Test
函數(shù)并傳遞參數(shù)'abc'
万哪。 相當(dāng)于var result = obj.Test('abc')
. - 考察第3步返回的返回值侠驯,如果無返回值或者返回一個(gè)非對象值,則將
obj
返回作為新對象奕巍;否則會將返回值作為新對象返回吟策。
形參默認(rèn)值
有默認(rèn)值的形參位置要放到最后
function add(a,b,c=2) {
console.log(a + b + c);
}
add(1,2) //5
與解構(gòu)賦值結(jié)合使用 結(jié)構(gòu)賦值的先后不影響
function connect({name, age=18, sex}) {
console.log(name);
console.log(age);
console.log(sex);
}
connect({
name:'小寶',
sex: 'man'
})
Symbol
Symbol是ES6新引入的一種原始數(shù)據(jù)類型,表示獨(dú)一無二的值的止,常用于命名不能沖突的場景檩坚,比如對象的key,或者定義常量替換無意義的字符串诅福,創(chuàng)建時(shí)直接Symbol()
匾委,不能使用new
。
作為key:
const MY_KEY = Symbol();
let obj = {};
obj[MY_KEY] = 123;
console.log(obj[MY_KEY]); // 123
let obj2 = {
[MY_KEY]: 123
};
console.log(obj2[MY_KEY]); // 123
let obj3 = {
[MY_KEY]() {
return 'bar';
}
};
console.log(obj3[MY_KEY]()); // bar
作為常量
const levels = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn'),
}
function log(type, message) {
switch (type) {
case levels.DEBUG:
console.log(message)
break
case levels.INFO:
console.log(message)
break
case levels.WARN:
console.log(message)
break
default:
console.log('default')
break
}
}
Symbol.for(key)
Symbol.for(key)
通過key
(一個(gè)字符串氓润,作為 symbol
注冊表中與某 symbol
關(guān)聯(lián)的鍵赂乐,同時(shí)也會作為該 symbol
的描述)來判斷其唯一性,key
必須是字符串或者可以被轉(zhuǎn)換成字符串(因此Symbol
類型不能作為key
旺芽,不過很難想象它有toString方法卻不能被動轉(zhuǎn)換成字符串)沪猴,不是字符串的,調(diào)用toString()
轉(zhuǎn)換為字符串采章,如果無法轉(zhuǎn)換成字符串的运嗜,會報(bào)錯(cuò)。undefined
和null
沒有toString()
,但是不會報(bào)錯(cuò)悯舟,當(dāng)做字符串'undefined'
和'null'
處理担租,默認(rèn)值即為undefined
。返回由給定的 key
找到的 symbol
抵怎,否則就是返回新創(chuàng)建的symbol
奋救。
判斷Symbol.for(key)
的返回值是否相等其實(shí)就是在判斷兩個(gè)key
的toString()
返回結(jié)果是否相等.
Symbol.for([1,2,3]) === Symbol.for('1,2,3'); // true
Symbol.for() === Symbol.for(undefined); // true
Symbol.for() === Symbol.for('undefined'); // true
Symbol.for(null) === Symbol.for('null'); // true
Symbol.for({}) === Symbol.for({a: 123}); // true
Symbol.for([]) === Symbol.for(""); // true
Symbol.for(Infinity) === Symbol.for("Infinity"); // true
Symbol.for(NaN) === Symbol.for("NaN"); // true
Symbol.iterator
Symbol.iterator
是一個(gè)內(nèi)置值如果對象有Symbol.iterator
, 即obj[Symbol.iterator] !== undefined
那么這個(gè)對象就可以被for...of
遍歷
for (let i of [1,2,3] ) {
console.log(i); // 1 2 3
}
for (let i of {num1: 1, num2: 2} ) {
console.log(i); // Uncaught TypeError: {(intermediate value)(intermediate value)} is not iterable
}
因此如何讓一個(gè)對象可以被for...of:
- 給對象添加一個(gè)
key
為Symbol.iterator
的屬性方法 - 這個(gè)方法必須返回一個(gè)迭代器對象,它的結(jié)構(gòu)必須如下:
{
next: function() {
return {
value: any, //每次迭代的結(jié)果
done: boolean //迭代結(jié)束標(biāo)識
}
}
}
舉個(gè)例子:
obj={names: ["Tom", "Jane", "Tim"];
obj[Symbol.iterator] = function () {
let i = 0
const _this = this
return {
next: () => {
return {
value: _this.names[i++],
done: i === _this.names.length
}
},
}
}
for(let i of obj) {
console.log(i); // Tom Jane Tim
}
獲得屬性名稱
const MY_KEY = Symbol()
let obj2 = {
[MY_KEY]: 123,
enum: 2,
nonEnum: 3,
}
console.log(Object.getOwnPropertyNames(obj2)) // ['enum', 'nonEnum']
console.log(Object.getOwnPropertySymbols(obj2)) // [Symbol()]
console.log(Reflect.ownKeys(obj2)) // ['enum', 'nonEnum', Symbol()]
Set
類似數(shù)組反惕,但成員值都是唯一的尝艘,可以方便的去重,求并集姿染、交集背亥、差集。
Map
Promise
有三種狀態(tài)pending
fulfilled
rejected
創(chuàng)建實(shí)例
new Promise(function(resolve, reject) {...})
創(chuàng)建Promise
實(shí)例時(shí),需要往構(gòu)造方法中傳入一個(gè)函數(shù)作為參數(shù)狡汉,這個(gè)函數(shù)可以通過調(diào)用傳入的resolve
和reject
方法來改變promise
實(shí)例的狀態(tài)娄徊,調(diào)用resolve
由pending
轉(zhuǎn)變?yōu)?code>fulfilled,調(diào)用reject
由pending
轉(zhuǎn)變?yōu)?code>rejected
實(shí)例方法
-
then
實(shí)例狀態(tài)發(fā)生變化時(shí)盾戴,觸發(fā)的回調(diào)函數(shù)寄锐,第一個(gè)參數(shù)是resolved
狀態(tài)的回調(diào)函數(shù),第二個(gè)參數(shù)是rejected
的回調(diào)函數(shù)(一般使用 catch 方法來替代第二個(gè)參數(shù)) -
catch
用于指定發(fā)生錯(cuò)誤的回調(diào)函數(shù) -
finally
用于指定 不管Promise
對象最后狀態(tài)如何尖啡,都會執(zhí)行的操作
靜態(tài)方法
Promise.all
將多個(gè)Promise
實(shí)例包裝成一個(gè)新的Promise
實(shí)例
const p = Promise.all([p1, p2, p3]);
只有p1
,p2
,p3
狀態(tài)全為fulfilled
橄仆,p
的狀態(tài)才會變成fulfilled
。此時(shí)可婶,p1
,p2
,p3
的返回值組成一個(gè)數(shù)組沿癞,傳遞給p
的回調(diào)函數(shù)。
只要p1
,p2
,p3
有一個(gè)狀態(tài)為rejected
矛渴,那么p
的狀態(tài)就變成rejected
。此時(shí)第一個(gè)被reject
的實(shí)例的返回值惫搏,會傳遞給p
的回調(diào)函數(shù)具温。Promise.race
將多個(gè)Promise
實(shí)例包裝成一個(gè)新的Promise
實(shí)例
const p = Promise.race([p1, p2, p3]);
三者誰先改變狀態(tài),p
也就會跟著改變狀態(tài)筐赔。率先改變的會將返回值傳遞給p
的回調(diào)函數(shù)铣猩。
await async
async 是加在函數(shù)前的修飾符,被async定義的函數(shù)會默認(rèn)返回一個(gè)Promise對象的實(shí)例茴丰,因此被async標(biāo)記的函數(shù)可以直接then达皿。
await也是一個(gè)修飾符,只能使用于被標(biāo)記為async的方法內(nèi)贿肩,取到值時(shí)才會往下執(zhí)行峦椰。
淺聊 promise setTimeout aysnc await
- 首先,這些不是同步任務(wù)汰规。
- JS是單線程語言(注意區(qū)分線程和進(jìn)程汤功,而且僅JS是單線程,瀏覽器渲染等占用的是其他線程)溜哮,在這個(gè)單線程上滔金,會有同步任務(wù)和異步任務(wù),異步任務(wù)又包括宏觀任務(wù)和微觀任務(wù)茂嗓。
- 宏觀任務(wù)包括(宿主發(fā)起):
script
(整體代碼)setTimeout
setInterval
餐茵、I/O
UI交互事件
postMessage
MessageChannel
setImmediate
- 微觀任務(wù)包括(JS引擎發(fā)起):
promise.then
MutationObserver
process.nextTick
await/async
(實(shí)際上是promise + generator 語法糖) - 執(zhí)行順序 所有的微任務(wù)都存在于某個(gè)宏任務(wù)中,所以一定是由宏任務(wù)開始執(zhí)行述吸。首先我們分析有多少個(gè)宏任務(wù)忿族,在每個(gè)宏任務(wù)中,分析有多少個(gè)微任務(wù),根據(jù)調(diào)用次序肠阱,確定宏任務(wù)中的微任務(wù)執(zhí)行次序票唆,根據(jù)宏任務(wù)的觸發(fā)規(guī)則和調(diào)用次序,確定宏任務(wù)的執(zhí)行次序屹徘,最后確定整個(gè)順序走趋。
console.log('start')
setTimeout(() => {
console.log('setTimeout complete')
})
setTimeout(() => {
new Promise((resolve, reject) => {
for(let i =0;i<5;i++) {
}
console.log('promise3 internal function complete')
resolve()
}).then(() => {
console.log('promise3 complete')
});new Promise((resolve, reject) => {
for(let i =0;i<5;i++) {
}
console.log('promise4 internal function complete')
resolve()
}).then(() => {
console.log('promise4 complete')
});
})
new Promise((resolve, reject) => {
for(let i =0;i<5;i++) {
}
console.log('promise internal function complete')
resolve()
}).then(() => {
console.log('promise complete')
});new Promise((resolve, reject) => {
for(let i =0;i<5;i++) {
}
console.log('promise2 internal function complete')
resolve()
}).then(() => {
console.log('promise2 complete')
});
/**
console.log('end')
start
promise internal function complete
promise2 internal function complete
end
promise complete
promise2 complete
setTimeout complete
promise3 internal function complete
promise4 internal function complete
promise3 complete
promise4 complete
**/
以上代碼的順序大致可以描述為:
-
script
宏觀任務(wù) - 同步任務(wù):
console
setTimeout
Promise
等初始化 - 所有微任務(wù)
promise1.then
promise2.then
- 下一個(gè)宏任務(wù)
setTimeout1
- 同步任務(wù):
console
- 無微觀任務(wù),因此直接開始下一個(gè)宏觀任務(wù)
setTimeout2
- 同步任務(wù)
Promise
初始化 - 所有微任務(wù)
promise3.then
promise4.then
深拷貝 淺拷貝
深拷貝后的所有數(shù)據(jù)均不受被拷貝的數(shù)據(jù)的影響噪伊,至于為什么有可能被影響不做贅述簿煌。
淺拷貝基本上是拷貝第一層的基本數(shù)據(jù)類型值,以及第一層的引用類型地址鉴吹。
淺拷貝 Object.assign
Object.assign
將一個(gè)或多個(gè)源對象的可枚舉屬性的值復(fù)制到目標(biāo)對象姨伟,并返回目標(biāo)對象。
const obj1 = {
name: 'Sue',
age: 18,
getAge: () => this.age,
key: Symbol('key'),
mom: {
name: 'Jane',
age: 45,
key: Symbol('key')
}
}
const obj2 = {}
Object.assign(obj2, obj1)
// {name: 'Sue', age: 18, key: Symbol(key), mom: {…}, getAge: ?}
obj2.mom.age = 50
obj1.mom.age
// 50
以上例子可以證明使用Object.assign
實(shí)現(xiàn)的是淺拷貝豆励,且可以拷貝Symbol
數(shù)據(jù)類型夺荒。
淺拷貝 數(shù)組
concat
let arr = [1, 2, 3];
let arr2 = arr.concat()
slice
let arr = [1, 2, 3];
let arr2 = arr.slice()
深拷貝 JSON.parse + JSON.stringify
let arr = [1, 3, { username: ' kobe' },function(){}];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].username = 'duncan';
console.log(arr[2].username): //kobe
這種方式不能拷貝函數(shù)
let arr = [1, 3, { username: ' kobe' },function(){}];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].username = 'duncan';
console.log(arr2[3]); // undefined
深拷貝 遞歸遍歷
function checkType(value) {
return Object.prototype.toString.call(value).slice(8, -1)
}
function deepClone(value) {
const type = checkType(value);
let result;
if(type === 'Object') {
result = {}
} else if (type === 'Array') {
result = []
} else {
return value
}
for(let i in value) {
const _v = value[i];
const _vt = checkType(_v);
if (_vt === 'Array' || _vt === 'Object') {
result[i] = deepClone(_v)
} else {
result[i] = _v
}
}
return result
}