## 2.1 關于this
### 2.1.1 為什么要用this
~~~
function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting = "Hello, I'm " + identify.call(this);
console.log(greeting);
}
var me = {
name: "Kyle"
};
var you = {
name: "Reader"
};
identify.call(me); // KYLE
identify.call(you); // READER
speak.call(me); // Hello, 我是KYLE
speak.call(you); // Hello, 我是 READER
~~~
如果不使用this,那就需要給identify() 和speak() 顯式傳入一個上下文對象。
~~~
function identify(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = "Hello, I'm " + identify(context);
console.log(greeting);
}
identify(you); // READER
speak(me); //hello, 我是KYLE
~~~
### 2.1.2 誤解
**1. 指向自身**
~~~
function foo(num) {
console.log("foo: " + num);
// 記錄foo 被調用的次數
this.count++;
}
foo.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被調用了多少次?
console.log(foo.count); // 0 -- ???
~~~
console.log 語句產生了4 條輸出,證明foo(..) 確實被調用了4 次,但是foo.count 仍然是0。顯然從字面意思指向函數自身來理解this 是錯誤的。
如果要從函數對象內部引用它自身,那只使用this 是不夠的。一般來說需要通過一個指向函數對象的詞法標識符(變量)來引用它。
思考一下下面這兩個函數:
~~~
function foo() {
foo.count = 4; // foo 指向它自身
}
setTimeout( function(){
// 匿名(沒有名字的)函數無法指向自身
}, 10 );
~~~
第一個函數被稱為具名函數,在它內部可以使用foo 來引用自身。但是在第二個例子中,傳入setTimeout(..) 的回調函數沒有名稱標識符(這種函數被稱為匿名函數),因此無法從函數內部引用自身。
另一種方法是強制this 指向foo 函數對象:
~~~
function foo(num) {
console.log("foo: " + num);
// 記錄foo 被調用的次數
// 注意,在當前的調用方式下(參見下方代碼),this 確實指向foo
this.count++;
}
foo.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
// 使用call(..) 可以確保this 指向函數對象foo 本身
foo.call(foo, i);
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被調用了多少次?
console.log( foo.count ); // 4
~~~
**2. this指向函數的詞法作用域**
**this 在任何情況下都不指向函數的詞法作用域。**在JavaScript 內部,作用域確實和對象類似,可見的標識符都是它的屬性。但是作用域“對象”無法通過JavaScript代碼訪問,它存在于JavaScript 引擎內部。
~~~
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); // ReferenceError: a is not defined
~~~
以上代碼試圖(但是沒有成功)跨越邊界,使用this 來隱式引用函數的詞法作用域。
* 首先,這段代碼試圖通過this.bar() 來引用bar() 函數。這是絕對不可能成功的。(調用bar() 最自然的方法是省略前面的this,直接使用詞法引用標識符。)
* 此外,還試圖使用this 聯通foo() 和bar() 的詞法作用域,從而讓bar() 可以訪問foo() 作用域里的變量a。這是不可能實現的,你不能使用this 來引用一個詞法作用域內部的東西。
**3. this到底是什么**
this 是在運行時進行綁定的,并不是在編寫時綁定,它的上下文取決于函數調用時的各種條件。**this 的綁定和函數聲明的位置沒有任何關系,只取決于函數的調用方式。**
當一個函數被調用時,會創建一個活動記錄(有時候也稱為執行上下文)。這個記錄會包含函數在哪里被調用(調用棧)、函數的調用方法、傳入的參數等信息。this 就是記錄的其中一個屬性,會在函數執行的過程中用到。
- 前言
- 第一章 JavaScript簡介
- 第三章 基本概念
- 3.1-3.3 語法、關鍵字和變量
- 3.4 數據類型
- 3.5-3.6 操作符、流控制語句(暫略)
- 3.7函數
- 第四章 變量的值、作用域與內存問題
- 第五章 引用類型
- 5.1 Object類型
- 5.2 Array類型
- 5.3 Date類型
- 5.4 基本包裝類型
- 5.5 單體內置對象
- 第六章 面向對象的程序設計
- 6.1 理解對象
- 6.2 創建對象
- 6.3 繼承
- 第七章 函數
- 7.1 函數概述
- 7.2 閉包
- 7.3 私有變量
- 第八章 BOM
- 8.1 window對象
- 8.2 location對象
- 8.3 navigator、screen與history對象
- 第九章 DOM
- 9.1 節點層次
- 9.2 DOM操作技術
- 9.3 DOM擴展
- 9.4 DOM2和DOM3
- 第十章 事件
- 10.1 事件流
- 10.2 事件處理程序
- 10.3 事件對象
- 10.4 事件類型
- 第十一章 JSON
- 11.1-11.2 語法與序列化選項
- 第十二章 正則表達式
- 12.1 創建正則表達式
- 12.2-12.3 模式匹配與RegExp對象
- 第十三章 Ajax
- 13.1 XMLHttpRequest對象
- 你不知道的JavaScript
- 一、作用域與閉包
- 1.1 作用域
- 1.2 詞法作用域
- 1.3 函數作用域與塊作用域
- 1.4 提升
- 1.5 作用域閉包
- 二、this與對象原型
- 2.1 關于this
- 2.2 全面解析this
- 2.3 對象
- 2.4 混合對象“類”
- 2.5 原型
- 2.6 行為委托
- 三、類型與語法
- 3.1 類型
- 3.2 值
- 3.3 原生函數