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

JavaScript中this指向、bind、call、apply、知識點與相關面試題匯總

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

一、JS中 this 究竟指向誰?

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

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

1. this 的值在何時確定?

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

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

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

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

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

① 默認綁定 (Default Binding)

當函數(shù)作為獨立調用(不帶任何修飾的函數(shù)調用)時,this 指向全局對象。

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

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

② 隱式綁定 (Implicit Binding)

當函數(shù)作為某個對象的方法被調用時,this 指向該對象。

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

③ 顯式綁定 (Explicit Binding)

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

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

  • bind:返回一個硬綁定了 this 的新函數(shù),之后無論怎么調用,this 都不會再變。

④ new 綁定 (new Binding)

當使用 new 關鍵字調用構造函數(shù)時,JavaScript 內部會創(chuàng)建一個新對象,并將 this 指向這個新創(chuàng)建的實例對象。

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

箭頭函數(shù)是 this 規(guī)則中的“特例”,因為它沒有自己的 this。

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

4. 如何判斷復雜場景下的指向?

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

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

兩個特殊檢查點:

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

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

總結公式

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

二、js中 this 常見面試題

1. 默認綁定與隱式綁定(基礎陷阱)

這是最常見的題型,考察你是否清楚“誰調用指向誰”。

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

const bare = obj.getName;

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

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

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

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

箭頭函數(shù)是面試官最喜歡用來對比 this 的工具,因為它沒有自己的 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 取決于它定義時所在的外層作用域。這里外層是全局環(huán)境(不是 obj 的花括號),所以指向 window。輸出:“Window”。

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

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

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

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”):重點!new 綁定的優(yōu)先級高于 bind。即使函數(shù)被綁定到了 obj1,使用 new 時依然會創(chuàng)建一個新對象,并將 this 指向這個新對象。

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

4. 定時器 + 閉包

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ù)傳遞后直接調用,屬于默認綁定。輸出:10。

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

?? 總結:this 指向判斷準則

你可以按照這個優(yōu)先級順序來快速判斷:

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

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

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

  • 默認綁定:如果以上都不是,在非嚴格模式下指向 window,嚴格模式下是 undefined。

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

三、 call、apply、bind 核心知識點匯總

1. 核心異同對比表

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

2. 為什么設計它們?(優(yōu)點與場景)

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

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

① 模擬實現(xiàn)call

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

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

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

  // 【3】通過對象來調用這個函數(shù)
  // 關鍵:一旦用 context.xxx() 調用,函數(shù)內部的 this 就會指向 context
  const result = context[fnKey](...args);

  // 【4】任務完成,把剛才臨時加進去的方法刪掉,保持對象原貌
  delete context[fnKey];

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

② 模擬實現(xiàn) apply

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

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

③ 模擬實現(xiàn) bind (進階版)

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

Function.prototype.myBind = function(context, ...args) {
  const self = this; // 保存原函數(shù)
  
  const bound = function(...nextArgs) {
    // 如果是通過 new 調用的,this 應指向實例(此時忽略 bind 傳入的 context)
    const isNew = this instanceof bound;
    return self.apply(isNew ? this : context, args.concat(nextArgs));
  };
  
  // 維護原型鏈(讓實例能繼承原函數(shù) prototype 上的屬性)
  bound.prototype = Object.create(self.prototype);
  
  return bound;
};

四、call、apply、bind 面試坑點

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

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

核心結論:包裝盒效應
bind 的本質是閉包。每調用一次 bind,就在原函數(shù)外面套了一個“殼子”。

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

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

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

關鍵點:內核的 fn 已經被第一個盒子用 call 或 apply 鎖死了,外層的盒子再怎么折騰,也改不動最深層那個 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);
}

// ==========================================
// 核心實驗:連續(xù)三次綁定
// ==========================================

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

bind3(); 

/* * 【 打印結果 】:Object_1 
 */

// ==========================================
// 邏輯偽代碼還原:為什么改不動?
// ==========================================

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

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

// 3. 執(zhí)行 bind2() 時:
// 調用 bind2_logic -> 執(zhí)行 bind1_logic.apply(obj2)
// 內部執(zhí)行 bind1_logic() -> 執(zhí)行 fn.apply(obj1)
// 最終 fn 看到的 this 永遠是 obj1

:多次 bind 之后,this 指向誰?

: 始終指向第一次 bind 綁定的對象。

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

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

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

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

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

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

通過 bind 綁定的函數(shù),如果被當做構造函數(shù)使用 new 調用,原本綁定的 this 會失效。

總結

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

相關文章

最新評論

夏津县| 珠海市| 资兴市| 枝江市| 青冈县| 长乐市| 周至县| 渝中区| 綦江县| 万载县| 萨嘎县| 怀来县| 通城县| 奈曼旗| 宜城市| 高州市| 水富县| 噶尔县| 桃园市| 淮阳县| 清流县| 柳江县| 策勒县| 黄浦区| 巴彦县| 铁岭县| 西城区| 马尔康县| 廊坊市| 图们市| 锦州市| 瓦房店市| 临江市| 临洮县| 普安县| 陆河县| 万荣县| 灌阳县| 寿阳县| 庆安县| 柘城县|