一、前言
this關鍵字是JavaScript中最復雜的機制之一,它是一個很特別的關鍵字,被自動定義在所有函式的作用域中,對于那些沒有投入時間學習this機制的JavaScript開發者來說,this的系結一直是一件非常令人困惑的事,

二、了解this
學習this的第一步是明白this既不指向函式自身也不指向函式的詞法作用域,你也許被這樣的解釋誤導過,但其實它們都是錯誤的,隨著函式使用場合的不同,this的值會發生變化,但總有一條原則就是JS中的this代表的是當前行為執行的主體,在JS中主要研究的都是函式中的this,但并不是說只有在函式里才有this,this實際上是在函式被呼叫時發生的系結,它指向什么完全取決于函式在哪里被呼叫,如何的區分this呢?
三、this到底是誰
這要分情況討論,常見有五種情況:
1、函式執行時首先看函式名前面是否有".",有的話,"."前面是誰,this就是誰;沒有的話this就是window
function fn(){
console.log(this);
}
var obj={fn:fn};
fn();//this->window
obj.fn();//this->obj
function sum(){
fn();//this->window
}
sum();
var oo={
sum:function(){
console.log(this);//this->oo
fn();//this->window
}
};
oo.sum();
2、自執行函式中的this永遠是window
(function(){ //this->window })();
~function(){ //this->window }();
3、給元素的某一個事件系結方法,當事件觸發的時候,執行對應的方法,方法中的this是當前的元素,除了IE6~8下使用attachEvent(IE一個著名的bug)
- DOM零級事件系結
oDiv.onclick=function(){
//this->oDiv
};
- DOM二級事件系結
oDiv.addEventListener("click",function(){
//this->oDiv
},false);
- 在IE6~8下使用attachEvent,默認的this就是指的window物件
oDiv.attachEvent("click",function(){
//this->window
});
我們大多數時候,遇到事件系結,如下面例子這種,對于IE6~8下使用attachEvent不必太較真
function fn(){
console.log(this);
}
document.getElementById("div1").onclick=fn;//fn中的this就是#divl
document.getElementById("div1").onclick=function(){
console.log(this);//this->#div1
fn();//this->window
};
4、在建構式模式中,類中(函式體中)出現的this.xxx=xxx中的this是當前類的一個實體
function CreateJsPerson(name,age){
//瀏覽器默認創建的物件就是我們的實體p1->this
this.name=name;//->p1.name=name
this.age=age;
this.writeJs=function(){
console.log("my name is"+this.name +",i can write Js");
};
//瀏覽器再把創建的實體默認的進行回傳
}
var p1=new CreateJsPerson("尹華芝",48);
必須要注意一點:類中某一個屬性值(方法),方法中的this需要看方法執行的時候,前面是否有".",才能知道this是誰,大家不妨看下接下來的這個例子,就可明白是啥意思,
function Fn(){
this.x=100;//this->f1
this.getX=function(){
console.log(this.x);//this->需要看getX執行的時候才知道
}
}
var f1=new Fn;
f1.getX();//->方法中的this是f1,所以f1.x=100
var ss=f1.getX;
ss();//->方法中的this是window ->undefined
5.call、apply和bind
我們先來看一個問題,想在下面的例子中this系結obj,怎么實作?
var obj={name:"浪里行舟"};
function fn(){
console.log(this);//this=>window
}
fn();
obj.fn();//->Uncaught TypeError:obj.fn is not a function
如果直接系結obj.fn(),程式就會報錯,這里我們應該用fn.call(obj)就可以實作this系結obj,接下來我們詳細介紹下call方法:
- call方法的作用:
①首先我們讓原型上的call方法執行,在執行call方法的時候,我們讓fn方法中的this變為第一個引數值obj;然后再把fn這個函式執行,
②call還可以傳值,在嚴格模式下和非嚴格模式下,得到值不一樣,
//在非嚴格模式下
var obj={name:"浪里行舟 "};
function fn(num1,num2){
console.log(num1+num2);
console.log(this);
}
fn.call(100,200);//this->100 num1=200 num2=undefined
fn.call(obj,100,200);//this->obj num1=100 num2=200
fn.call();//this->window
fn.call(null);//this->window
fn.call(undefined);//this->window
//嚴格模式下
fn.call();//在嚴格模式下this->undefined
fn.call(null);// 在嚴格模式 下this->null
fn.call(undefined);//在嚴格模式下this->undefined
- apply和call方法的作用是一模一樣的,都是用來改變方法的this關鍵字并且把方法
執行,而且在嚴格模式下和非嚴格模式下對于第一個引數是null/undefined這種情況的規
律也是一樣的,
兩者唯一的區別:call在給fn傳遞引數的時候,是一個個的傳遞值的,而apply不是一個個傳,而是把要給fn傳遞的引數值統一的放在一個陣列中進行操作,但是也相當子一個個的給fn的形參賦值,總結一句話:call第二個引數開始接受一個引數串列,apply第二個引數開始接受一個引數陣列
fn.call(obj,100,200);
fn.apply(obj,[100,200]);
- bind:這個方法在IE6~8下不兼容,和call/apply類似都是用來改變this關鍵字的,但是和這兩者有明顯區別:
fn.call(obj,1,2);//->改變this和執行fn函式是一起都完成了
fn.bind(obj,1,2);//->只是改變了fn中的this為obj,并且給fn傳遞了兩個引數值1、2,
但是此時并沒有把fn這個函式執行
var tempFn=fn.bind(obj,1,2);
tempFn(); //這樣才把fn這個函式執行
bind體現了預處理思想:事先把fn的this改變為我們想要的結果,并且把對應的引數值也準備好,以后要用到了,直接的執行即可,
call和apply直接執行函式,而bind需要再一次呼叫,
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)

上述代碼沒有執行,bind回傳改變了背景關系的一個函式,我們必須要手動去呼叫:
b.bind(a,1,2)() //3
必須要宣告一點:遇到第五種情況(call apply和bind),前面四種全部讓步,
四、箭頭函式this指向
箭頭函式正如名稱所示那樣使用一個“箭頭”(=>)來定義函式的新語法,但它優于傳統的函式,主要體現兩點:更簡短的函式并且不系結this,
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth; // this指向window或undefined
};
return fn();
}
};
現在,箭頭函式完全修復了this的指向,箭頭函式沒有自己的this,箭頭函式的this不是呼叫的時候決定的,而是在定義的時候處在的物件就是它的this,
換句話說,箭頭函式的this看外層的是否有函式,如果有,外層函式的this就是內部箭頭函式的this,如果沒有,則this是window,
<button id="btn1">測驗箭頭函式this_1</button>
<button id="btn2">測驗箭頭函式this_2</button>
<script type="text/javascript">
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function () {
btn1.onclick = () => {
console.log(this);//obj
};
}
};
obj.getName();
</script>

