this:它到底指向哪裏!箭頭函數的this又是啥?

JavaScritp 中的 this

總結一句話(永真給的信)

大雪紛飛,永真在井上面丟垃圾一封信,信上寫着如下內容:

誰最終調用,this 就指向誰。

那麼如何理解永真寫的話呢?請接着往下看~


默認綁定(指向少主window)

我(this)的職責就是保護指向 少主,少主喜歡用window玩遊戲,姑且叫少主window吧。

  • 只狼聽從了義父之前的命令,好好保護少主,所以this指針(只狼 )默認綁定(跟隨 )的是 window少主
console.log(this);      // window
  • 少主活了幾百年,還那麼年輕,而且會的東西很多 少主還會a函數,寫全局函數的話,也是指向的 window
function a() {
    console.log(this);
}
a()                     // window
//實際上是  window.a(); 我們可以展開window看看

在這裏插入圖片描述

  • 少主還會做日晷,沙漏等小工具,用來做定時器, 定時器是window少主 )的方法,所以this也會指向window
// setInterval 也是 window 下的方法
setInterval(function () {
    console.log(this);		// window
}, 1000)

嚴格模式(變若之子)

爲了追尋長生不老,山上的僧人開始作妖,嚴格實驗了好多小孩,最後就一個小女孩活了下來,成了變若之子(假的皇子)

  • 嚴格模式下的this指向 undefined(因爲是假的皇子,所以就undefined了)
function a() {
    "use strict";
    console.log(this);
}
a()                     // undefined
console.log(this);

補充:什麼是嚴格模式
簡單來說就是爲了更好的規範 JavaScript 語言的書寫,具體請參考阮一峯的嚴格模式詳解

隱式綁定(葦名弦一郎)

情景:我們帶着皇子一路小跑,走出隧道,只見弦一郎在堵門,(簡介,遊戲名《只狼》,第一個BOSS的名字)

開門見山!!先來打葦名弦一郎

var name = '我是全局name'
function fn() {
    var name = '我是fn';
    console.log(this.name);
}
var objA = {
    name:'我是objA',
    showA:fn
}
var objB = {
    name:"我是B對象",
    showB:objA
}
objB.showB.showA()

請問最終打印的是什麼呢?這裏我先不告訴你們答案,先跟着我一步一步往下走~

分支1:沒打過弦一郎(沒做對 ):失去手臂,等待佛雕師(作者 )給你安義肢(講解 )。
分支2:打過弦一郎:同樣失去手臂(不要怪我卑鄙!我叫屑一狼),等待佛雕師…
戰果:經驗 +0,金幣 -0,金錢 -0,HP -0,失去真手臂

次日:看到淡淡的油燈光亮,發現自己躺在破舊寺廟的草蓆上,並且獲得了義肢。
戰果:獲得義肢,HP +100

來看一下栗子(葦名城雜兵,經驗怪 ):

function fn() {
    console.log(this);
}
var obj = {
    show: fn
};
obj.show();       // {show: ƒ}
// this 指向的關鍵在於是誰最終執行的

成功忍殺看懂了這個 :金錢+5,經驗+5

第二個栗子(葦名城雜兵,經驗怪 ):

function fn() {
    console.log(this);
}
var obj = {
    show: fn()		// 因爲fn才最終執行者,括號代表執行!而 fn 是 window 下的函數
};
obj.show;       // window

成功忍殺:HP+5,金錢+5,經驗+5
獲得少主給的:傷葫蘆,楔丸

此時來到了葦名城 城邑外圍 城門道,遇到了第一個boss(河源田直盛)

function fn() {
    console.log(this.name);
}
var objA = {
    name:'我是A對象',
    showA:fn
}
var objB = {
    name:"我是B對象",
    showB:objA
}
objB.showB.showA()       // 我是A對象
// 爲啥指的是objA呢?
// 你要清除是誰最終調用的fn,是不是objA裏纔有fn啊,最終調用的也是objA!所以指向就是objA。

