JavaScript中函數(shù)中的this到底是什么

目錄

  1. 函數(shù)中this是什么
  2. 如何改變this的指向(call, apply, bind)
  3. 上述方法的不同點
  4. bind方法的實現(xiàn)原理

正文

1. 函數(shù)中this是什么

this,是指向當前函數(shù)的運行環(huán)境,也就是執(zhí)行上下文旦签。

  1. 當直接在瀏覽器全局環(huán)境中調(diào)用函數(shù)洋腮,那么渐行,this指向的就是Window對象潮秘;
function simple() {
    console.log(this);
}
// 直接在全局找那個調(diào)用
simple();
全局中調(diào)用函數(shù)中的this
  1. 直接調(diào)用特定對象中的方法時核行,此時這個方法就運行在這個特定對象的中喇完;
var runEnv = {
    name: 'yyp',
    age: 18,

    printThis: function() {
        console.log(this);
    }
}

// 運行runEnv對象中printThis方法
runEnv.printThis();
特定對象中方法的this
  1. 特殊情況:如果我們在全局中保留了特定對象中方法的引用后伦泥, 直接在全局中執(zhí)行,這是锦溪,由于此時的方法不脯,是在全局環(huán)境中執(zhí)行的,因此this指向全局刻诊;
var runEnv = {
    name: 'yyp',
    age: 18,

    printThis: function() {
        console.log(this);
    }
}

// 在全局中保留runEnv對象中printThis方法的引用
var cloneFun = runEnv.printThis;

// 在全局中運行這個函數(shù)
cloneFun();
在全局中運行保存的新引用
  1. 使用new進行調(diào)用
    使用new調(diào)用情況則有所不同防楷,首先會先創(chuàng)建一個新對象,然后將函數(shù)的this指向指向這個新對象则涯,然后函數(shù)中所有語句都是在這個運行作用域上執(zhí)行复局,最后返回這個新對象。
function Person(name, age) {
    this.name = name;
    this.age = age;
}
// 調(diào)用Person構(gòu)造函數(shù)
var person = new Person('yyp', 12);
剛進如函數(shù)時this和執(zhí)行完兩條語句后的this

從上面四種情況可以得出粟判,this在一個動態(tài)的概念亿昏,相對于運行過程的。

2. 如何改變this的指向(call, apply, bind)

以上档礁,是this的一些常規(guī)指向情況角钩,在實際開發(fā)中,可以根據(jù)實際的需求呻澜,改變函數(shù)中的this指向递礼。call, apply, bind都是函數(shù)原型上的方法,因此每個函數(shù)都可以調(diào)用這些方法易迹。

2 使用方法簡介
  1. call方法
    語法: fun.call(thisArg[, arg1[, arg2[, ...]]])
    thisArg:運行時this的指向
    arg1, arg2, ...: 可選傳遞給函數(shù)的參數(shù)
var person1 = {
    name: 'yyp'
}

var person2 = {
    name: 'wg'
}

function sayHi() {
    console.log('Hi, ' + this.name);
}

// 綁定this指向person1
sayHi.call(person1);

// 綁定this指向person2
sayHi.call(person2);
最終運行結(jié)果
  1. apply方法
    語法: fun.apply(thisArg, [argsArray])
    thisArg:運行時this的指向
    argsArray: 傳遞給函數(shù)的參數(shù)列表數(shù)組
var person1 = {
    name: 'yyp'
}

var person2 = {
    name: 'wg'
}

function sayHi() {
    console.log('Hi, ' + this.name);
}

// 綁定this指向person1
sayHi.apply(person1);

// 綁定this指向person2
sayHi.apply(person2);

上面列出的代碼宰衙,和上一次的代碼只是將call方法變?yōu)閍pply平道,具體apply和call用戶不同睹欲,下文會單獨進行解釋。

  1. bind方法
    語法: fun.bind(thisArg[, arg1[, arg2[, ...]]])一屋;
    thisArg:運行時this的指向
    arg1, arg2, ...: 可選傳遞給函數(shù)的參數(shù)
var person1 = {
    name: 'yyp'
}

var person2 = {
    name: 'wg'
}

function sayHi() {
    console.log('Hi, ' + this.name);
}

// 綁定this指向person1窘疮,返回一個函數(shù)
var hello_person1 = sayHi.bind(person1);
// 執(zhí)行函數(shù)
hello_person1();

// 綁定this指向person2,返回一個函數(shù)
var hello_person2 = sayHi.bind(person2);
// 執(zhí)行函數(shù)
hello_person2();

