函数⑥作用域与命名空间
?
盡管 JavaScript 支持一對花括號創(chuàng)建的代碼段,但是并不支持塊級作用域; 而僅僅支持?函數(shù)作用域。
function test() { // 一個作用域for(var i = 0; i < 10; i++) { // 不是一個作用域// count}console.log(i); // 10 }注意:?如果不是在賦值語句中,而是在 return 表達(dá)式或者函數(shù)參數(shù)中,{...}?將會作為代碼段解析, 而不是作為對象的字面語法解析。如果考慮到?自動分號插入,這可能會導(dǎo)致一些不易察覺的錯誤。
譯者注:如果?return?對象的左括號和?return?不在一行上就會出錯。
// 譯者注:下面輸出 undefined function add(a, b) {return a + b; } console.log(add(1, 2));JavaScript 中沒有顯式的命名空間定義,這就意味著所有對象都定義在一個全局共享的命名空間下面。
每次引用一個變量,JavaScript 會向上遍歷整個作用域直到找到這個變量為止。 如果到達(dá)全局作用域但是這個變量仍未找到,則會拋出?ReferenceError異常。
隱式的全局變量
// 腳本 A foo = '42';// 腳本 B var foo = '42'上面兩段腳本效果不同。腳本 A 在全局作用域內(nèi)定義了變量?foo,而腳本 B 在當(dāng)前作用域內(nèi)定義變量?foo。
再次強(qiáng)調(diào),上面的效果完全不同,不使用?var?聲明變量將會導(dǎo)致隱式的全局變量產(chǎn)生。
// 全局作用域 var foo = 42; function test() {// 局部作用域foo = 21; } test(); foo; // 21在函數(shù)?test?內(nèi)不使用?var?關(guān)鍵字聲明?foo?變量將會覆蓋外部的同名變量。 起初這看起來并不是大問題,但是當(dāng)有成千上萬行代碼時,不使用?var?聲明變量將會帶來難以跟蹤的 BUG。
// 全局作用域 var items = [/* 數(shù)組 */]; for(var i = 0; i < 10; i++) {subLoop(); }function subLoop() {// subLoop 函數(shù)作用域for(i = 0; i < 10; i++) { // 沒有使用 var 聲明變量// 干活} }外部循環(huán)在第一次調(diào)用?subLoop?之后就會終止,因?yàn)?subLoop?覆蓋了全局變量?i。 在第二個?for?循環(huán)中使用?var?聲明變量可以避免這種錯誤。 聲明變量時絕對不要遺漏?var?關(guān)鍵字,除非這就是期望的影響外部作用域的行為。
局部變量
JavaScript 中局部變量只可能通過兩種方式聲明,一個是作為函數(shù)參數(shù),另一個是通過?var?關(guān)鍵字聲明。
// 全局變量 var foo = 1; var bar = 2; var i = 2;function test(i) {// 函數(shù) test 內(nèi)的局部作用域i = 5;var foo = 3;bar = 4; } test(10);foo?和?i?是函數(shù)?test?內(nèi)的局部變量,而對?bar?的賦值將會覆蓋全局作用域內(nèi)的同名變量。
變量聲明提升(Hoisting)
JavaScript 會提升變量聲明。這意味著?var?表達(dá)式和?function?聲明都將會被提升到當(dāng)前作用域的頂部。
bar(); var bar = function() {}; var someValue = 42;test(); function test(data) {if (false) {goo = 1;} else {var goo = 2;}for(var i = 0; i < 100; i++) {var e = data[i];} }上面代碼在運(yùn)行之前將會被轉(zhuǎn)化。JavaScript 將會把?var?表達(dá)式和?function聲明提升到當(dāng)前作用域的頂部。
// var 表達(dá)式被移動到這里 var bar, someValue; // 缺省值是 'undefined'// 函數(shù)聲明也會提升 function test(data) {var goo, i, e; // 沒有塊級作用域,這些變量被移動到函數(shù)頂部if (false) {goo = 1;} else {goo = 2;}for(i = 0; i < 100; i++) {e = data[i];} }bar(); // 出錯:TypeError,因?yàn)?bar 依然是 'undefined' someValue = 42; // 賦值語句不會被提升規(guī)則(hoisting)影響 bar = function() {};test();沒有塊級作用域不僅導(dǎo)致?var?表達(dá)式被從循環(huán)內(nèi)移到外部,而且使一些?if?表達(dá)式更難看懂。
在原來代碼中,if?表達(dá)式看起來修改了全局變量?goo,實(shí)際上在提升規(guī)則被應(yīng)用后,卻是在修改局部變量。
如果沒有提升規(guī)則(hoisting)的知識,下面的代碼看起來會拋出異常ReferenceError。
// 檢查 SomeImportantThing 是否已經(jīng)被初始化 if (!SomeImportantThing) {var SomeImportantThing = {}; }實(shí)際上,上面的代碼正常運(yùn)行,因?yàn)?var?表達(dá)式會被提升到全局作用域的頂部。
var SomeImportantThing;// 其它一些代碼,可能會初始化 SomeImportantThing,也可能不會// 檢查是否已經(jīng)被初始化 if (!SomeImportantThing) {SomeImportantThing = {}; }譯者注:在 Nettuts+ 網(wǎng)站有一篇介紹 hoisting 的文章,其中的代碼很有啟發(fā)性。
// 譯者注:來自 Nettuts+ 的一段代碼,生動的闡述了 JavaScript 中變量聲明提升規(guī)則 var myvar = 'my value'; (function() { alert(myvar); // undefined var myvar = 'local value'; })();名稱解析順序
JavaScript 中的所有作用域,包括全局作用域,都有一個特別的名稱?this?指向當(dāng)前對象。
函數(shù)作用域內(nèi)也有默認(rèn)的變量?arguments,其中包含了傳遞到函數(shù)中的參數(shù)。
比如,當(dāng)訪問函數(shù)內(nèi)的?foo?變量時,JavaScript 會按照下面順序查找:
注意:?自定義?arguments?參數(shù)將會阻止原生的?arguments?對象的創(chuàng)建。
命名空間
只有一個全局作用域?qū)е碌某R婂e誤是命名沖突。在 JavaScript中,這可以通過?匿名包裝器?輕松解決。
(function() {// 函數(shù)創(chuàng)建一個命名空間window.foo = function() {// 對外公開的函數(shù),創(chuàng)建了閉包};})(); // 立即執(zhí)行此匿名函數(shù)匿名函數(shù)被認(rèn)為是?表達(dá)式;因此為了可調(diào)用性,它們首先會被執(zhí)行。
( // 小括號內(nèi)的函數(shù)首先被執(zhí)行 function() {} ) // 并且返回函數(shù)對象 () // 調(diào)用上面的執(zhí)行結(jié)果,也就是函數(shù)對象有一些其他的調(diào)用函數(shù)表達(dá)式的方法,比如下面的兩種方式語法不同,但是效果一模一樣。
// 另外兩種方式 +function(){}(); (function(){}());結(jié)論
推薦使用匿名包裝器(譯者注:也就是自執(zhí)行的匿名函數(shù))來創(chuàng)建命名空間。這樣不僅可以防止命名沖突, 而且有利于程序的模塊化。
另外,使用全局變量被認(rèn)為是不好的習(xí)慣。這樣的代碼容易產(chǎn)生錯誤并且維護(hù)成本較高。
轉(zhuǎn)載于:https://www.cnblogs.com/luckyxb/p/6398534.html
總結(jié)
以上是生活随笔為你收集整理的函数⑥作用域与命名空间的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ViewPager外部获取“当前显示的页
- 下一篇: 图片双面打印顺序混乱_为什么双面打印一面