最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript中this指向、bind、call、apply、知識(shí)點(diǎn)與相關(guān)面試題匯總

 更新時(shí)間:2026年05月18日 08:59:37   作者:Hello--_--World  
Javascript是一門基于對(duì)象的動(dòng)態(tài)語(yǔ)言,也就是說(shuō)所有東西都是對(duì)象,一個(gè)很典型的例子就是函數(shù)也被視為普通的對(duì)象,這篇文章主要介紹了JavaScript中this指向、bind、call、apply、知識(shí)點(diǎn)與相關(guān)面試題的相關(guān)資料,需要的朋友可以參考下

一、JS中 this 究竟指向誰(shuí)?

在 JavaScript 中,this 的指向問(wèn)題常被稱為“面試第一道坎”。其實(shí),掌握它的秘訣只有一句話:

  • this 的指向并不取決于函數(shù)定義的位置,而是取決于函數(shù)“如何被調(diào)用”。
  • 箭頭函數(shù)的 this 捕獲自定義時(shí)所處的外部(父級(jí))詞法環(huán)境。

1. this 的值在何時(shí)確定?

this 是在執(zhí)行上下文(Execution Context)創(chuàng)建時(shí)確定的。
這意味著:

  • 定義時(shí)無(wú)效: 僅僅聲明一個(gè)普通函數(shù),this 是沒有實(shí)際意義的占位符。

  • 運(yùn)行時(shí)確定: 只有當(dāng)函數(shù)被真正觸發(fā)調(diào)用時(shí),JavaScript 引擎才會(huì)根據(jù)調(diào)用方式把特定的對(duì)象“綁定”給 this。

2. 四大核心綁定規(guī)則

判斷 this 指向時(shí),可以按照以下四個(gè)規(guī)則的優(yōu)先級(jí)進(jìn)行對(duì)比:

① 默認(rèn)綁定 (Default Binding)

當(dāng)函數(shù)作為獨(dú)立調(diào)用(不帶任何修飾的函數(shù)調(diào)用)時(shí),this 指向全局對(duì)象。

  • 非嚴(yán)格模式: 指向 window (瀏覽器) 或 global (Node.js)。

  • 嚴(yán)格模式 (‘use strict’): this 為 undefined。

② 隱式綁定 (Implicit Binding)

當(dāng)函數(shù)作為某個(gè)對(duì)象的方法被調(diào)用時(shí),this 指向該對(duì)象。

const obj = {
  name: 'Gemini',
  sayHi() { console.log(this.name); }
};
obj.sayHi(); // this 指向 obj,輸出 "Gemini"
  • 注意 隱式丟失隱式綁定容易出現(xiàn)“丟失”現(xiàn)象。例如將 obj.sayHi 賦值給一個(gè)變量再調(diào)用,就會(huì)退化為默認(rèn)綁定。

③ 顯式綁定 (Explicit Binding)

通過(guò) call()、apply() 或 bind() 直接指定 this 的值。

  • call/apply:立即執(zhí)行函數(shù)并改變 this。

  • bind:返回一個(gè)硬綁定了 this 的新函數(shù),之后無(wú)論怎么調(diào)用,this 都不會(huì)再變。

④ new 綁定 (new Binding)

當(dāng)使用 new 關(guān)鍵字調(diào)用構(gòu)造函數(shù)時(shí),JavaScript 內(nèi)部會(huì)創(chuàng)建一個(gè)新對(duì)象,并將 this 指向這個(gè)新創(chuàng)建的實(shí)例對(duì)象。

3. 箭頭函數(shù)的this為何不同?

