javaScript面向對象編程之this關鍵字

javaScript面向對象編程之this關鍵字

1. 含義

簡而言之,this就是屬性或方法“當前”所在的對象。

比如:

var person = {
	name: "jidi",
	print: function(){
		return "name:"+this.name;
	}
}
person.print(); // "name:jidi"

在上面這個例子中,this.name中的this表示name屬性所在的對象。因爲this.nameprint()方法中調用,而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主要在以下場景使用。

  1. 構造函數
    構造函數中的this指的實例。
  2. 全局環境
    全局環境中使用,指的是頂層對象window
  3. 對象的方法
    如果對象的方法裏面包含thisthis的指向就是方法運行時所在的對象。該方法賦值給另一個對象,就會改變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其實是函數的內存地址。即exampleexample.f存儲在兩個內存地址中。example.f()是從地址一調用地址二,所以地址二的運行環境是地址一,this指向exampleexample.f是直接調用地址二,此時運行環境就是全局環境,因此this指向全局環境。

4. 綁定this的方法

this的動態切換,爲 JavaScript 創造了巨大的靈活性,但有時,需要把this固定下來,JavaScript 提供了callapplybind這三個方法,來切換/固定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方法的參數,是一個對象。如果參數爲空、nullundefined,則默認傳入全局對象。

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教程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章