上例中,由于箭頭函式不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this,其實可以簡化為如下代碼:
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function () {
console.log(this)
}
};
obj.getName();
那假如上一層并不存在函式,this指向又是誰?
<button id="btn1">測驗箭頭函式this_1</button>
<button id="btn2">測驗箭頭函式this_2</button>
<script type="text/javascript">
let btn2 = document.getElementById('btn2');
let obj = {
name: 'kobe',
age: 39,
getName: () => {
btn2.onclick = () => {
console.log(this);//window
};
}
};
obj.getName();
</script>

上例中,雖然存在兩個箭頭函式,其實this取決于最外層的箭頭函式,由于obj是個物件而非函式,所以this指向為Window物件
由于this在箭頭函式中已經按照詞法作用域系結了,所以,用call()或者apply()呼叫箭頭函式時,無法對this進行系結,即傳入的第一個引數被忽略:
var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; // 1990
var fn = (y) => y - this.birth; // this.birth仍是1990
return fn.call({birth:2000}, year);
}
};
obj.getAge(2018); // 28
擴展閱讀
箭頭函式-廖雪峰
JS中的箭頭函式與this
this、apply、call、bind
作者:浪里行舟
鏈接:你還沒搞懂 this?
來源:github
著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/248934.html
標籤:JavaScript
上一篇:一次Vue代碼bug定位
下一篇:d3 zoom 抖動問題 事件
