成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

重讀你不知道的JS (上) 第一節(jié)三章

lavor / 1535人閱讀

摘要:如果是聲明中的第一個(gè)詞,那么就是一個(gè)函數(shù)聲明,否則就是一個(gè)函數(shù)表達(dá)式。給函數(shù)表達(dá)式指定一個(gè)函數(shù)名可以有效的解決以上問題。始終給函數(shù)表達(dá)式命名是一個(gè)最佳實(shí)踐。也有開發(fā)者干脆關(guān)閉了靜態(tài)檢查工具對(duì)重復(fù)變量名的檢查。

你不知道的JS(上卷)筆記

你不知道的 JavaScript

JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 JavaScript 開發(fā)者,如果沒有認(rèn)真學(xué)習(xí)的話也無法真正理解它們.

上卷包括倆節(jié):

作用域和閉包

this 和對(duì)象原型

作用域和閉包

希望 Kyle 對(duì) JavaScript 工作原理每一個(gè)細(xì)節(jié)的批判性思 考會(huì)滲透到你的思考過程和日常工作中。知其然,也要知其所以然。

函數(shù)作用域和塊作用域

正如我們?cè)诘?2 章中討論的那樣,作用域包含了一系列的“氣泡”,每一個(gè)都可以作為容 器,其中包含了標(biāo)識(shí)符(變量、函數(shù))的定義。這些氣泡互相嵌套并且整齊地排列成蜂窩 型,排列的結(jié)構(gòu)是在寫代碼時(shí)定義的。

但是,究竟是什么生成了一個(gè)新的氣泡?只有函數(shù)會(huì)生成新的氣泡嗎? JavaScript 中的其 他結(jié)構(gòu)能生成作用域氣泡嗎?

函數(shù)中的作用域

JavaScript 具有基于函數(shù)的作用域;

無論標(biāo)識(shí)符 聲明出現(xiàn)在作用域中的何處,這個(gè)標(biāo)識(shí)符所代表的變量或函數(shù)都將附屬于所處作用域的氣 泡。

函數(shù)作用域的含義是指,屬于這個(gè)函數(shù)的全部變量都可以在整個(gè)函數(shù)的范圍內(nèi)使用及復(fù) 用(事實(shí)上在嵌套的作用域中也可以使用)。

這種設(shè)計(jì)方案是非常有用的,能充分利用 JavaScript 變量可以根據(jù)需要改變值類型的“動(dòng)態(tài)”特性。這是什么意思?

隱藏的內(nèi)部實(shí)現(xiàn)

可以把變量和函數(shù)包裹在一個(gè)函數(shù)的作用域中,然后用這個(gè)作用域 來“隱藏”它們。

Q: 為什么“隱藏”變量和函數(shù)是一個(gè)有用的技術(shù)?

A: 大都是從最小特權(quán)原則中引申出來 的,也叫最小授權(quán)或最小暴露原則。這個(gè)原則是指在軟件設(shè)計(jì)中,應(yīng)該最小限度地暴露必 要內(nèi)容,而將其他內(nèi)容都“隱藏”起來,比如某個(gè)模塊或?qū)ο蟮?API 設(shè)計(jì)。

設(shè)計(jì)上將具體內(nèi)容私有化了,設(shè)計(jì)良好的軟件都會(huì) 依此進(jìn)行實(shí)現(xiàn)。

規(guī)避沖突

全局命名空間
通常會(huì)在全局作用域中聲明一個(gè)名字足夠獨(dú)特的變量,通常是一個(gè)對(duì)象。這個(gè)對(duì)象 被用作庫的命名空間,所有需要暴露給外界的功能都會(huì)成為這個(gè)對(duì)象(命名空間)的屬 性,而不是將自己的標(biāo)識(shí)符暴漏在頂級(jí)的詞法作用域中。

模塊管理
任何庫都無需將標(biāo)識(shí)符加入到全局作用域中,而是通過依賴管理器 的機(jī)制將庫的標(biāo)識(shí)符顯式地導(dǎo)入到另外一個(gè)特定的作用域中。

避免同名標(biāo)識(shí)符之間的沖突

函數(shù)作用域

在任意代碼片段外部添加包裝函數(shù),可以將內(nèi)部的變量和函數(shù)定義“隱
藏”起來,外部作用域無法訪問包裝函數(shù)內(nèi)部的任何內(nèi)容。