成功忍殺看懂了這個 :HP+10,金錢+10,經驗+10
獲得技能:識破

總結:以上例子可以說明,this的指向跟函數寫在哪裏沒啥關係,關鍵在於最終調用者是誰

隱式丟失(赤鬼 + 鬼庭形部雅孝)

情景:來到了被鎖住的赤鬼。發現兜裏沒有月隱糖,也沒有噴火器,沒關係,有閃避+突刺,超級逃課打法。

var name = '我是全局name';
var obj = {
    name:'我是obj的name',
    fn:function () {
        console.log(this.name);
    }
}
var xxx = obj.fn;
xxx();		// 我是全局name
// 這裏爲什麼是全局name呢
// 因爲xxx是最終調用者,而xxx屬於window,所以this指向的是window

成功忍殺:獲得佛珠,忍者之藥理知識•甲
失敗:死,(少主的龍血之力,無盡復活 )

接着來到下一關

情景:來到了葦名城 正門外城區,遇到鬼庭形部雅孝,不慌,追的驢的尾巴,砍就完事兒了,逃課

var name = '我是全局name';
var obj = {
    name:'我是obj的name',
    fn:function () {
        console.log(this.name);
    }
}
function xxx(param) {
	// 2. 然後在這裏執行函數
    param();
    // 相當於 window.param()
}
// 1. 此時我把函數體當成參數傳遞進去了
xxx(obj.fn)     // 我是全局name

成功忍殺:戰鬥記憶:鬼庭形部雅孝(攻擊力強化),機關筒(義手強化工具)。
失敗:死,(少主的龍血之力,無盡復活 )

接着到了火牛關

情景:火牛血厚,建議買鞭炮(前面的都看懂了

var name = '我是全局name';
var obj = {
    name:'我是obj的name',
    fn:function () {
        console.log(this.name);
    }
};
var obj1 = {
    name:"福山潤"
};

obj1.xxx = obj.fn;
obj1.xxx();     // 福山潤

成功忍殺:佛珠,忍者藥理·乙。
失敗:死,減一半的¥

顯式綁定(櫻龍,白蛇)

我們可以通過call幹柿子 )、apply )和bind櫻龍之淚 )方法更改指針的指向(不同結局)。

// 顯示轉換
var objA = {
    name: "aaa"
};
var objB = {
    name: "bbb"
};
var objC = {
    name: "ccc"
};
var name = '我是全局name';
function fn() {
    console.log(this.name);
}
fn();               // 我是全局name
fn.call(objA);      // aaa
fn.apply(objB);     // bbb
fn.bind(objC)();    // ccc

成功忍殺梟,櫻龍,白蛇:獲得櫻樹枝,櫻龍之淚,鮮柿子,月隱糖
失敗:死,(少主的龍血之力,無盡復活 ),金錢 -1/2

某些方法也可以顯式的更改指針,比方說forEach

var obj = {
    name:'鈴木雅之'
};
var arr = [1,2,3,4];
arr.forEach(function () {
    console.log(this.name);     // 4 * 鈴木雅之
},obj)

