《你不知道的JavaScript 上》讀書筆記

標(biāo)簽: js 還有原型鏈要更新呀


作用域

  • 作用域是查找變量的一套規(guī)則
  • 如果查找的目的是對(duì)變量進(jìn)行賦值卖氨,則進(jìn)行LHS查詢绳姨;如果查找的目的是獲取變量的值满哪,則進(jìn)行RHS查詢。Left/Right Hand Sidei,變量在等號(hào)左邊或等號(hào)右邊
  • LHS查詢:賦值操作躯枢,如=操作符、調(diào)用函數(shù)時(shí)傳參
 function foo(e) {
    var b = a;
    return a + b;
}
var c = foo(2);
  • 例子中LHS查詢有三處:b=a c=foo(2) 把2賦給a(隱式變量分配)
  • RHS查詢有四處:foo(2) =a a b
  • 異常
    • ReferenceError,引用一個(gè)未被定義的變量剥纷。
    • TypeError,對(duì)結(jié)果的操作不合理呢铆,如引用一個(gè)undefined類型的值的屬性
    • SyntaxError晦鞋,語(yǔ)法錯(cuò)誤
  • 如何判斷當(dāng)前是否在嚴(yán)格模式下:函數(shù)直接調(diào)用時(shí)this指向運(yùn)行時(shí)環(huán)境的全局對(duì)象。在非嚴(yán)格模式下棺克,瀏覽器中全局對(duì)象為Window悠垛,Nodejs控制臺(tái)中全局對(duì)象為Global;在嚴(yán)格模式下娜谊,全局對(duì)象是undefined确买。因此可判斷如下
var isStrict = (function(){return !this;}());
console.log('isStrict ' + isStrict);
  • 編譯。傳統(tǒng)編譯語(yǔ)言纱皆,代碼執(zhí)行前需要編譯拇惋,包括三個(gè)過程:詞法分析(將代碼拆分為詞法單元)周偎、語(yǔ)法解析(將詞法單元數(shù)組轉(zhuǎn)換為語(yǔ)法樹)、代碼生成(生成可執(zhí)行代碼)

詞法作用域VS動(dòng)態(tài)作用域

  • 編譯的詞法分析階段知曉各標(biāo)識(shí)符及如何聲明的撑帖,因此可以在執(zhí)行過程中進(jìn)行查找蓉坎。詞法作用域意味著作用域在書寫代碼時(shí)函數(shù)聲明的位置決定。
  • eval with
  • 詞法作用域又叫靜態(tài)作用域胡嘿,和動(dòng)態(tài)作用域的區(qū)別是動(dòng)態(tài)作用域是在運(yùn)行時(shí)而非定義時(shí)確定的蛉艾。詞法作用域關(guān)注函數(shù)在何處聲明,動(dòng)態(tài)作用域關(guān)注函數(shù)從何處調(diào)用衷敌,其作用域鏈?zhǔn)腔谶\(yùn)行時(shí)的調(diào)用棧
  • wiki:大多數(shù)現(xiàn)在程序設(shè)計(jì)語(yǔ)言都是采用靜態(tài)作用域規(guī)則勿侯,如C/C++、C#缴罗、Python助琐、Java、JavaScript面氓;采用動(dòng)態(tài)作用域的語(yǔ)言有Pascal兵钮、Emacs Lisp、Common Lisp(兼有靜態(tài)作用域)舌界、Perl(兼有靜態(tài)作用域)掘譬。C/C++是靜態(tài)作用域語(yǔ)言,但在宏中用到的名字呻拌,也是動(dòng)態(tài)作用域葱轩。
// 詞法作用域:
function foo() {
    print a;    // 輸出2
}

function bar() {
    var a = 3;
    foo();
}

var a = 2;
bar();
// 動(dòng)態(tài)作用域
function foo() {
    print a;    // 輸出3而不是2
}

function bar() {
    var a = 3;
    foo();
}

var a = 2;
bar();