這種技術(shù)可以解決一些問題,但是它并不理想,因?yàn)闀?huì)導(dǎo)致一些額外的問題:

必須聲明一個(gè)具名函數(shù) foo(),意味著 foo 這個(gè)名稱本身“污染”了所在作用域(在這個(gè) 例子中是全局作用域)

必須顯式地通過函數(shù)名(foo())調(diào)用這個(gè)函數(shù)才能運(yùn)行其 中的代碼。

如果函數(shù)不需要函數(shù)名(或者至少函數(shù)名可以不污染所在作用域),并且能夠自動(dòng)運(yùn)行, 這將會(huì)更加理想。

(function foo(){ // <-- 添加這一行
  var a = 3;
  console.log( a ); // 3
})(); // <-- 以及這一行
console.log( a ); // 2

函數(shù)聲明和函數(shù)表達(dá)式之間最重要的區(qū)別是它們的名稱標(biāo)識(shí)符將會(huì)綁定在何處。

注意:區(qū)分函數(shù)聲明和表達(dá)式最簡(jiǎn)單的方法是看 function 關(guān)鍵字出現(xiàn)在聲明中的位 置(不僅僅是一行代碼,而是整個(gè)聲明中的位置)。如果 function 是聲明中 的第一個(gè)詞,那么就是一個(gè)函數(shù)聲明,否則就是一個(gè)函數(shù)表達(dá)式。

片段中 foo 被綁定在函數(shù)表達(dá)式自身的函數(shù)中而不是所在作用域中。

類似的還有于 +function foo() {}() 對(duì)函數(shù)求值的操作,都能做到避免泄露

換句話說,(function foo(){ .. })作為函數(shù)表達(dá)式意味著foo只能在..所代表的位置中 被訪問,外部作用域則不行。foo 變量名被隱藏在自身中意味著不會(huì)非必要地污染外部作 用域。

匿名和具名
setTimeout( function() {
         console.log("I waited 1 second!");
}, 1000 );

這叫做匿名函數(shù)表達(dá)式, 因?yàn)閒unction()沒有名稱標(biāo)識(shí)符。函數(shù)表達(dá)式可以是匿名的,而函數(shù)聲明則不可以省略函數(shù)名.

匿名函數(shù)表達(dá)式寫起來簡(jiǎn)單快捷,但是它有幾個(gè)缺點(diǎn)需要考慮:

匿名函數(shù)在棧追蹤中不會(huì)顯示出有意義的函數(shù)名,使得調(diào)試很困難。

如果沒有函數(shù)名,當(dāng)函數(shù)需要引用自身時(shí),只能使用已經(jīng)過期的arguments.callee引用,比如在遞歸中。另一個(gè)函數(shù)需要引用自身的例子是在事件觸發(fā)后事件監(jiān)聽器需要解綁自身。

匿名函數(shù)省略了對(duì)于代碼可讀性/可理解性很重要的函數(shù)名。一個(gè)描述性的名詞可以讓代碼不言自明。

行內(nèi)函數(shù)表達(dá)式非常強(qiáng)大且有用——匿名和具名之間的區(qū)別并會(huì)有對(duì)這點(diǎn)有任何影響。 給函數(shù)表達(dá)式指定一個(gè)函數(shù)名可以有效的解決以上問題。

始終給函數(shù)表達(dá)式命名是一個(gè)最佳實(shí)踐。

setTimeout( function timeoutHandler() { // <-- 快看,我有名字了!
  console.log( "I waited 1 second!" );
}, 1000 );
立即執(zhí)行函數(shù)表達(dá)式

幾年前社區(qū)給它規(guī)定了一個(gè)術(shù)語:IIFE,代表立即執(zhí)行函數(shù)表達(dá)式 (Immediately Invoked Function Expression);

IIFE的形式有下面?zhèn)z種:
(function(){ .. })()
(function(){ .. }())

用法1, 把它們當(dāng)作函數(shù)調(diào)用并傳遞參數(shù)進(jìn)去
例如:

var a = 2;
(function IIFE( global ) {
  var a = 3;
  console.log( a ); // 3 
  console.log( global.a ); // 2
})( window );
console.log( a ); // 2

