摘要:盡可能的使用局部變量,少用全局變量。正確的實(shí)現(xiàn)就是在函數(shù)體內(nèi)部使用將聲明成局部變量。在新特性中,引入了塊級(jí)作用域這個(gè)概念,因此還可以使用,來聲明局部變量。它們共享外部變量,并且閉包還可以更新的值。
變量作用域
作用域,對(duì)于JavaScript語言來說無處不在,變量作用域,函數(shù)作用域(運(yùn)行時(shí)上下文和定義時(shí)上下文),作用域污染等等都跟作用域息息相關(guān),掌握J(rèn)avaScript作用于規(guī)則,可以很好地避免一些極端的情況。
盡量少用全局對(duì)象在JavaScript語言下定義一個(gè)全局對(duì)象或者變量是最容易不過的了,這種全局變量最容易聲明并且使用,因?yàn)樗鼙徽麄€(gè)程序所訪問到,但是經(jīng)驗(yàn)豐富的程序員應(yīng)該盡量避免使用全局變量,它有如下的缺點(diǎn):
全局變量會(huì)污染共享的公共命名空間,可能會(huì)導(dǎo)致意外的命名沖突
全局變量不利于模塊化,它會(huì)導(dǎo)致獨(dú)立組件之間的不必要耦合
但是,也不是說一定禁止使用全局變量,在一些情況下全局變量的使用是不可避免的,例如,它是各個(gè)獨(dú)立組件之間進(jìn)行交互的唯一途徑。
盡可能的使用局部變量,少用全局變量。
局部變量:IIFE,let,cosnt等。
this.foo; // undefined window.foo; // undefined var foo = "luffy";//聲明一個(gè)全局變量,會(huì)自動(dòng)綁定到window對(duì)象下 this.foo; // "luffy" window.foo; // "luffy"
可以使用全局對(duì)象來判斷程序是否在瀏覽器環(huán)境下可以運(yùn)行
是否支持地理位置
if(navigator.geolocation){ //user geolocation }
是否支持HTML5
if (window.applicationCache) { alert("你的瀏覽器支持HTML5"); } else { alert("你的瀏覽器不支持HTML5"); }
等等很多種新特性的檢測(cè)。(需要時(shí)請(qǐng)問BD)
始終聲明局部變量在JavaScript中,如果不使用var關(guān)鍵字進(jìn)行變量聲明,該變量會(huì)被隱式轉(zhuǎn)換成全局變量,因此會(huì)造成不必要的全局空間污染。
function swap(a,b) { temp = a; // temp變成了global a = b; b = temp; }
這段程序沒有使用var來聲明temp變量,最終導(dǎo)致以外的創(chuàng)建了一個(gè)從全局的變量temp,雖然代碼執(zhí)行起來也沒有錯(cuò)誤。正確的實(shí)現(xiàn)就是在函數(shù)體內(nèi)部使用var temp;將temp聲明成局部變量。
避免使用with語句在ES6新特性中,引入了塊級(jí)作用域這個(gè)概念,因此還可以使用let,const來聲明局部變量。
with語句作用是讓代碼運(yùn)行在特定對(duì)象作用域內(nèi)。with語句是JavaScript最令人詬病的特性,不可靠且效率低下。
熟練掌握閉包總而言之一句話,避免使用with語句即可
努力掌握J(rèn)avaScript閉包這一概念,對(duì)于前端程序員來說會(huì)有非常大的幫助。理解閉包不用死背概念,理解三個(gè)基本事實(shí)就可以。
JavaScript閉包允許你引用在當(dāng)前函數(shù)以外定義的變量function outerFoo() { var outVar = "外部變量"; return function() { console.log("我在函數(shù)體內(nèi)部,訪問到了:" + outVar); } } var foo = outerFoo(); foo(); //我在函數(shù)體內(nèi)部,訪問到了:外部變量
上面這個(gè)例子,函數(shù)內(nèi)部返回一個(gè)函數(shù),對(duì)于foo來說,可以訪問到自身外部定義的非全局變量(outVar是outerFoo函數(shù)體內(nèi)的局部變量)。
即使函數(shù)已經(jīng)返回,當(dāng)前函數(shù)仍然可以引用在外部函數(shù)所定義的變量仍然是上面那個(gè)例子,外部函數(shù)已經(jīng)結(jié)束執(zhí)行過程,返回值賦值給了foo變量(結(jié)果是一個(gè)函數(shù)),現(xiàn)在foo是window作用域下的一個(gè)函數(shù),如圖所示:
可以看到,window下的foo函數(shù)正確的訪問到了outFoo函數(shù)體內(nèi)部的局部變量。
原因:JavaScript的函數(shù)值包含了比調(diào)用它們的時(shí)候執(zhí)行的那部分代碼還要多的信息,也就是說它們還在內(nèi)部存儲(chǔ)了它們可能會(huì)引用的定義在其封閉作用域內(nèi)的變量,這種在其所涵蓋的作用域內(nèi)部跟蹤變量的函數(shù)叫做閉包。
上述的foo函數(shù)就是一個(gè)閉包,其代碼本身引用了外部變量outVar,每一次foo函數(shù)被調(diào)用,都會(huì)引用到這個(gè)outVar變量。
對(duì)于閉包,有一個(gè)很高的評(píng)價(jià):閉包是JavaScript最優(yōu)雅最有表現(xiàn)力的特性之一
function box() { var val = undefined; //給變量賦值undefined比不給變量賦值要優(yōu)秀 return { set: function(newVal) { val = newVal; }, get: function() { return val; }, type: function() { return typeof val } } } var b = box(); b.type();//"undefined" b.set(17.2); b.get();//17.2 b.type();//"number"
上述例子產(chǎn)生了一個(gè)包含三個(gè)閉包的對(duì)象,這三個(gè)閉包分別對(duì)應(yīng)函數(shù)返回對(duì)象的set、get、type屬性。它們共享外部變量val,并且set閉包還可以更新val的值。
理解變量聲明提升實(shí)際上,閉包存儲(chǔ)的是外部變量的引用,而不是它們真實(shí)值的副本.所以閉包是可以更新改變外部變量的值的。
在ES6,JavaScript開始引入塊級(jí)作用域,使用塊級(jí)作用域聲明的變量只有在包含他們的封閉語句或代碼塊{}內(nèi)部可以使用。但是,在ES6之前,是不存在塊級(jí)作用域的。var聲明的變量會(huì)被綁定到離它聲明最近的那個(gè)作用域上,如果找不到就綁定到最外層window上。
function isWinner(player, others) { var higest = 0; for(var i = 0, n = others.length; i < n; i++){ var player = others[i]; //此處重復(fù)聲明了一個(gè)player變量 if(player.score > higest) { higest = player.score; } } return player.score > higest; }
上述函數(shù)目的是判斷player是否是最高分,但是,在函數(shù)內(nèi)部重復(fù)聲明了player變量,因此,每一次循環(huán)都修改了函數(shù)體的傳入值player變量本身,最終結(jié)果肯定不是預(yù)期的。
使用立即調(diào)用的函數(shù)表達(dá)式來創(chuàng)建局部作用域在ES5版本,JavaScript的變量作用域概念存在兩種,函數(shù)級(jí)作用域和全局作用域。ES6增加了塊級(jí)作用域,使用let、const可以聲明塊級(jí)作用域變量。
立即執(zhí)行的函數(shù)表達(dá)式(IIFE),是前端面試過程基本都會(huì)問到的問題。下面使用一個(gè)老生常談的面試題來講解它:
window.onload = function() { var result = []; for (var i = 0; i < 5; i++) { result[i] = function() { console.log(i); } } console.log(result[1]());// 5 }
大家應(yīng)該都知道,最后的執(zhí)行結(jié)果應(yīng)該是5,并且,result數(shù)組內(nèi)部存的值都是5。
造成結(jié)果的原因可以用上面閉包的基本事實(shí)來解釋,內(nèi)部函數(shù)保存的變量其實(shí)是外部變量的引用,也就是說,result的每一個(gè)元素內(nèi)部所引用的i值會(huì)隨著for循環(huán)變化而變化,當(dāng)我們調(diào)用result[1]();這條語句的時(shí)候,i值已經(jīng)變成了5,所以輸出是5。
解決辦法,就是使用立即執(zhí)行函數(shù)來創(chuàng)建一個(gè)局部作用域來解決。
window.onload = function() { var result = []; for (var i = 0; i < 5; i++) { (function(j) { result[j] = function() { console.log(j); } })(i) } console.log(result[1]());// 1 }
上面使用立即執(zhí)行函數(shù)得到了正確的結(jié)果,原因就是它創(chuàng)建了一段塊級(jí)作用域,立即函數(shù)內(nèi)部將外部變量i的值當(dāng)做參數(shù)穿入內(nèi)部也就是參數(shù)j,之后內(nèi)部使用的一直都是j這個(gè)局部變量,所以就得到了正確的運(yùn)行結(jié)果。
當(dāng)心命名函數(shù)表達(dá)式笨拙的作用域上面IIFE是通過創(chuàng)建一個(gè)塊級(jí)作用域的方式解決的這個(gè)問題,其實(shí)在ES6中,非常簡(jiǎn)便的就可以解決,那就是使用let關(guān)鍵字定義i值,因?yàn)閘et定義的變量就是塊級(jí)作用域變量。for(let i = 0; i < 5l i++)即可得到預(yù)期答案。
在使用IIFE的時(shí)候要注意幾件事:首先,代碼塊不能包含任何跳出塊的break語句和continue語句,因?yàn)樵诤瘮?shù)外部使用break和continue語句是不合法的。其次,如果代碼塊引用了this或特別的arguments變量,IIFE會(huì)改變它們的語義。
這條規(guī)則對(duì)于現(xiàn)在的環(huán)境和正確使用JavaScript語法規(guī)則編程的程序員來說不太使用,簡(jiǎn)單來說就是下面這種情況。
//推薦使用定義函數(shù)的方式 //第一種:函數(shù)聲明 function double(x) { return x*2; } //第二種:函數(shù)表達(dá)式 var foo = function(){ return x*2; } //不合理的定義函數(shù)的方式 var f = function three(x) { return x*3; }
當(dāng)心局部塊函數(shù)聲明笨拙的作用域千萬不要使用兩種混搭的這種形式,雖然在目前的眾多瀏覽器中都是合法的,但是在低版本終會(huì)存在問題,并且,變量three的作用域只是在自身函數(shù)體內(nèi)部,在其他地方都是不能被引用的,而變量f可以被外部引用 。
function foo() { return "global"; } function test(x) { function foo() { return "local"; } var result = []; if (x) { result.push(foo()); } result.push(foo()); return result; } test(true); // ["local","local"] test(false); // ["local"]
這種嵌套函數(shù)聲明的方式也容易出現(xiàn)問題,全局作用域下聲明了函數(shù)foo,在test函數(shù)體內(nèi)部又聲明了一個(gè)foo函數(shù),最后輸出的結(jié)果是函數(shù)體內(nèi)部foo的執(zhí)行結(jié)果。對(duì)于上面這種情況,應(yīng)該在test函數(shù)體內(nèi)部使用函數(shù)表達(dá)式的形式聲明一個(gè)新變量。
function foo() { return "global"; } function test(x) { var g = foo; var result = []; if (x) { g = function() { return "local"; } result.push(g()); } result.push(g()); return result; } test(true); // ["local","local"] test(false); // ["global"]避免使用eval創(chuàng)建局部變量 間接調(diào)用eval函數(shù)優(yōu)于直接調(diào)用
關(guān)于eval函數(shù)理解不是很深,沒太理解這兩節(jié)的內(nèi)容,等有時(shí)間從頭翻翻紅寶書回來再看看。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90067.html
摘要:如果為假值,不傳或者傳入,函數(shù)都會(huì)返回但是,傳入這個(gè)值是完全有可能的,所以這種判斷形勢(shì)是不正確的或者使用來判斷也可以原始類型優(yōu)于封裝類型對(duì)象擁有六個(gè)原始值基本類型布爾值,數(shù)字,字符串,,和對(duì)象。 作為一個(gè)前端新人,多讀書讀好書,夯實(shí)基礎(chǔ)是十分重要的,正如蓋樓房一樣,底層穩(wěn)固了,才能越壘越高。從開始學(xué)習(xí)到現(xiàn)在,基礎(chǔ)的讀了紅寶書《JavaScript高級(jí)程序設(shè)計(jì)》,犀牛書《JavaScri...
摘要:對(duì)象字面量定義一個(gè)空對(duì)象這里的空指的是其自身屬性為空,對(duì)象繼承了的屬性和方法添加屬性方法完全刪除屬性方法自定義構(gòu)造函數(shù)用操作符調(diào)用構(gòu)造函數(shù)時(shí),函數(shù)內(nèi)部會(huì)發(fā)發(fā)生以下情況創(chuàng)建一個(gè)新對(duì)象,并且引用了該對(duì)象并繼承了該函數(shù)的原型屬性和方法被加入到的引 對(duì)象字面量 //定義一個(gè)空對(duì)象,這里的空指的是其自身屬性為空,dog對(duì)象繼承了Object.prototype的屬性和方法 var dog={} ...
摘要:對(duì)象被傳遞到從句中被捕獲。一些語言提供了尾遞歸優(yōu)化。這意味著如果一個(gè)函數(shù)返回自身遞歸調(diào)用的結(jié)果,那么調(diào)用的過程會(huì)被替換為一個(gè)循環(huán),可以顯著提高速度。構(gòu)建一個(gè)帶尾遞歸的函數(shù)。語言精粹讀書筆記函數(shù) 第四章 函數(shù) Functions (二) 參數(shù) arguments arguments數(shù)組: 函數(shù)可以通過此參數(shù)訪問所有它被調(diào)用時(shí)傳遞給它的參數(shù)列表,包括哪些沒有被分配給函數(shù)聲明時(shí)定義的形式參數(shù)...
閱讀 3476·2023-04-25 18:52
閱讀 2488·2021-11-22 15:31
閱讀 1228·2021-10-22 09:54
閱讀 3017·2021-09-29 09:42
閱讀 612·2021-09-26 09:55
閱讀 915·2021-09-13 10:28
閱讀 1111·2019-08-30 15:56
閱讀 2111·2019-08-30 15:55