函數(shù)作用域

  • 最小特權(quán)原則
  • 在軟件設(shè)計(jì)中,應(yīng)該最小限度地暴露必要內(nèi)容藐握,而將其隱藏起來靴拱,比如某個(gè)模塊或?qū)ο蟮腁PI設(shè)計(jì)。
  • 避免暴露私有的變量或函數(shù)猾普,避免其被有意或無意地以非預(yù)期的方式使用缭嫡。
function doSomething(a){
    b = a + doSomethingElse(a * 2);
    console.log(b * 3);
}
function doSomethingElse(e){
    return e - 1;
}
var b;
doSomething(2); // 15
  • 上例中b doSomethisElse均應(yīng)是doSomething的內(nèi)部?jī)?nèi)容,改進(jìn):
function doSomething(a){
    function doSomethingElse(e){
        return e - 1;
    }
    var b;
    b = a + doSomethingElse(a * 2);
    console.log(b * 3);
}
doSomething(2); // 15           
  • 立即執(zhí)行函數(shù)表達(dá)式IIFE 的兩種形式
(function(){console.log('a');})() // a
// 第一個(gè)()將函數(shù)變成表達(dá)式抬闷,第二個(gè)()立即執(zhí)行這個(gè)函數(shù)

+function(){console.log('a');}() // a
// + 也會(huì)將函數(shù)變成一個(gè)表達(dá)式妇蛀,js會(huì)解析成運(yùn)算(bootstrap的js源碼采用此種方式),第二個(gè)()立即執(zhí)行這個(gè)函數(shù)

js中神奇的 +
new Date(); // Fri Aug 05 2016 16:58:08 GMT+0800 (CST)
+ new Date; // 1470387494773 (一個(gè)時(shí)間戳)

// 時(shí)間戳:unix時(shí)間笤成,從`協(xié)調(diào)時(shí)間時(shí)`1970年1月1日0時(shí)0分0秒起至現(xiàn)在的總秒數(shù)
  • IIFE進(jìn)階1:當(dāng)作函數(shù)調(diào)用并傳參评架,提高可讀性,改進(jìn)代碼風(fēng)格炕泳。
var a = 2;
(function IIFE(global){
    var a = 1;
    console.log(a); // 1
    console.log(global.a); // 2
})(window);
  • IIFE進(jìn)階2:倒置代碼的運(yùn)行順序纵诞,將需要運(yùn)行的代碼放在第二位,在IIFE執(zhí)行后當(dāng)作參數(shù)傳遞進(jìn)去
var a = 2;
(function IIFE(def){
    def(window);
    console.log(a); // 2
}(function def(global){    
    var a = 1;
    console.log(a); // 1
    console.log(global.a); // 2
}));

塊級(jí)作用域

let ES6變量聲明

  • 隱式綁定在所在塊級(jí)作用域
var foo = true;
if (foo) {
    var bar = foo * 2;
    console.log(bar); // 2
}
console.log(bar); // 2
var foo = true;
if (foo) {
    let bar = foo * 2;
    console.log(bar); // 2
}
console.log(bar); // ReferenceError: bar is not defined
  • 顯式創(chuàng)建一個(gè)塊級(jí)作用域
var foo = true;
if (foo) {
    {
        let bar = foo * 2;
        console.log(bar); // 2
    } // 一個(gè)顯式的塊
    console.log(bar); // ReferenceError: bar is not defined
}
console.log(bar); // ReferenceError: bar is not defined
  • let聲明的變量在塊級(jí)作用域中不會(huì)提升
  • 垃圾回收培遵,let塊級(jí)作用域中的內(nèi)容執(zhí)行完后被銷毀
  • let循環(huán)
for(let i=0;1<10;i++) {
    console.log(i);
}
console.log(i); // ReferenceError: i is not defined
  • let將i綁定在for循環(huán)的塊中浙芙,不會(huì)污染全局變量登刺,并且將其重新綁定在循環(huán)的每一次迭代中。

const ES6變量聲明

  • const聲明的變量只在聲明時(shí)賦值嗡呼,不可修改纸俭,否則會(huì)報(bào)錯(cuò)
  • const聲明的變量必須在聲明時(shí)就賦值。