我們將 window 對(duì)象的引用傳遞進(jìn)去,但將參數(shù)命名為 global,因此在代碼風(fēng)格上對(duì)全局 對(duì)象的引用變得比引用一個(gè)沒有“全局”字樣的變量更加清晰。當(dāng)然可以從外部作用域傳 遞任何你需要的東西,并將變量命名為任何你覺得合適的名字。這對(duì)于改進(jìn)代碼風(fēng)格是非 常有幫助的。

用法2,解決 undefined 標(biāo)識(shí)符的默認(rèn)值被錯(cuò)誤覆蓋導(dǎo)致的異常(雖 然不常見)。
例如:將一個(gè)參數(shù)命名為 undefined,但是在對(duì)應(yīng)的位置不傳入任何值,這樣就可以 保證在代碼塊中 undefined 標(biāo)識(shí)符的值真的是 undefined:

undefined = true; // 給其他代碼挖了一個(gè)大坑!絕對(duì)不要這樣做!
(function IIFE( undefined ) {
var a;
if (a === undefined) {

console.log( "Undefined is safe here!" );

}
})();

用法3:倒置代碼的運(yùn)行順序
例如:將需要運(yùn)行的函數(shù)放在第二位,在 IIFE 執(zhí)行之后當(dāng)作參數(shù)傳遞進(jìn)去。這種模式在 UMD(Universal Module Definition)項(xiàng)目中被廣 泛使用。盡管這種模式略顯冗長(zhǎng),但有些人認(rèn)為它更易理解。

var a = 2;
(function IIFE( def ) {
  def( window );
})(function def( global ) {
  var a = 3;
  console.log( a ); // 3 
  console.log( global.a ); // 2
});

塊作用域

塊作用域的用處:變量的聲明應(yīng)該距離使用的地方越近越好,并最大限度地本地化。

塊作用域是一個(gè)用來對(duì)之前的最小授權(quán)原則進(jìn)行擴(kuò)展的工具,將代碼從在函數(shù)中隱藏信息 擴(kuò)展為在塊中隱藏信息。

為什么要把一個(gè)只在 for 循環(huán)內(nèi)部使用(至少是應(yīng)該只在內(nèi)部使用)的變量 i 污染到整個(gè)
函數(shù)作用域中呢?

可惜,表面上看 JavaScript 并沒有塊作用域的相關(guān)功能。

with

with 關(guān)鍵字。它不僅是一個(gè)難于理解的結(jié)構(gòu),同時(shí)也是塊作用域的一 個(gè)例子(塊作用域的一種形式),用 with 從對(duì)象中創(chuàng)建出的作用域僅在 with 聲明中而非外 部作用域中有效。

try/catch

非常少有人會(huì)注意到 JavaScript 的 ES3 規(guī)范中規(guī)定 try/catch 的 catch 分句會(huì)創(chuàng)建一個(gè)塊作
用域,其中聲明的變量?jī)H在 catch 內(nèi)部有效。

例如:

  try {
    undefined(); // 執(zhí)行一個(gè)非法操作來強(qiáng)制制造一個(gè)異常
  }
  catch (err) {
    console.log( err ); // 能夠正常執(zhí)行! 
  }
  console.log( err ); // ReferenceError: err not found

盡管這個(gè)行為已經(jīng)被標(biāo)準(zhǔn)化,并且被大部分的標(biāo)準(zhǔn) JavaScript 環(huán)境(除了老 版本的 IE 瀏覽器)所支持,但是當(dāng)同一個(gè)作用域中的兩個(gè)或多個(gè) catch 分句 用同樣的標(biāo)識(shí)符名稱聲明錯(cuò)誤變量時(shí),很多靜態(tài)檢查工具還是會(huì)發(fā)出警告。 實(shí)際上這并不是重復(fù)定義,因?yàn)樗凶兞慷急话踩叵拗圃趬K作用域內(nèi)部, 但是靜態(tài)檢查工具還是會(huì)很煩人地發(fā)出警告。為了避免這個(gè)不必要的警告,很多開發(fā)者會(huì)將 catch 的參數(shù)命名為 err1、 err2 等。也有開發(fā)者干脆關(guān)閉了靜態(tài)檢查工具對(duì)重復(fù)變量名的檢查。

let

