作用域
沒有塊作用域
因為你可能已經注意到上一個觀點,javascript中沒有塊作用域的概念,隻有函數作用域。可以試試下面的代碼:
for(var i=0; i<10; i++) {
console.log(i);
}
var i;
console.log(i); // 10
當i被定義在for循環中,退出循環後它人被保留在這個作用域内,是以最後調用console.log輸出了10。這裡有一個JSLint警告來讓你避免這個問題:強制将所有的變量定義在函數的開頭。 我們有可能通過寫一個立即執行的function來建立一個作用域:
(function (){
for(var i=0; i<10; i++) {
console.log(i);
}
}());
var i;
console.log(i); // undefined
當你在内部函數之前聲明一個變量,然後在函數裡重聲明這個變量,那将會出現一個奇怪的問題,示例代碼如下:
var x = 3;
(function (){
console.log(x + 2); // 5
x = 0; //No var declaration
}());
但是,如果你在内部函數中重新聲明x變量,會出現一個奇怪的問題:
var x = 3;
(function (){
console.log(x + 2); //NaN - x is not defined
var x = 0; //var declaration
}());
這是因為在函數中x變量被重新定義了,這說明了翻譯程式将var表達式移動到了函數頂部了,最終就變成這樣執行了:
var x = 3;
(function (){
var x;
console.log(x + 2); //NaN - x is not defined
x = 0;
}());
這個實在是太有意義了!
全局變量
Javascript 有一個全局作用域,在為你的代碼建立命名空間時一定要小心謹慎。全局變量會給你的應用增加一些性能問題,因為當你通路它們時,運作時不得不通過每一個作用域來建立知道找到它們為止。他們會因你的有意或者無意而被通路或者修改,這将導緻另外一個更加嚴重的問題 - 跨站點腳本***。如果一個不懷好意的家夥在你的頁面上找出了如何執行那些代碼的方法,那麼他們就可以通過修改全局變量非常容易地擾亂你的應用。缺乏經驗的開發者在無意中會不斷的将變量添加到全局作用域中,通過本文,将會告訴大家這樣會發生什麼意外的事情。
我曾經看到過下面的代碼,它将嘗試聲明兩個值相等的局部變量:
var a = b = 3;
這樣非常正确的得到了a=3和b=3,但是a在局部作用域中而b在全局作用域中,”b=3“将會被先執行,全局操作的結果,3,再被配置設定給局部變量a。
下面的代碼聲明了兩個值為3的變量,這樣能達到預期的效果:
var a = 3,
b = a;
”this“和内部函數
”this“關鍵字通常指目前正在執行的函數所在的對象,然而,如果函數并沒有在對象上被調用,比如在内部函數中,”this“就被設定為全局對象(window),如下代碼:
var obj = {
doSomething: function () {
var a = "bob";
console.log(this); // 目前執行的對象
(function () {
console.log(this); // window - "this" is reset
console.log(a); // "bob" - still in scope
}());
}
};
obj.doSomething();
雜項
資料不存在:”null“和”undefined“
有兩種對象狀态來表明資料不存在:null和undefined。這會讓那些從其他程式設計語言比如C#轉過來的程式員變得相當混亂。也許你會期望下面的代碼傳回true:
var a;
a === null; //false
a === undefined; //true
“a” 實際上是undefined的(盡管你用雙等号==來與null比較會得出true的結果,但這隻是表面上看起來正确的另一個錯誤)。
如果你想檢查一個變量是否真的存在值,那你不能用雙等号==去判斷,要用下面的方法:
if(a !== null && a !== undefined) {
...
}
哈,你也許會說,既然null和undefined都是false,那麼你可以這樣去做:
if(a) {
...
}
當然,0是false,空字元串也是。那麼如果這其中一個是a的正确的值的話,你就要用前者了。那種比較短小的比較方式,适合于比較objects, arrays, 和booleans類型。
重定義undefined
非常正确,你可以重定義undefined,因為它不是一個保留字:
undefined = "surprise!";
但是,你要通過給undefined變量配置設定一個值或者使用”void“操作符來取回值(否則這是相當沒用的)。
undefined = void 0;
這就是為什麼jquery腳本庫的第一行要這樣寫了:
(function ( window, undefined ) {
... // jQuery library!
}(window));
這個函數被調用時是傳入一個參數的,同時確定了第二個參數”undefined“實際上是undefined的。
順便說一下,你不能重定義null - 但是你可以重定義NaN,Infinity和帶構造函數的内置類型。可以這樣嘗試一下:
Array = function (){ alert("hello!"); }
var a = new Array();
當然,你可以在任何地方用文字文法聲明Array。
可選的分号
Javascript代碼中分号是可選的,是以初學者寫代碼就簡單多了。但是很不幸的是如果忽略了分号并不會給任何人帶來友善。結果是當解釋器遇到錯誤時,必須追溯并嘗試去猜測因為哪些分号漏寫導緻的問題。
這裡有一個經典的例子:
return
{
a: "hello"
};
上面的代碼并不會傳回一個對象,而是傳回了undefined - 但是也沒有錯誤抛出。其實是因為分号自動加到了return語句後面,其他的代碼都是非常正确的,但是就是什麼都不執行,這就證明了在 javascript中,左花括号應該緊跟這一行而不該換行,這不隻是一個程式設計風格的問題。下面的代碼才會正确傳回一個屬性為a的對象:
return {
a: "hello"
};
NaN
NaN的類型是...Number
typeof NaN === "number" //true
另外NaN和任何東西比較都是false:
NaN === NaN; // false
因為NaN之間是不能比較的,唯一判斷一個數字是否為NaN的方法是調用isNaN方法。
從另一個方面可以說明,我們也可以用函數isFinite,當其中一個操作數為NaN或者InFinity時傳回false。
arguments對象
在一個函數中,我們可以引用arguments對象來周遊傳入的參數清單,第一個比較怪異的地方是這個對象并不是Array,而是一個類似 Array的對象(有一個length屬性,其值在0-length-1之間)。為了将其轉換成array,我們可以array的splice函數來建立 其對應的array數組:
(function(){
console.log(arguments instanceof Array); // false
var argsArray = Array.prototype.slice.call(arguments);
console.log(argsArray instanceof Array); // true
}());
第二個比較怪異的地方是當一個函數的簽名中有顯式arguments參數時,它們是可以被重新配置設定的并且arguments對象也會被改變。這就表明了arguments對象指向了變量本身。你不能利用arguments對象來給出它們的初始值:
(function(a){
alert(arguments[0]); //1
a = 2;
alert(arguments[0]); //2
}(1));
轉載于:https://blog.51cto.com/291856/1111103