var foo = true;
if(foo) {
    var a = 2;
    const b = 3;
    const c = {};
    const d; // SyntaxError: Missing initializer in const declaration
    a = 3;
    b = 4; // TypeError: Assignment to constant variable
    c.a = 1; 
    console.log(c); // {a: 1}
    console.log(c.a); // 1
}
console.log(a); // 3
console.log(b); // ReferenceError: b is not defined

提升(hoisting)

  • 理解提升:引擎會(huì)在解釋js代碼前首先進(jìn)行編譯南窗,編譯的一部分工作就是找到所有聲明揍很,并用合適的作用域?qū)⑺麄儼饋怼K新暶鲿?huì)在代碼執(zhí)行前被處理万伤。
  • 編譯階段聲明會(huì)被提升窒悔,賦值或其他運(yùn)算邏輯留在原地在執(zhí)行過程中被處理。
  • 函數(shù)聲明提升的同時(shí)敌买,包含了實(shí)際函數(shù)的隱藏值简珠。
foo(); // undefined;
function foo() {
    console.log(a);
    var a = 1;
}
// 此時(shí)foo()可正常調(diào)用,foo()函數(shù)內(nèi)部也有變量提升
  • 函數(shù)表達(dá)式和變量聲明類似虹钮,變量標(biāo)識(shí)符會(huì)提升聋庵,賦值沒有被提升
foo(); // TypeError: foo is not a function
var foo = function() {
    console.log(a);
    var a = 2;
}
// 此時(shí)報(bào)錯(cuò)不是ReferenceError,因?yàn)榇藭r(shí)變量標(biāo)識(shí)符foo被提升到了頂部,var foo;但賦值未被提升芜抒,此時(shí)foo的值是undefined,對(duì)一個(gè)undefined值進(jìn)行函數(shù)調(diào)用是非法操作珍策,因此拋出TypeError異常
  • 提升中函數(shù)優(yōu)先
foo(); // 1
var foo;
function foo() { 
    console.log(1); 
} 
foo = function() {
    console.log(2);
}

引擎解釋:

function foo() { 
    console.log(1); 
} 
foo(); 
foo = function() {
    console.log(2);
}
// var foo;是重復(fù)聲明被忽略托启。
foo(); // 此時(shí)再調(diào)用foo()值為2宅倒,第二次定義的函數(shù)表達(dá)式覆蓋了前面的函數(shù)聲明。
  • 一個(gè)函數(shù)聲明會(huì)被提升到所在作用域的頂部
foo(); //  TypeError: foo is not a function
var a = true;
if (a) {
    function foo(){console.log(11);} 
} else {
    function foo(){console.log(22);} // 
}
// 老版本中兩個(gè)foo函數(shù)會(huì)被提升屯耸,第二個(gè)foo函數(shù)會(huì)覆蓋第一個(gè)拐迁,因此foo()執(zhí)行結(jié)果是22;新版本中if塊內(nèi)僅提升函數(shù)聲明疗绣,但未包含實(shí)際函數(shù)的隱藏值线召,因此foo()執(zhí)行結(jié)果是TypeError

作用域閉包

  • 閉包:內(nèi)部函數(shù)可以訪問外部函數(shù)作用域,并持有對(duì)原始詞法作用域的引用多矮。
  • 經(jīng)典例子:循環(huán)與閉包
for(var i=0;i<5;i++) {
    setTimeout(function timer() {
        console.log(i);
    }, i*1000);
}
  • 輸出五次5的原因:循環(huán)中的五個(gè)函數(shù)在每次迭代中分別定義缓淹,但它們被封閉在一個(gè)共享的全局作用域,因此共享一個(gè)i的引用塔逃。
  • 解決方法是給每次循環(huán)的函數(shù)創(chuàng)建一個(gè)單獨(dú)的作用域
for(var i=0;i<5;i++) {
    (function() {
        var j = i;
        setTimeout(function timer() {
            console.log(j);
        }, j*1000);
    })()
}
// IIFE為每次迭代創(chuàng)建一個(gè)新的作用域讯壶,延遲函數(shù)的回調(diào)可以將新的作用域封閉在每個(gè)迭代內(nèi)部,每個(gè)迭代都有一個(gè)正確的變量湾盗。

