javaScript面向對象編程之this關鍵字
目錄
1. 含義
簡而言之,this
就是屬性或方法“當前”所在的對象。
比如:
var person = {
name: "jidi",
print: function(){
return "name:"+this.name;
}
}
person.print(); // "name:jidi"
在上面這個例子中,this.name
中的this
表示name屬性所在的對象。因爲this.name
在print()
方法中調用,而priint()
方法所在當前對象爲person
。所以this
指向person
對象,this.name
即爲person.name
。
對象的屬性賦值給另一個對象後,屬性所在當前對象跟着改變,this
的指向也會改變。
var person = {
name: "jidi",
print: function(){
return "name:"+this.name;
}
}
// 創建一個新對象
var lady = {
name: 'lady'
};
// 將person.print屬性賦值給lady
lady.print = person.print;
person.print(); // "name:jidi"
lady.print(); // "name:lady"
以一種更清晰的方式展示this
的指向變化。
// 定義一個全局函數print
function printName(){
return "name:"+this.name;
}
// 分別創建兩個對象
var o1 = {
name: "Object1",
print: printName
};
var o2 = {
name: "Object2",
print: printName
};
o1.print(); // "name:Object1"
o2.print(); // "name:Object2"
上面例子中,printName()
函數內部使用了this
,隨着printName()
函數所在的對象不同,this
的指向也不同。
只要函數被賦值給另一個變量,this
的指向就會改變。
var p = {
name: "p",
print: function(){
return this.name;
}
};
var name = "我是全局變量";
// 將p.print賦值給q
var q = p.print;
p.print(); // “p”
q(); // "我是全局變量"
this
其實很常見,比如說:
<!--HTML表單片段-->
年齡:<input type="text" name = "age" placeholder="請輸入你的年齡!" onChange="varifyAge(this)"></input>
<!--javaScript代碼片段--->
function onChange(object){
if(object.value > 120 || object.value < 0 ){
console.error("你輸入的年齡不合法!");
}
}
上面這個例子,this
指向的是<input>
這個文本框。
在JavaScript 中,一切皆對象,運行環境也是對象。函數都是在某個對象之中運行,this
就是函數運行時所在的對象(環境)。
2. 實質
javaScript中之所以有this
關鍵字,跟內存裏面的數據結構有很大關係。
var example = {color: "red"};
在上述代碼中,將一個對象賦值給變量example
,javaScript引擎會先在內存裏生成一個對象{color: "red"}
,然後把這個對象的內存地址賦值給變量example
。即變量example
是一個對象的內存地址 。當讀取example.color
時,先從example
拿到對象內存地址,然後再從內存地址提取出來原始的對象。
原始的對象以字典結構保存,每一個屬性名都對應一個屬性描述對象。以上面的例子爲例,實際上是以下面的形式保存的。
{
color:{ // 屬性描述對象
value: "red",
writable: true,
enumerable: true,
configurable: true
}
}
如果對象的屬性是一個函數,會將函數單獨保存,把函數的內存地址賦值給屬性名對應的屬性描述對象的value
。
var example = {
print: function(){
console.log("我是一個函數");
}
}
// 內存中的保存形式
{
print: { // 屬性描述對象
value: 函數內存地址,
......
}
}
由於函數是在內存中單獨存儲的,所以可以在不同的上下文環境中執行。
function printName(){
console.log(this.name);
}
var person = {
name: "person對象裏的name",
printName: printName
}
var name = "全局環境中的name"
// 單獨執行
printName(); // "全局環境中的name"
//person環境中執行
person.printName(); // "person對象裏的name",
JavaScript 允許在函數體內部,引用當前環境的其他變量。由於函數可以在不同的運行環境執行,所以需要有一種機制,能夠在函數體內部獲得當前的運行環境。所以,this
就出現了,它的設計目的就是在函數體內部,指代函數當前的運行環境。
3. 使用場合
this
主要在以下場景使用。
- 構造函數
構造函數中的this
指的實例。 - 全局環境
全局環境中使用,指的是頂層對象window
。 - 對象的方法
如果對象的方法裏面包含this
,this
的指向就是方法運行時所在的對象。該方法賦值給另一個對象,就會改變this
的指向。
var example ={
f: function () {
console.log(this);
}
};
// 情況一
example.f() // obj
// 情況二
(example.f = example.f)() // window
// 情況三
(false || example.f)() // window
// 情況四
(1, example.f)() // window
之所以出現上述情況,是因爲example
是個對象,它的屬性f
其實是一個內存地址,指向屬性f
對應的屬性描述對象,但是屬性f
對應的描述對象的value
其實是函數的內存地址。即example
和 example.f
存儲在兩個內存地址中。example.f()
是從地址一調用地址二,所以地址二的運行環境是地址一,this
指向example
。example.f
是直接調用地址二,此時運行環境就是全局環境,因此this
指向全局環境。
4. 綁定this的方法
this
的動態切換,爲 JavaScript 創造了巨大的靈活性,但有時,需要把this
固定下來,JavaScript 提供了call
、apply
、bind
這三個方法,來切換/固定this
的指向。
4.1 Function.prototype.call()
函數實例的call
方法,可以指定函數內部this
的指向,然後在所指定的作用域中,調用該函數。
var obj = {};
var f = function () {
return this;
};
f() === window // true
// 調用call方法,改變this指向
f.call(obj) === obj // true
在上面的例子中,全局環境中運行函數f()
,this
指向全局環境;使用call()
方法,將this
的指向改變爲obj
對象,然後在obj
對象的作用域中運行函數f()
。
call
方法的參數,是一個對象。如果參數爲空、null
和undefined
,則默認傳入全局對象。
var number = 123;
var obj = { number: 456 };
function a() {
console.log(this.number);
}
// 參數爲空,null,undefined默認傳入全局對象
a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
// 改變this的指向
a.call(obj) // 456
call
方法還可以接受多個參數。call
的第一個參數就是this
所要指向的那個對象,後面的參數則是函數調用時所需的參數。
function add(a, b) {
return a + b;
}
// 多個參數,第一個爲this指向的對象,後面的參數是函數調用時需要的參數
add.call(this, 1, 2) // 3
4.2 Function.prototype.apply()
apply
方法的作用與call
方法類似,也是改變this
指向,然後再調用該函數。唯一的區別就是,它接收一個數組作爲函數執行時的參數。
apply
常用來轉換類似數組的對象。
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
4.3 Function.prototype.bind()
bind
方法用於將函數體內的this
綁定到某個對象,然後返回一個新函數。
var counter = {
count: 0,
add: function () {
this.count++;
}
};
// 不綁定
var func = counter.add;
func();
counter.count; // 0
// 綁定
var func = counter.add.bind(counter);
func();
counter.count // 1
上面例子中,如果不綁定,將counter.add
賦值給func
,此時this
指向全局環境。
注意:bind
方法每運行一次,就會返回一個新函數。
5. 參考鏈接
本篇博文是我自己學習筆記,原文請參考javaScript教程。