ES6 改變了現(xiàn)狀,引入了新的 let 關(guān)鍵字,提供了除 var 以外的另一種變量聲明方式。

  var foo = true;
  if (foo) {
    let bar = foo * 2;
    bar = something( bar );
    console.log( bar );
  }
  console.log( bar ); // ReferenceError

ES6中的if表達(dá)式中的{}并不具備塊級(jí)作用域的劃分,僅僅只能表明一個(gè)語句塊,因?yàn)橐谄渲新暶鲏K級(jí)作用域變量還需要let來輔助。

let 關(guān)鍵字可以將變量綁定到所在的任意作用域中(通常是 { .. } 內(nèi)部)。換句話說,let為其聲明的變量隱式地了所在的塊作用域。

在開發(fā)和修改代碼的過 程中,如果沒有密切關(guān)注哪些塊作用域中有綁定的變量,并且習(xí)慣性地移動(dòng)這些塊或者將 其包含在其他的塊中,就會(huì)導(dǎo)致代碼變得混亂。
為塊作用域顯式地創(chuàng)建塊可以部分解決這個(gè)問題,使變量的附屬關(guān)系變得更加清晰。通常 來講,顯式的代碼優(yōu)于隱式或一些精巧但不清晰的代碼。顯式的塊作用域風(fēng)格非常容易書 寫,并且和其他語言中塊作用域的工作原理一致:

var foo = true;
if (foo) {
  { // <-- 顯式的快
    let bar = foo * 2;
    bar = something( bar );
    console.log( bar );
  }
}
console.log( bar ); // ReferenceError

只要聲明是有效的,在聲明中的任意位置都可以使用 { .. } 括號(hào)來為 let 創(chuàng)建一個(gè)用于綁 定的塊。在這個(gè)例子中,我們?cè)?if 聲明內(nèi)部顯式地創(chuàng)建了一個(gè)塊,如果需要對(duì)其進(jìn)行重 構(gòu),整個(gè)塊都可以被方便地移動(dòng)而不會(huì)對(duì)外部 if 聲明的位置和語義產(chǎn)生任何影響。

垃圾收集

另一個(gè)塊作用域非常有用的原因和閉包及回收內(nèi)存垃圾的回收機(jī)制相關(guān)。

function process(data) {
// 在這里做點(diǎn)有趣的事情
}
var someReallyBigData = { .. };
process( someReallyBigData );
var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt) {
  console.log("button clicked");
}, /*capturingPhase=*/false );

click 函數(shù)的點(diǎn)擊回調(diào)并不需要 someReallyBigData 變量。理論上這意味著當(dāng) process(..) 執(zhí) 行后,在內(nèi)存中占用大量空間的數(shù)據(jù)結(jié)構(gòu)就可以被垃圾回收了。但是,由于 click 函數(shù)形成 了一個(gè)覆蓋整個(gè)作用域的閉包,JavaScript 引擎極有可能依然保存著這個(gè)結(jié)構(gòu)(取決于具體 實(shí)現(xiàn))。

塊作用域可以打消這種顧慮,可以讓引擎清楚地知道沒有必要繼續(xù)保存 someReallyBigData 了:
function process(data) {
// 在這里做點(diǎn)有趣的事情
}
// 在這個(gè)塊中定義的內(nèi)容可以銷毀了!
{
let someReallyBigData = { .. };
process( someReallyBigData );
}
var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt){
console.log("button clicked");
}, /capturingPhase=/false );

為變量顯式聲明塊作用域,并對(duì)變量進(jìn)行本地綁定是非常有用的工具,可以把它添加到你
的代碼工具箱中了。

let循環(huán)

for 循環(huán)頭部的 let 不僅將 i 綁定到了 for 循環(huán)的塊中,事實(shí)上它將其重新綁定到了循環(huán) 的每一個(gè)迭代中,確保使用上一個(gè)循環(huán)迭代結(jié)束時(shí)的值重新進(jìn)行賦值。

每個(gè)迭代進(jìn)行重新綁定的原因非常有趣,我們會(huì)在第 5 章討論閉包時(shí)進(jìn)行說明。

const

除了 let 以外,ES6 還引入了 const,同樣可以用來創(chuàng)建塊作用域變量,但其值是固定的 (常量)。之后任何試圖修改值的操作都會(huì)引起錯(cuò)誤。

小結(jié)