箭頭函數(shù)是 this 規(guī)則中的“特例”,因?yàn)樗?strong>沒有自己的 this。

  • 有何不同: 箭頭函數(shù)的 this 捕獲自定義時(shí)所處的外部(父級(jí))詞法環(huán)境。
  • 為什么: 在箭頭函數(shù)出現(xiàn)前,回調(diào)函數(shù)(如 setTimeout)中的 this 經(jīng)常意外指向全局,開發(fā)者不得不使用 var self = this 這種寫法。箭頭函數(shù)通過(guò)詞法作用域(Lexical Scoping)讓 this 保持邏輯上的連貫性,即“外層是誰(shuí),我就是誰(shuí)”。
  • 不可改變: 由于箭頭函數(shù)本身沒有綁定 this,所以 call()、apply()bind() 對(duì)它無(wú)效。

4. 如何判斷復(fù)雜場(chǎng)景下的指向?

面對(duì)嵌套、回調(diào)等復(fù)雜代碼,可以遵循以下 “優(yōu)先級(jí)排除法”(由高到低):

優(yōu)先級(jí)判定條件結(jié)論
1 (最高)函數(shù)是 new 調(diào)用的嗎?是: 指向新創(chuàng)建的實(shí)例對(duì)象。
2函數(shù)通過(guò) call/apply/bind 調(diào)用嗎?是: 指向指定的第一個(gè)參數(shù)。
3函數(shù)在某個(gè)上下文對(duì)象中調(diào)用嗎?終點(diǎn): 指向該所屬對(duì)象(如 obj.foo())。
4 (最低)以上都不是(獨(dú)立調(diào)用)默認(rèn): 指向全局對(duì)象(嚴(yán)格模式下為 undefined)。

兩個(gè)特殊檢查點(diǎn):

  1. 先看是否為箭頭函數(shù):
    如果是,無(wú)視以上所有規(guī)則,直接看它定義時(shí)外層的普通函數(shù) this 是誰(shuí)。

  2. 回調(diào)函數(shù)的陷阱:
    例如 setTimeout(obj.foo, 100),這里雖然傳入了 obj.foo,但函數(shù)實(shí)際上是在定時(shí)器到期后由全局環(huán)境調(diào)用的,屬于**“隱式丟失”**,this 通常指向全局。

總結(jié)公式

誰(shuí)調(diào)用,指誰(shuí);沒調(diào)用,指全局;new 了指實(shí)例;箭頭函數(shù)看“親爹”。

二、js中 this 常見面試題

1. 默認(rèn)綁定與隱式綁定(基礎(chǔ)陷阱)

這是最常見的題型,考察你是否清楚“誰(shuí)調(diào)用指向誰(shuí)”。

var name = "Window";
const obj = {
  name: "Object",
  getName: function() {
    console.log(this.name);
  }
};

const bare = obj.getName;

obj.getName(); // 打印什么?
bare();        // 打印什么?

obj.getName():屬于隱式綁定,調(diào)用者是 obj,所以 this 指向 obj。輸出:“Object”。

bare():雖然它拿到了函數(shù)引用,但調(diào)用時(shí)是直接運(yùn)行的(默認(rèn)綁定)。在非嚴(yán)格模式下,this 指向 window。輸出:“Window”。

2. 箭頭函數(shù)(核心差異)

箭頭函數(shù)是面試官最喜歡用來(lái)對(duì)比 this 的工具,因?yàn)?strong>它沒有自己的 this。

const obj = {
  name: "ArrowObj",
  sayHi: () => {
    console.log(this.name);
  },
  sayHello: function() {
    const inner = () => console.log(this.name);
    inner();
  }
};

obj.sayHi();    // 打印什么?
obj.sayHello(); // 打印什么?

obj.sayHi():箭頭函數(shù)的 this 取決于它定義時(shí)所在的外層作用域。這里外層是全局環(huán)境(不是 obj 的花括號(hào)),所以指向 window。輸出:“Window”。

obj.sayHello():inner 是箭頭函數(shù),它會(huì)找外層非箭頭函數(shù)的 this。它的外層是 sayHello,而 sayHello 是被 obj 調(diào)用的,其 this 是 obj。輸出:“ArrowObj”。