最終的運行結(jié)果和上面兩種情況中一樣冀墨。

3. 上述方法的不同點
  1. call和apply方法的不同之處
    call闸衫,apply方法主要是在傳遞參數(shù)的方式上不同,上面舉例的函數(shù)不需要提供參數(shù)诽嘉,因此蔚出,兩個可以交替使用弟翘。
    但是當函數(shù)調(diào)用需要傳遞參數(shù)時,兩者的使用方法就不一樣了骄酗,有上面提供的語法格式可以知道稀余,call方法將需要傳遞的參數(shù)平鋪,一個一個的傳遞趋翻,而apply方法睛琳,則需要將所有需要傳遞的參數(shù)放到一個數(shù)組中,然后傳遞給函數(shù)踏烙。
var runEnv = {
    name: 'yyp'
}

function sayHi(str) {
    console.log(str + ' ' + this.name);
}

// 使用call方法綁定this师骗,并傳遞參數(shù),將參數(shù)依次傳遞
sayHi.call(runEnv, 'Hi!');

// 使用bind方法綁定this讨惩,并傳遞參數(shù)辟癌,所有傳入的參數(shù),需要先組成數(shù)組荐捻,然后作為第二個參數(shù)傳入
sayHi.apply(runEnv, ['Hello!']);

在使用過程中如何選擇
1.1 如果給定參數(shù)列表是數(shù)組形式愿待,選用apply

var items = [1, 2, 3, 4];

// Math對象中的max方法,可以接受多個參數(shù)
// 如果需要梳理的元素是一個數(shù)組靴患,那么我們可以使用apply使用方法
// 不用將數(shù)組中的元素一個個提取出來仍侥,在進行處理
var max = Math.max.apply(null, items);

console.log(max);

1.2 從性能方面考慮

ECMA-262文檔中call方法定義
ECMA-262文檔中apply方法定義

從這兩個文檔中我們可以發(fā)現(xiàn),調(diào)用call方法鸳君,如果傳入?yún)?shù)农渊,直接從第二個參數(shù)從左向右將參數(shù)添加到argList中即可,而apply方法要調(diào)用一個CreateListFromArrayLike方法或颊,將傳入的數(shù)組元素處理為合法的argList砸紊,因此在可能存在性能上的差異。

jsPerf中對其運行性能進行對比發(fā)現(xiàn)對比結(jié)果囱挑。在參數(shù)較少(1-3)個時采用call的方式調(diào)用(lodash就是采用這種方式重寫的)醉顽。

  1. bind方法和其他兩種方法的不同之處
    bind方法,是ES5才提出的平挑,其主要的不同就在于游添,call和apply方法,是在指定的作用域上直接運行函數(shù)通熄,而bind方法是創(chuàng)建一個新的函數(shù)供后期直接使用唆涝,其傳入的參數(shù),也會直接綁定到這個新函數(shù)上唇辨。

2.1 bind函數(shù)的運行作用域
即使在全局作用域中運行廊酣,或者用call或者apply綁定其他作用域,都不會改變其運行作用域赏枚。

function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 創(chuàng)建一個空對象
var emptyObj = {};
// 將函數(shù)的this指向emptyObj亡驰,獲取一個新的函數(shù)
var bindPerson = Person.bind(emptyObj);
// 在全局中運行該函數(shù)
bindPerson('yyp', 18);
運行后emptyObj對象內(nèi)容

運行綁定后獲取的新函數(shù)時晓猛,此時我們已經(jīng)將this綁定到制定的空對象上,運行bindPerson函數(shù)凡辱,相當于運行Person.call(emptyObj, 'yyp', 18);因此會在emptyObj上添加兩個屬性name和age鞍帝。

2.2 bind時綁定提前綁定參數(shù)
如果在調(diào)用bind函數(shù)時,提供傳入?yún)?shù)時煞茫,此時這些參數(shù)帕涌,將會直接綁定到新函數(shù)上,后續(xù)執(zhí)行新函數(shù)傳入的參數(shù)將會追加到這些參數(shù)后面续徽。

function Person(name, age) {
    this.name = name;
    this.age = age;
}

var emptyObj = {};
// 此時提供一個參數(shù)
var bindPerson = Person.bind(emptyObj, 'yyp');
// 調(diào)用時提供一個參數(shù)蚓曼,此時效果和前一個保持一致
bindPerson(18);

// 提供兩個參數(shù)時,根據(jù)實際參數(shù)情況钦扭,會丟掉后面的參數(shù)
// bindPerson('yyp', 18);

