看 TODOMVC 源代碼的時候辈讶,看到如下一段代碼:
var app = app || {};
https://github.com/tastejs/todomvc/blob/master/examples/backbone/js/models/todo.js#L2
很多次看到過類似的代碼,也大概明白這是怎么回事,可是底瓣,為啥要這么寫斯议,這么寫是怎么起作用的呢芒划?
在 StackOverflow 上,也有帶著類似困惑的小伙伴問了類似的問題哲思,這里:
http://stackoverflow.com/questions/6439579/what-does-var-foo-foo-assign-a-variable-or-an-empty-object-to-that-va
“標準答案”是這么解釋的:
Your guess as to the intent of
|| {}
is pretty close.This particular pattern when seen at the top of files is used to create a namespace, i.e. a named object under which functions and variables can be created without unduly polluting the global object.
The reason why it's used is so that if you have two (or more) files:
var MY_NAMESPACE = MY_NAMESPACE || {}; MY_NAMESPACE.func1 = {}
and
var MY_NAMESPACE = MY_NAMESPACE || {}; MY_NAMESPACE.func2 = {}
both of which share the same namespace it then doesn't matter in which order the two files are loaded, you still get
func1
andfunc2
correctly defined within theMY_NAMESPACE
object correctly.The first file loaded will create the initial
MY_NAMESPACE
object, and any subsequently loaded file willaugment the object.Usefully, this also allows asynchronous loading of scripts that share the same namespace which can improve page loading times. If the <script> tags have the defer attribute set you can't know in which order they'll be interpreted, so as described above this fixes that problem too.
不過這個答案給出的是為啥要這么用,至于這么寫起作用的原理沒有介紹很多吩案。
單純看一處代碼:
var app = app || {};
這里涉及到變量的聲明和賦值棚赔。而變量的聲明涉及到“變量聲明提前”的問題,比如在一個函數(shù)中徘郭,無論變量是不是在函數(shù)的最開始聲明的靠益,變量都可以使用,例如:
function foo() {
x = 'ok';
console.log(x);
var x;
}
當然残揉,如果沒有最后的 var x;
聲明語句胧后,變量 x
就成了全局變量了。
并且抱环,在一個作用域下壳快,多次聲明同一變量纸巷,由于“變量聲明提前”的緣故,其實仍然是同一個變量眶痰。
這一部分的具體介紹見:《JavaScript 權威指南》(第6版) 5.3.1 var瘤旨。
回過頭來琢磨最開始的問題,之所以 var app = app || {}
能夠有效竖伯,應該是由于這些在不同文件中的語句存哲,最終是在同一作用域下執(zhí)行的。最終在該作用域(也就是全局作用域)下七婴,只有一個app
變量祟偷。而||
的作用,就是在app
變量已有值的情況下本姥,直接返回已有的值肩袍,沒有值的情況下返回右側的空對象,從而確保app
始終是非空的對象婚惫。
關于||
的這種用法的具體介紹氛赐,請看:《JavaScript 權威指南》(第6版)4.10.2 邏輯或(||)。
那么先舷,如果這些語句不在同一作用域下執(zhí)行的話艰管,會怎樣呢?例如:
var app = app || {};
app.foo = 'foo';
(function () {
var app = app || {};
app.bar = 'bar';
})()
試驗一下蒋川,就會發(fā)現(xiàn)牲芋,上述代碼執(zhí)行完之后,app 對象還是沒有屬性 bar
的捺球。
為什么呢缸浦?
簡單來說,因為函數(shù)內(nèi)的 app
是個內(nèi)部變量氮兵,與外部的 app
互不影響裂逐。
再把上面的例子改寫下:
var app = app || {};
app.foo = 'foo';
(function () {
app.bar = 'bar';
var app = app || {};
})()
這樣的話又如何呢?這次是不是代碼執(zhí)行后外部的 app
有了屬性 bar
了呢泣栈?
執(zhí)行一下的話會發(fā)現(xiàn)卜高,居然報錯了!
Uncaught TypeError: Cannot set property 'bar' of undefined
仔細琢磨下....其實還是“變量聲明提前”在搞鬼南片。函數(shù)內(nèi)部的變量 app
的聲明提前了掺涛,但是賦值語句不會提前。沒有賦值的變量的值是什么呢疼进?是 undefined
的薪缆。這里的錯誤說清楚了吧,因為此時的 app
是函數(shù)內(nèi)部的私有變量颠悬,還沒有賦值矮燎。為啥不是外面的 app
呢定血,因為函數(shù)內(nèi)部聲明了同名的 app
,而通過“變量聲明提前”诞外,函數(shù)內(nèi)的 app
都是指向內(nèi)部的變量的澜沟。
小結
其實像這里分析的代碼,在日常工作中會看到很多峡谊,大致是怎么回事是清楚的茫虽,可是真的要去詳細解釋為啥這么做,這么做又是為啥會起作用既们,可能就懵了濒析。
編程這件事好像特別看重細節(jié),畢竟無論多么 NB 的應用啥纸,也都是一行行代碼構建起來的号杏。細節(jié)上不清楚,很可能會出現(xiàn)一些習以為常到反而看不出來的小 bug斯棒,這樣的東西多了盾致,問題也就大了。