3. 構(gòu)造函數(shù)與顯式綁定(優(yōu)先級(jí))

當(dāng) new、bind、call/apply 同時(shí)出現(xiàn)時(shí),考察的是優(yōu)先級(jí)。

function Foo(name) {
  this.name = name;
}

const obj1 = {};
const bar = Foo.bind(obj1);
bar("Jack");
console.log(obj1.name); // 打印什么?

const bazz = new bar("Rose");
console.log(obj1.name); // 打印什么?
console.log(bazz.name); // 打印什么?

bar(“Jack”):使用 bind 將 this 永久綁定到了 obj1。輸出:“Jack”。

new bar(“Rose”):重點(diǎn)!new 綁定的優(yōu)先級(jí)高于 bind。即使函數(shù)被綁定到了 obj1,使用 new 時(shí)依然會(huì)創(chuàng)建一個(gè)新對(duì)象,并將 this 指向這個(gè)新對(duì)象。

obj1.name 依然是 “Jack”,而 bazz.name 是 “Rose”。

4. 定時(shí)器 + 閉包

var length = 10;
function fn() {
  console.log(this.length);
}

var obj = {
  length: 5,
  method: function(fn) {
    fn(); 
    arguments[0](); 
  }
};

obj.method(fn, 1);

fn():作為參數(shù)傳遞后直接調(diào)用,屬于默認(rèn)綁定。輸出:10。

arguments0:這是一個(gè)極具迷惑性的點(diǎn)。arguments[0] 實(shí)際上是調(diào)用 arguments 對(duì)象上的第 0 個(gè)屬性。這屬于隱式綁定,this 指向 arguments 對(duì)象。因?yàn)?arguments 傳入了兩個(gè)參數(shù),它的 length 是 2。輸出:2。

?? 總結(jié):this 指向判斷準(zhǔn)則

你可以按照這個(gè)優(yōu)先級(jí)順序來(lái)快速判斷:

  • new 綁定:函數(shù)是否在 new 中調(diào)用?如果是,this 指向新創(chuàng)建的對(duì)象。

  • 顯式綁定:函數(shù)是否通過(guò) call、apply 或 bind 調(diào)用?如果是,this 指向指定的對(duì)象。

  • 隱式綁定:函數(shù)是否在某個(gè)上下文對(duì)象中調(diào)用(如 obj.func())?如果是,this 指向那個(gè)對(duì)象。

  • 默認(rèn)綁定:如果以上都不是,在非嚴(yán)格模式下指向 window,嚴(yán)格模式下是 undefined。

  • 箭頭函數(shù):以上規(guī)則統(tǒng)統(tǒng)無(wú)效,請(qǐng)看它外層的函數(shù)/全局作用域的 this 是誰(shuí)。

三、 call、apply、bind 核心知識(shí)點(diǎn)匯總

1. 核心異同對(duì)比表