函數(shù)是 JavaScript 中最常見的作用域單元。本質(zhì)上,聲明在一個(gè)函數(shù)內(nèi)部的變量或函數(shù)會(huì)在所處的作用域中“隱藏”起來,這是有意為之的良好軟件的設(shè)計(jì)原則。 但函數(shù)不是唯一的作用域單元。塊作用域指的是變量和函數(shù)不僅可以屬于所處的作用域,也可以屬于某個(gè)代碼塊(通常指 { .. } 內(nèi)部)。

從 ES3 開始,try/catch 結(jié)構(gòu)在 catch 分句中具有塊作用域。

在 ES6 中引入了 let 關(guān)鍵字(var 關(guān)鍵字的表親),用來在任意代碼塊中聲明變量。if (..) { let a = 2; } 會(huì)聲明一個(gè)劫持了 if 的 { .. } 塊的變量,并且將變量添加到這個(gè)塊 中。
有些人認(rèn)為塊作用域不應(yīng)該完全作為函數(shù)作用域的替代方案。兩種功能應(yīng)該同時(shí)存在,開 發(fā)者可以并且也應(yīng)該根據(jù)需要選擇使用何種作用域,創(chuàng)造可讀、可維護(hù)的優(yōu)良代碼。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101464.html

相關(guān)文章

  • 重讀你不知道JS () 一節(jié)四章

    摘要:如果提升改變了代碼執(zhí)行的順序,會(huì)造成非常嚴(yán)重的破壞。聲明本身會(huì)被提升,而包括函數(shù)表達(dá)式的賦值在內(nèi)的賦值操作并不會(huì)提升。要注意避免重復(fù)聲明,特別是當(dāng)普通的聲明和函數(shù)聲明混合在一起的時(shí)候,否則會(huì)引起很多危險(xiǎn)的問題 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 Ja...

    chanjarster 評(píng)論0 收藏0
  • 重讀你不知道JS () 一節(jié)五章

    摘要:詞法作用域的查找規(guī)則是閉包的一部分。因此的確同閉包息息相關(guān),即使本身并不會(huì)真的使用閉包。而上面的創(chuàng)建一個(gè)閉包,本質(zhì)上這是將一個(gè)塊轉(zhuǎn)換成一個(gè)可以被關(guān)閉的作用域。結(jié)合塊級(jí)作用域與閉包模塊這個(gè)模式在中被稱為模塊。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 Jav...

    worldligang 評(píng)論0 收藏0
  • 重讀你不知道JS () 一節(jié)二章

    摘要:詞法作用域定義在詞法階段的作用域由你在寫代碼時(shí)將變量和塊作用域?qū)懺谀膩頉Q定的,因此當(dāng)詞法分析器處理代碼時(shí)會(huì)保持作用域不變。欺騙詞法作用域在詞法分析器處理過后依然可以修改作用域。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 JavaScript 開發(fā)者,如果沒...

    baihe 評(píng)論0 收藏0
  • 重讀你不知道JS () 一節(jié)一章

    摘要:的抽象語法樹中可能會(huì)有一個(gè)叫作的頂級(jí)節(jié)點(diǎn),接下來是一個(gè)叫作它的值是的子節(jié)點(diǎn),以及一個(gè)叫作的子節(jié)點(diǎn)。值得注意的是,是非常重要的異常類型。嚴(yán)格模式下,未聲明的和倆者行為相同,都會(huì)是。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 JavaScript 開發(fā)者,如果...

    lk20150415 評(píng)論0 收藏0
  • Python:Tornado 第三章:HTML5 WebSocket概念及應(yīng)用:一節(jié):WebSoc

    摘要:上一篇文章第二章實(shí)戰(zhàn)演練開發(fā)網(wǎng)站第九節(jié)防止跨站攻擊下一篇文章第三章概念及應(yīng)用第二節(jié)服務(wù)端編程的異步特性使得其非常適合服務(wù)器的高并發(fā)處理,客戶端與服務(wù)器的持久連接應(yīng)用框架就是高并發(fā)的典型應(yīng)用。因?yàn)槭堑臉?biāo)準(zhǔn)協(xié)議,所以不受企業(yè)防火墻的攔截。 上一篇文章:Python:Tornado 第二章:實(shí)戰(zhàn)演練:開發(fā)Tornado網(wǎng)站:第九節(jié):防止跨站攻擊下一篇文章:Python:Tornado 第三章...

    mo0n1andin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<