2.3 使用new調(diào)用函數(shù)時纫版,出現(xiàn)的問題
使用new函數(shù)進行調(diào)用時,this不會指向bind時設定的對象客情,而是和直接使用new調(diào)用原始函數(shù)行為保持一致其弊,但是之前提前綁定的參數(shù),還是會生效膀斋。

function Person(name, age) {
    this.name = name;
    this.age = age;
}

var emptyObj = {};
var bindPerson = Person.bind(emptyObj);

var person = new bindPerson('yyp', 18);
使用new調(diào)用函數(shù)時運行結(jié)果

從上面運行結(jié)果可以發(fā)現(xiàn)梭伐,使用new調(diào)用時,綁定的作用于失效仰担,this原先的this糊识,因此emptyObj還是為空。

4. bind方法的實現(xiàn)原理
Function.prototype.bind = function(oThis) {
    // 判斷調(diào)用這個方法的對象是不是一個函數(shù)
    if (typeof this !== 'function') {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    // 處理傳入的參數(shù)部分
    // aArgs:保存綁定時傳入的參數(shù)
    // fToBind:指向需要綁定的函數(shù)
    // fNOP: 空函數(shù)
    // fBound:將要返回的函數(shù)引用
    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function() {},
        fBound = function() {
            // 綁定函數(shù)執(zhí)行時運行的處理邏輯
            // 如果當前任何環(huán)境中運行摔蓝,執(zhí)行函數(shù)中的this為之前制定的作用域赂苗,即作用域不會做二次綁定
            // 如果使用new進行調(diào)用時,執(zhí)行函數(shù)中的this不改變
            return fToBind.apply(this instanceof fNOP ?
                this :
                oThis,
                // 獲取調(diào)用時(fBound)的傳參.bind 返回的函數(shù)入?yún)⑼沁@么傳遞的
                aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 維護原型關系
    if (this.prototype) {
        // Function.prototype doesn't have a prototype property
        fNOP.prototype = this.prototype;
    }

    // 返回的函數(shù)fBound繼承fNOP贮尉,是fNOP的一個實例拌滋,
    // 作用:1. 維持原型鏈
    // 2. 用new進行該函數(shù)調(diào)用時,此時的this是fNOP的一個實例
    fBound.prototype = new fNOP();

    return fBound;
};

由上面代碼可以了解到猜谚,調(diào)用bind函數(shù)進行綁定后败砂,會返回一個新的函數(shù),并且將調(diào)用時傳入的參數(shù)保存起來龄毡。當調(diào)用這個綁定函數(shù)時吠卷,先判斷this的指向锡垄,如果是使用new調(diào)用沦零,this將會是bind函數(shù)的實例(綁定函數(shù)又是內(nèi)部fNOP的實例),直接使用當前this货岭;如果是其他情況路操,則直接在之前綁定的運行作用域上執(zhí)行疾渴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市屯仗,隨后出現(xiàn)的幾起案子搞坝,更是在濱河造成了極大的恐慌,老刑警劉巖魁袜,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桩撮,死亡現(xiàn)場離奇詭異,居然都是意外死亡峰弹,警方通過查閱死者的電腦和手機店量,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鞠呈,“玉大人融师,你說我怎么就攤上這事∫狭撸” “怎么了旱爆?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窘茁。 經(jīng)常有香客問我怀伦,道長,這世上最難降的妖魔是什么山林? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任空镜,我火速辦了婚禮,結(jié)果婚禮上捌朴,老公的妹妹穿的比我還像新娘吴攒。我一直安慰自己,他們只是感情好砂蔽,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布洼怔。 她就那樣靜靜地躺著,像睡著了一般左驾。 火紅的嫁衣襯著肌膚如雪镣隶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天诡右,我揣著相機與錄音安岂,去河邊找鬼。 笑死帆吻,一個胖子當著我的面吹牛域那,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播猜煮,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼次员,長吁一口氣:“原來是場噩夢啊……” “哼败许!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起淑蔚,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤市殷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后刹衫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體醋寝,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年带迟,在試婚紗的時候發(fā)現(xiàn)自己被綠了甥桂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡邮旷,死狀恐怖黄选,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情婶肩,我是刑警寧澤办陷,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站律歼,受9級特大地震影響民镜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜险毁,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一制圈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧畔况,春花似錦鲸鹦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吵瞻,卻和暖如春葛菇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背橡羞。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工眯停, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卿泽。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓莺债,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子九府,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容