最近看了《你不知道的JavaScript》這本書的上卷,感覺相當(dāng)不錯(cuò),一些以前模糊不清的感念從這本書里都弄懂了,下面是我的簡單的筆記,寫在這里權(quán)做記錄。
變量的賦值:編譯器在當(dāng)前作用域中聲明變量,運(yùn)行時(shí)引擎在作用域中查找該變量,并為其賦值
var a = 2; //相當(dāng)于var a; 和 a = 2;兩條指令
LHS和RHS,實(shí)際上相當(dāng)于左引用和右引用,也可以說是定值和引用,RHS查詢失敗拋出referenceError異常,LHS查詢失敗會隱式創(chuàng)建一個(gè)全局變量(非嚴(yán)格模式下),或者拋出referenceError異常(嚴(yán)格模式下)
referenceError作用域查找不到, typeError作用域鏈查找到了,但是類型錯(cuò)誤。
function foo(a) {
console.log(a + b); //referenceError
b = a;//創(chuàng)建一個(gè)全局變量
}
foo(2);
欺騙詞法(會被嚴(yán)格模式影響),導(dǎo)致無法在編譯時(shí)對作用域查找進(jìn)行優(yōu)化
- eval(),eval修改其所處的詞法作用域,就像eval中的代碼寫在這里一樣
function foo(str, a) {
eval(str); //欺騙
console.log(a, b);
}
var b = 2;
foo("var b = 3", 1); //1, 3
嚴(yán)格模式下eval有自己的詞法作用域。
function foo(str) {
"use strict";
eval(str);
console.log(a); // referenceError
}
foo("var a = 2"); //1, 3
- with(),根據(jù)傳遞給它的對象創(chuàng)建一個(gè)新的詞法作用域,將對象的屬性作為該對象中的標(biāo)識符
function foo(obj) {
with(obj) {
a = 2;
}
}
var o1 = {
a:3
};
var a2 = {
b:3
};
foo(o1);
console.log(o1.a); // 2
foo(o2);
console.log(o2.a); //undefined
console.log(a); //2
函數(shù)聲明和函數(shù)表達(dá)式:名稱標(biāo)識符綁定的位置不同(函數(shù)所在作用域、函數(shù)本身)
//foo被綁定在所在作用域中
var a = 2;
function foo() {
var a = 3;
console.log(a); //3
}
foo();
console.log(a);//2
//函數(shù)聲明,foo只能在函數(shù)內(nèi)部使用,不會污染外部作用域
var a = 2;
(function foo() {
var a = 3;
console.log(a); //3
})();
console.log(a);//2
匿名函數(shù)缺點(diǎn):1. 調(diào)用棧更難追蹤;2. 自我引用更難;3. 代碼更難理解
塊作用域:
- with:用with從對象中創(chuàng)建的作用域僅在with聲明中有效,
- catch:
try {
undefined();//執(zhí)行一個(gè)非法操作來強(qiáng)制制造出一個(gè)異常
}
catch(err) {
console.log(err);//可以正常執(zhí)行
}
console.log(err);//referenceError: err not found
- let為其聲明的變量隱式的劫持了所在的塊作用域(不會在塊內(nèi)進(jìn)行變量提升)
var foo = true;
if(foo) {
let bar = foo *2;
bar = something(bar);
console.log(bar);
}
console.log(bar);//referenceError
- const,創(chuàng)建塊級作用域,值固定。
變量提升:函數(shù)優(yōu)先,且重復(fù)的var聲明會被忽略,但重復(fù)的函數(shù)聲明會覆蓋之前的聲明。
閉包:函數(shù)當(dāng)成值傳遞的時(shí)候,會出現(xiàn)閉包,因?yàn)楹瘮?shù)不是在其定義時(shí)所在的作用域里執(zhí)行,但執(zhí)行時(shí)仍保存了書寫位置的作用域
function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
var baz = foo();
baz();//2,這就是閉包的效果
循環(huán)和閉包
共享閉包作用域、共享變量
for(var i = 0; i < 3; i++) {
setTimeout(function timer() {
console.log(i); //3,3,3
}, 0);
}
IIFE(立即執(zhí)行函數(shù)表達(dá)式)形成的作用域里并沒有內(nèi)容,仍然不行
for(var i = 0; i < 3; i++) {
(function () {
setTimeout(function timer() {
console.log(i); //3,3,3
}, 0);
})();
}
每個(gè)迭代生成一個(gè)新的作用域,保存了正確的變量值
for(var i = 0; i < 3; i++) {
(function () {
var j = i;
setTimeout(function timer() {
console.log(j); //0,1,2
}, 0);
})();
}
let劫持塊作用域
for(var i = 0; i < 3; i++) {
let j = i;
setTimeout(function timer() {
console.log(j); //0,1,2
}, 0);
}
let表明i每次迭代聲明一次,并用上次迭代結(jié)束時(shí)的值初始化這個(gè)變量
for(let i = 0; i < 3; i++) {
setTimeout(function timer() {
console.log(j); //0,1,2
}, 0);
}
模塊
1、必須有外部封閉函數(shù),且至少執(zhí)行一次;2、封閉函數(shù)必須返回至少一個(gè)內(nèi)部函數(shù)
function CoolModule() {
var something = 'cool';
var another = [1, 2, 3];
function doSomething() {
console.log(something);
}
function doAnother() {
console.log(another.join('!'));
}
return {
ds: doSomething,
da: doAnother
};
}
var foo = CoolModule();
foo.ds();
foo.da();