文章目錄
- 結論(In conclusion)
- 命名函數的賦值運算式(Named function expression)
- 全域範圍內(The global scope)
- 函數調用(Calling a function)
- 方法調用(Calling a method)
- 調用建構函式(Calling a constructor)
- 常見誤解(Common pitfalls)
- 方法的賦值運算式(Assigning methods)
- JavaScript Garden - 原文
- JavaScript Garden - 中文翻譯
hasOwnProperty
為了判斷一個對象是否包含自訂屬性而不是原型鏈上的屬性, 我們需要使用繼承自 Object.prototype
的 hasOwnProperty
方法。
注意: 通過判斷一個屬性是否 undefined
是不夠的。 因為一個屬性可能確實存在,只不過它的值被設定為 undefined
。
hasOwnProperty
是JavaScript中唯一一個處理屬性但是不需要尋找原型鏈的方法。
// 修改Object.prototypeObject.prototype.bar = 1; var foo = {goo: undefined};foo.bar; // 1'bar' in foo; // truefoo.hasOwnProperty('bar'); // falsefoo.hasOwnProperty('goo'); // true
只有 hasOwnProperty
可以給出正確和期望的結果,這在遍曆對象的屬性時會很有用。 沒有其它方法可以用來排除原型鏈上的屬性,而不是定義在對象自身上的屬性。
hasOwnProperty
作為屬性(
hasOwnProperty
as a property)
JavaScript 不會保護 hasOwnProperty
被非法佔用,因此如果一個對象碰巧存在這個屬性, 就需要使用外部的 hasOwnProperty
函數來擷取正確的結果。
var foo = { hasOwnProperty: function() { return false; }, bar: 'Here be dragons'};foo.hasOwnProperty('bar'); // 總是返回 false// 使用其它對象的 hasOwnProperty,並將其上下為設定為foo{}.hasOwnProperty.call(foo, 'bar'); // true
結論(In conclusion)
當檢查對象上某個屬性是否存在時,hasOwnProperty
是唯一可用的方法。 同時在使用 for in
loop 遍曆對象時,推薦總是使用 hasOwnProperty
方法, 這將會避免原型對象擴充帶來的幹擾。
函數(Functions)
函數是JavaScript中的一等對象,這意味著可以把函數像其它值一樣傳遞。 一個常見的用法是把匿名函數作為回呼函數傳遞對非同步函數中。
函式宣告(The
function
declaration)
function foo() {}
上面的方法會在執行前被 解析(hoisted),因此它存在於當前內容相關的任意一個地方, 即使在函數定義體的上面被調用也是對的。
foo(); // 正常運行,因為foo在代碼運行前已經被建立function foo() {}
函數賦值運算式(The
function
expression)
var foo = function() {};
這個例子把一個匿名的函數賦值給變數 foo
。
foo; // 'undefined'foo(); // 出錯:TypeErrorvar foo = function() {};
由於 var
定義了一個聲明語句,對變數 foo
的解析是在代碼運行之前,因此 foo
變數在代碼運行時已經被定義過了。
但是由於指派陳述式只在運行時執行,因此在相應代碼執行之前, foo
的值預設為 undefined。
命名函數的賦值運算式(Named function expression)
另外一個特殊的情況是將命名函數賦值給一個變數。
var foo = function bar() { bar(); // 正常運行}bar(); // 出錯:ReferenceError
bar
函式宣告外是不可見的,這是因為我們已經把函數賦值給了 foo
; 然而在 bar
內部依然可見。這是由於 JavaScript 的 命名處理 所致, 函數名在函數內總是可見的。
this 的工作原理(How
this
works)
JavaScript 有一套完全不同於其它語言的對 this
的處理機制。 在五種不同的情況下 ,this
指向的各不相同。
全域範圍內(The global scope)
this;
當在全部範圍內使用 this
,它將會指向全域對象。 (譯者註:瀏覽器中啟動並執行JavaScript指令碼,這個全域對象是 window)
函數調用(Calling a function)
foo();
這裡 this
也會指向全域對象。
ES5 注意: 在strict 模式下(strict mode),不存在全域變數。 這種情況下 this
將會是 undefined
。 (譯者註:ES5指的是ECMAScript 5,是2009-12發布的最新的 JavaScript 版本。)
方法調用(Calling a method)
test.foo();
這個例子中,this
指向 test
對象。
調用建構函式(Calling a constructor)
new foo();
如果函數傾向於和 new
關鍵詞一塊使用,則我們稱這個函數是 建構函式。 在函數內部,this
指向新建立的對象。
顯式的設定
this
(Explicit setting of
this
)
function foo(a, b, c) {}var bar = {};foo.apply(bar, [1, 2, 3]); // 數組將會被擴充,如下所示foo.call(bar, 1, 2, 3); // 傳遞到foo的參數是:a = 1, b = 2, c = 3
當使用 Function.prototype
上的 call
或者 apply
方法時,函數內的 this
將會被 顯式設定為函數調用的第一個參數。
因此函數調用的規則在上例中已經不適用了,在foo
函數內 this
被設定成了 bar
。
注意: 在對象的字面聲明文法中,this
不能用來指向對象本身。 因此 var obj = {me: this}
中的 me
不會指向 obj
,因為 this
只可能出現在上述的五種情況中。 (譯者註:這個例子中,如果是在瀏覽器中運行,obj.me等於window對象。)
常見誤解(Common pitfalls)
儘管大部分的情況都說的過去,不過第一個規則(譯者註:這裡指的應該是第二個規則,也就是直接調用函數時,this
指向全域對象) 被認為是JavaScript語言另一個錯誤設計的地方,因為它從來就沒有實際的用途。
Foo.method = function() { function test() { // this 將會被設定為全域對象(譯者註:瀏覽器環境中也就是 window 對象) } test();}
一個常見的誤解是 test
中的 this
將會指向 Foo
對象,實際上不是這樣子的。
為了在 test
中擷取對 Foo
對象的引用,我們需要在 method
函數內部建立一個局部變數指向 Foo
對象。
Foo.method = function() { var that = this; function test() { // 使用 that 來指向 Foo 對象 } test();}
that
只是我們隨意起的名字,不過這個名字被廣泛的用來指向外部的 this
對象。 在 closures 一節,我們可以看到 that
可以作為參數傳遞。
方法的賦值運算式(Assigning methods)
另一個看起來奇怪的地方是函數別名,也就是將一個方法賦值給一個變數。
var test = someObject.methodTest;test();
上例中,test
就像一個普通的函數被調用;因此,函數內的 this
將不再被指向到 someObject
對象。
雖然 this
的晚綁定特性似乎並不友好,但是這確實基於原型繼承賴以生存的土壤。
function Foo() {}Foo.prototype.method = function() {};function Bar() {}Bar.prototype = Foo.prototype;new Bar().method();
當 method
被調用時,this
將會指向 Bar
的執行個體對象。
本中文翻譯由三上石上原創,部落格園首發,轉載請註明出處。