or

for(let i=0;i<5;i++) { // i在每次迭代都會(huì)聲明
    setTimeout(function timer() {
        console.log(i);
    }, i*1000);
}

or

for(var i=0;i<5;i++) {
    let j = i; // 創(chuàng)建塊級(jí)作用域
    setTimeout(function timer() {
        console.log(i);
    }, i*1000);
}

this

  • 隱式傳遞一個(gè)對(duì)象引用
  • 一個(gè)函數(shù)被調(diào)用時(shí)伏蚊,會(huì)創(chuàng)建一個(gè)執(zhí)行上下文,包含函數(shù)在哪里被調(diào)用格粪,函數(shù)的調(diào)用方式躏吊,傳入的參數(shù)等氛改,this是其中的一個(gè)屬性,是函數(shù)被調(diào)用時(shí)發(fā)生的綁定比伏。this既不指向函數(shù)本身也不指向函數(shù)的詞法作用域胜卤。
  • 直接調(diào)用 new dot(.) call apply bind
function fo(){this.x='x';};
// 直接調(diào)用
fo(); // ()代表立即執(zhí)行
x;    // 'x' this指向全局對(duì)象window
var fn = function(){'use strict';this.x='x';};
x;    // ReferenceError: x is not defined,此時(shí)全局對(duì)象是undefined;

// new調(diào)用
var foo = new fo();
foo.x; // 'x'

// dot(.)調(diào)用
var aa = {x: 'aaa',fo: fo};
aa.x; // 'aaa'
aa.fo; // function fo(){this.x='x';};
aa.fo();
aa.x; // 'x'

// call
var b = {x: 'bb'};
b.x; // 'bb'
b.fo(); // TypeError: b.fo is not a function
fo.call(b);
b.x; // 'x'

// apply bind 用法和call相似,this均指向fo.call(args),args的第一個(gè)參數(shù)

對(duì)象

  • javascript六種類型:string number boolean null undefined object
  • 內(nèi)置對(duì)象凳怨,對(duì)象子類型:string number boolean object function array date regexp error
  • ES6增加了可計(jì)算屬性名瑰艘,用[]包裹,eg:
var prefix = foo; 
var myObject = {[prefix + 'bar']: 'hello', [prefix + 'baz']: 'world'};
myObject['foobar']; // hello
myObject['foobaz']; // world
  • 數(shù)組也是對(duì)象肤舞,但是最好用對(duì)象存儲(chǔ)鍵值對(duì)紫新,用數(shù)組存儲(chǔ)數(shù)組下標(biāo)值對(duì);
// 給數(shù)組定義數(shù)字形式的屬性名的bug
var myArray = ['hello', 'world', 42];
myArray.baz = 'bar';
myArray.length; // 3
myArray.baz; // 'bar'
myArray['3'] = 'test'; 
myArray.length; // 4 
myArray; // ['hello', 'world', 42, 'test']
  • 檢查屬性是否存在
var myObject = { a: 2 };
myObject['b']; // 2
myObject.b; // 2
'a' in myObject; // true
myObject.hasOwnProperty('a'); // true
// in操作符檢查屬性是否在對(duì)象中或prototype原型鏈中李剖;hasOwnProperty僅檢查屬性是否在對(duì)象中芒率。
  • 屬性描述符 ES5
  • getOwnpropertyDescriptor查詢對(duì)象的屬性的屬性描述符,格式:Object.getOwnpropertyDescriptor(對(duì)象名,'屬性名');
Object.getOwnPropertyDescriptor(myObject,'a');
// Object {
//    value: 2, 
//    writable: true,     可寫篙顺,可修改屬性的值value
//    enumerable: true,   可枚舉偶芍,出現(xiàn)在對(duì)象屬性的遍歷中
//    configurable: true  可配置,可用defineProperty修改屬性描述符德玫,刪除一個(gè)屬性delete myObject.a; if false,除value和writable外的特性不能被改變匪蟀。
// }
  • defineProperty添加一個(gè)新屬性或修改一個(gè)已有屬性(的屬性描述符),格式:Object.defineProperty(對(duì)象名,'屬性名',{});
