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