特性callapplybind
立即執(zhí)行(返回一個(gè)新函數(shù))
參數(shù)形式逐個(gè)列舉:fn.call(obj, 1, 2)數(shù)組/類數(shù)組:fn.apply(obj, [1, 2])逐個(gè)列舉(支持偏函數(shù)/柯里化傳參)
主要用途借用方法、精準(zhǔn)修改 this處理數(shù)組數(shù)據(jù)(如 Math.max鎖定回調(diào)函數(shù)的 this、預(yù)設(shè)參數(shù)
返回值函數(shù)執(zhí)行的結(jié)果函數(shù)執(zhí)行的結(jié)果綁定了 this 后的新函數(shù)

2. 為什么設(shè)計(jì)它們?(優(yōu)點(diǎn)與場(chǎng)景)

  • 代碼復(fù)用:無(wú)需通過(guò)繼承,即可讓一個(gè)對(duì)象直接“借用”另一個(gè)對(duì)象的方法。
  • 解耦:將對(duì)象與其方法執(zhí)行的上下文分離,增加邏輯靈活性。
  • 解決 this 丟失:在異步回調(diào)(如 setTimeout)或事件處理中,防止 this 意外指向全局對(duì)象。

3. 手寫模擬實(shí)現(xiàn)(面試高頻)

① 模擬實(shí)現(xiàn)call

核心思路:將函數(shù)設(shè)為目標(biāo)對(duì)象的臨時(shí)屬性,利用隱式綁定規(guī)則執(zhí)行,執(zhí)行完后刪除。

Function.prototype.myCall = function(context, ...args) {
  // 【1】確定要綁定的目標(biāo)對(duì)象
  // 如果沒傳目標(biāo)對(duì)象,就默認(rèn)是 window
  context = context || window;

  // 【2】把當(dāng)前函數(shù)“變”成目標(biāo)對(duì)象的一個(gè)方法
  // 這里的 this 指向的就是那個(gè)“正在求助”的函數(shù)
  const fnKey = Symbol('temporaryFunction');
  context[fnKey] = this; 

  // 【3】通過(guò)對(duì)象來(lái)調(diào)用這個(gè)函數(shù)
  // 關(guān)鍵:一旦用 context.xxx() 調(diào)用,函數(shù)內(nèi)部的 this 就會(huì)指向 context
  const result = context[fnKey](...args);

  // 【4】任務(wù)完成,把剛才臨時(shí)加進(jìn)去的方法刪掉,保持對(duì)象原貌
  delete context[fnKey];

  // 【5】返回函數(shù)執(zhí)行的結(jié)果
  return result;
};

② 模擬實(shí)現(xiàn) apply

邏輯與 call 基本一致,區(qū)別在于對(duì)第二個(gè)參數(shù)(數(shù)組)的處理。

Function.prototype.myApply = function(context, args) {
  context = context || window;
  const key = Symbol('key');
  context[key] = this;
  
  // 判斷 args 是否為數(shù)組或類數(shù)組,并進(jìn)行解構(gòu)傳參
  const result = Array.isArray(args) ? context[key](...args) : context[key]();
  
  delete context[key];
  return result;
};

③ 模擬實(shí)現(xiàn) bind (進(jìn)階版)

需要考慮:返回新函數(shù)、參數(shù)合并(柯里化)、支持 new 實(shí)例化調(diào)用。

Function.prototype.myBind = function(context, ...args) {
  const self = this; // 保存原函數(shù)
  
  const bound = function(...nextArgs) {
    // 如果是通過(guò) new 調(diào)用的,this 應(yīng)指向?qū)嵗ù藭r(shí)忽略 bind 傳入的 context)
    const isNew = this instanceof bound;
    return self.apply(isNew ? this : context, args.concat(nextArgs));
  };
  
  // 維護(hù)原型鏈(讓實(shí)例能繼承原函數(shù) prototype 上的屬性)
  bound.prototype = Object.create(self.prototype);
  
  return bound;
};

四、call、apply、bind 面試坑點(diǎn)

1. 連續(xù) bind 的結(jié)果

  • fn.bind(obj1).bind(obj2)() -> this 依然指向 obj1。
  • 原因:bind 返回的是一個(gè)新的包裝函數(shù),第二次 bind 綁定的是這個(gè)包裝函數(shù),而最內(nèi)層的 fn 始終只認(rèn)第一次綁定的 obj1。

核心結(jié)論:包裝盒效應(yīng)
bind 的本質(zhì)是閉包。每調(diào)用一次 bind,就在原函數(shù)外面套了一個(gè)“殼子”。

  • 第一次 bind:把原函數(shù) fn 塞進(jìn)一個(gè)包裝盒,并在盒子里寫死 this 指向 obj1。

  • 第二次 bind:是給這個(gè)包裝盒又套了一個(gè)新盒子,試圖改變包裝盒的 this。

  • 最終執(zhí)行:最外層的盒子被調(diào)用,它去調(diào)用里面的盒子,里面的盒子最終調(diào)用最內(nèi)核的 fn。

關(guān)鍵點(diǎn):內(nèi)核的 fn 已經(jīng)被第一個(gè)盒子用 call 或 apply 鎖死了,外層的盒子再怎么折騰,也改不動(dòng)最深層那個(gè) call 的參數(shù)。

var name = 'Global Window';

const obj1 = { name: 'Object_1' };
const obj2 = { name: 'Object_2' };
const obj3 = { name: 'Object_3' };

function fn() {
  console.log(this.name);
}

// ==========================================
// 核心實(shí)驗(yàn):連續(xù)三次綁定
// ==========================================

const bind1 = fn.bind(obj1);
const bind2 = bind1.bind(obj2);
const bind3 = bind2.bind(obj3);

bind3(); 

/* * 【 打印結(jié)果 】:Object_1 
 */

// ==========================================
// 邏輯偽代碼還原:為什么改不動(dòng)?
// ==========================================

// 1. bind1 相當(dāng)于:
const bind1_logic = function(...args) {
  return fn.apply(obj1, args); // <--- 這里已經(jīng)把 obj1 寫死了
};

// 2. bind2 相當(dāng)于:
const bind2_logic = function(...args) {
  // 這里的 this 指向 obj2,但那又怎樣?
  // 它內(nèi)部調(diào)用的是 bind1_logic
  return bind1_logic.apply(obj2, args); 
};

// 3. 執(zhí)行 bind2() 時(shí):
// 調(diào)用 bind2_logic -> 執(zhí)行 bind1_logic.apply(obj2)
// 內(nèi)部執(zhí)行 bind1_logic() -> 執(zhí)行 fn.apply(obj1)
// 最終 fn 看到的 this 永遠(yuǎn)是 obj1

問(wèn):多次 bind 之后,this 指向誰(shuí)?

: 始終指向第一次 bind 綁定的對(duì)象。

原因: * bind 方法返回的是一個(gè)新函數(shù)(閉包)。

  • 當(dāng)我們多次 bind 時(shí),實(shí)際上是多層函數(shù)嵌套。

  • 只有最靠近原函數(shù)的那個(gè) bind(即第一次)是通過(guò) apply/call 真正綁定了原函數(shù)的 this。

  • 后續(xù)的 bind 只是在修改“包裝函數(shù)”的 this,而包裝函數(shù)內(nèi)部執(zhí)行原函數(shù)時(shí),依然使用的是第一次綁定好的 obj1。

追問(wèn):那怎么才能改掉 bind 后的 this?
: 只有一種辦法——使用 new 關(guān)鍵字。因?yàn)?new 綁定的優(yōu)先級(jí)高于 bind 綁定(如果是普通函數(shù)而非箭頭函數(shù)的話)。

2. new 的優(yōu)先級(jí)最高:

通過(guò) bind 綁定的函數(shù),如果被當(dāng)做構(gòu)造函數(shù)使用 new 調(diào)用,原本綁定的 this 會(huì)失效。

總結(jié)

到此這篇關(guān)于JavaScript中this指向、bind、call、apply、知識(shí)點(diǎn)與相關(guān)面試題匯總的文章就介紹到這了,更多相關(guān)JS this指向、bind、call、apply內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

陇南市| 重庆市| 丰顺县| 蓬安县| 武安市| 东兴市| 盐边县| 永年县| 聂拉木县| 长沙县| 屏东市| 东海县| 五家渠市| 田阳县| 大兴区| 吴堡县| 辽源市| 新绛县| 屏山县| 江山市| 万盛区| 南投市| 吴江市| 乌拉特后旗| 淄博市| 阿鲁科尔沁旗| 长宁县| 竹山县| 利津县| 信宜市| 衢州市| 安平县| 彭泽县| 威海市| 星子县| 锦屏县| 墨玉县| 清苑县| 塘沽区| 子洲县| 新源县|