Object.defineProperty(myObject,'b',{
       value: 3,
       writable: true,
       configurable: true,
       enumerable: true
}); // Object {a: 2, b: 3}
Object.getOwnPropertyDescriptor(myObject,'b');
// Object {value: 3, writable: true, enumerable: true, configurable: true}
  • 枚舉屬性 enumerable
var myObject = {};

Object.defineProperty(myObject,'a',{
      value: 2,
      enumerable: true
});

Object.defineProperty(myObject,'b',{
      value: 3,
      enumerable: false
});

myObject.b; // 3
'b' in myObject; // true
myObject.hasOwnProperty('b'); // true

for(var k in myObject) {
      console.log(k+': '+myObject[k]);
} // a: 2
// myObject.b存在且可正常訪問宰僧,但不出現(xiàn)在屬性的遍歷中

myObject.propertyIsEnumerable('b'); // false
// propertyIsEnumerable檢查給定的屬性名存在于對(duì)象中(不包括原型鏈中)&& 可枚舉的

Object.keys(myObject); // ['a'] 返回可枚舉屬性

Object.getOwnPropertyNames(myObject); // ['a','b'] 返回所有屬性
  • 數(shù)組遍歷注意事項(xiàng):數(shù)組上應(yīng)用for..in循環(huán)時(shí)不僅包含所有數(shù)值索引材彪,且包含所有可枚舉屬性;因此遍歷數(shù)組最好使用傳統(tǒng)for循環(huán)或forEach循環(huán)琴儿。ES6中新增for..of循環(huán)遍歷數(shù)據(jù)結(jié)構(gòu)段化。數(shù)組有內(nèi)置的@@iterator,因此for..of可直接遍歷數(shù)組造成,而不能直接遍歷對(duì)象显熏。P122
var myArray = ['hello','world',42];
myArray.baz = 'bar';
myArray['3']='test';
myArray; // ["hello", "world", 42, "test"]

for(var k in myArray) {console.log(k,myArray[k]);}
//  0 hello  1 world  2 42  3 test  baz bar

for(vari=0;i<myArray.length;i++){console.log(i,myArray[i])}; 
//  0 hello  1 world  2 42  3 test 

myArray.forEach(function(e){console.log(e)}); 
// hello world 42 test

for(var v of myArray){console.log(v);};
// hello world 42 test

for(var v of myObject){console.log(v);};
// TypeError: myObject[Symbol.iterator] is not a function

原型prototype


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市晒屎,隨后出現(xiàn)的幾起案子喘蟆,更是在濱河造成了極大的恐慌,老刑警劉巖鼓鲁,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕴轨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡坐桩,警方通過查閱死者的電腦和手機(jī)尺棋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人膘螟,你說我怎么就攤上這事成福。” “怎么了荆残?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵奴艾,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我内斯,道長(zhǎng)蕴潦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任俘闯,我火速辦了婚禮潭苞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘真朗。我一直安慰自己此疹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布遮婶。 她就那樣靜靜地躺著蝗碎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旗扑。 梳的紋絲不亂的頭發(fā)上蹦骑,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音臀防,去河邊找鬼眠菇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛清钥,可吹牛的內(nèi)容都是我干的琼锋。 我是一名探鬼主播放闺,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼祟昭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了怖侦?” 一聲冷哼從身側(cè)響起篡悟,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匾寝,沒想到半個(gè)月后搬葬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡艳悔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年急凰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猜年。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抡锈,死狀恐怖疾忍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情床三,我是刑警寧澤一罩,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站撇簿,受9級(jí)特大地震影響聂渊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜四瘫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望找蜜。 院中可真熱鬧诊胞,春花似錦、人聲如沸锹杈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)竭望。三九已至邪码,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咬清,已是汗流浹背闭专。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旧烧,地道東北人影钉。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像掘剪,于是被迫代替她去往敵國(guó)和親平委。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容