越到後期,遊戲所需要的技巧就越高,熟練度也就越重要,(不我就是平a格擋流
顯式綁定的優先級 大於 隱式綁定的優先級,顯式>隱式>默認

var objA = {
    name:'aaa',
    fn:function () {
        console.log(this.name);
    }
}
var objB = {
    name:'bbb'
}
// 前面的是objA.fn() ,隱式綁定,省略了括號,
// 後面的call()是顯示綁定
// 當顯式隱式都存在的時候,顯式優先級更大
objA.fn.call(objB)      //bbb

Dom節點中的this(雜兵)

發現個小胖子,他跟同伴走丟了,他想要我給他找彩色風車…

其實這個跟之前講的蠻像的,我就在這裏再重複一下叭

<body>
    <div class="btn">點我</div>
</body>
<script>
    function show(){
        console.log(this);
    }
    document.querySelector('div').addEventListener('click', show);
    // <div class="btn">點我</div>
    
    document.querySelector('div').addEventListener('click', show());
    // window
</script>

成功忍殺:臥槽小胖子好可憐的,別殺了,騙到商人那裏打掃戰場
失敗:良心受到譴責嗯~ o( ̄▽ ̄)o

new

來到了葦名之底 毒沼,獅子猿 new 出一個小獅子猿,二打一,太難了

function Person() {
    // 1. 剛開始這裏的this指向的是 window
    // 因爲這是函數啊
    this.name = '堺雅人'
}
// 2. 然後通過new方法改變了 this 指向
var p = new Person();

console.log(p.name);

成功忍殺:戰鬥記憶:無首獅子猿,佛珠*2,血刀術(使用不死斬斷絕不死後獲得)
失敗:死,減一半的¥

箭頭函數(另類)

來到了葦名城 城邑外圍 城門道,看到個沒有頭的傢伙,無首,尼瑪打了一架,被戳了菊花,屏幕上出現了:菜

取決於:外層(上層)作用域中的this,和他本身沒有關係

var name = 'www';
function fn() {
    // 這裏就是箭頭函數上層作用域
    // 這裏的 this 指向 window
    console.log(this.name);
    return () => {
        console.log(this.name);
    }
}
var objA = {
    name:'aaa'
};
var xxx = fn();        // www
xxx();                 // www

成功忍殺:哼將之降靈
失敗:死,減一半的¥

var name = 'www';
function fn() {
	// 這裏就是箭頭函數上層作用域
    // 這裏的 this 通過 call 方法指向了 objA
    console.log(this.name);
    return () => {
        console.log(this.name);
    }
}
var objA = {
    name:'aaa'
};
var xxx = fn.call(objA);        // aaa
xxx();                          // aaa

情景:最後來到了葦名城主城 貯水城區,弦一郎喝了變若() 水,劍聖葦名一心想要實現他兒子的最後的願望,就是守護這片土地,於是重返戰場…

  • 箭頭函數中的this它既是喫軟飯的,也很專一
var name = 'www';
function fn() {
    // 喫軟飯的箭頭函數this:一直跟隨着上層作用域 
    // 這裏就是箭頭函數上層作用域
    console.log(this.name);
    return () => {
        console.log(this.name);
    }
}
var objA = {
    name:'aaa'
};
var objB = {
    name:'bbb'
};
// 此時xxx就是箭頭函數
var xxx = fn.call(objA);        // aaa

// 我這時修改箭頭函數的指向,然並卵
// 箭頭函數中的this很專一,取決於外層作用域的指向
xxx.call(objB);                 // aaa
  • 如果想改箭頭函數的this,那麼只能修改上層作用域的this,比方說上面的例子
// 只有修改上層作用域的this,才能改變箭頭函數的this
var xxx = fn.call(objb);        // bbb

// 即使是修改了箭頭函數的指向也沒用
xxx.call(objA);                 // bbb

成功忍殺:戰鬥記憶:劍聖 葦名一心,祕傳·龍閃(技能)。
失敗:死,我死了200多回hhhh。
通關,趕快去救小皇子~


補充

執行上下文

執行上下文是指 函數調用時 在執行棧中產生的變量對象,這個變量對象我們無法直接訪問,但是可以訪問其中的變量、this對象等。

let fn, bar; // 1、進入全局上下文環境
bar = function(x) {
  let b = 5;
  fn(x + b); // 3、進入fn函數上下文環境
};
fn = function(y) {
  let c = 5;
  console.log(y + c); //4、fn出棧,bar出棧
};
bar(10); // 2、進入bar函數上下文環境

img

每次函數調用時,執行棧棧頂都會產生一個新的執行上下文環境,JavaScript引擎會以棧的方式來處理它們,這個棧,我們稱其爲函數調用棧(call stack)。棧底永遠都是全局上下文,而棧頂就是當前處於活動狀態的正在執行的上下文,也稱爲活動對象(running execution context,圖中藍色的塊),區別與底下被掛起的變量對象(執行上下文)。

字面量

用於表達源代碼中一個固定值的表示法

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