沐鳴代理:_JavaScript this 關鍵字詳解
一、前言
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
站長推薦
1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲
2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入
鏈接: http://www.fly63.com/article/detial/10078