自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

前端面試題:Call的用法及實現(xiàn)

開發(fā) 前端
手寫 Call,核心在于通過另一種修改 this 指向的方式:Obj.fn() 執(zhí)行時 This 會指向Obj 對象。

大家好,我是前端西瓜哥。

我之前寫了一篇手寫 bind 的文章,里面直接使用了原生 call 方法。

有讀者說他面試的時候這個 call 也要求自己實現(xiàn)的。

那我們今天來手寫 call。apply 的實現(xiàn)也是一樣,只是調(diào)用形式有點區(qū)別。

call 的用法

我們先看看 Function.prototype.call() 的用法。

call() 可以修改函數(shù)調(diào)用時 this 的指向,其余參數(shù)則會作為原函數(shù)的參數(shù)。

call 接收的參數(shù):

  1. 第一個參數(shù) thisArg。代表 this 將會被指向的值。如果不是對象,也會通過 Object() 方法轉(zhuǎn)換為對象。如果是 null 或 undefined,this 則會指向全局對象(即 window 或 global),或在嚴格模式("use strict;")下,保持 undefined 或 null;
  2. 其余參數(shù)。第二個往后的參數(shù)則會傳入到原函數(shù)中。

例子:

function sum(num1, num2) {
return this.val + num1 + num2;
}
const obj = { val: 1 };
sum.call(obj, 2, 3); // 6

上面代碼中,this 指向了 obj。

Function.prototype.apply 也是類似,但它的參數(shù)是以數(shù)組的形式存在的。上面的 call 寫法等價于:

sum.call(obj, [2, 3]);

call 的實現(xiàn)

JS 函數(shù)中的 this 指向是在運行時決定的,里面的規(guī)則比較多,但其中有一條是:

如果是通過 obj.fn() 執(zhí)行時,this 會指向前面的 obj 對象。

那我們只要將傳入對象和原方法進行拼接,拼成上面這個 對象.方法 的形式,執(zhí)行時,this 就能乖乖指向我們傳入的 thisArg 了。

實現(xiàn)如下:

Function.prototype.myCall = function(thisArg, ...args) {
const context = Object(thisArg) || window;
// 構(gòu)造唯一 key
const fn = Symbol();
// 組裝成"對象.方法"形式并調(diào)用,來改變 this
context[fn] = this;
const ret = context[fn](...args);
// 刪掉臨時加的 key,復(fù)原 thisArg
delete context[fn];
return ret;
}

這里我們用 Symbol() 創(chuàng)建了一個唯一的 key,是為了防止覆蓋掉 thisArg 原有的同名屬性。

執(zhí)行完后,記得將這個 key 移除掉,防止污染 thisArg 對象。

如果面試官要你用 ES5 實現(xiàn),那會復(fù)雜很多,我這里也給出實現(xiàn)吧。

在這之前,我們先來學(xué)點前置知識。

判斷是否為嚴格模式

var strict = (function() { return !this })();

利用了嚴格模式下,如果沒有指定 this(通過 bind、call、前面帶對象等方式),就會得到 undefined 的機制。如果是非嚴格模式,this 會拿到全局變量。

fn(...args) 的 ES5 實現(xiàn)

ES6 的擴展運算符 ... 能夠?qū)?shù)組 args,進行拆分按順序放到函數(shù)中。

const args = [4, 5, 6];
fn(...args);
// 等價于
fn(4, 5, 6);

那我們用 ES5,也能將數(shù)組拆分成一個參數(shù)塞到函數(shù)中嗎?

可以,但我們要用一點奇技淫巧:Function 方法。

Function 方法用得比較少。它可以在運行時創(chuàng)建一個函數(shù),最后一個參數(shù)是函數(shù)體內(nèi)容,前面的參數(shù)則是函數(shù)的參數(shù)。

const sum = new Function('a', 'b', 'return a + b');
sum(2, 6) // 8

fn(...args) 的 ES5 實現(xiàn)為:

function construct(fn, args) {
var list = [];
for (var i = 0; i < args.length; i++) {
list[i] = 'a[' + i + ']';
}
var f = new Function('fn', 'a', 'return fn(' + list.join(', ') + ')');
return f(fn, args);
}

Function 方法可以根據(jù)參數(shù)長度,動態(tài)生成 new Function('fn', 'a', 'return fn(a[0], a[1])') 形式的函數(shù),來實現(xiàn)類似擴展運算符的效果。

還有種寫法是用 eval,也能根據(jù)字符串動態(tài)生成可執(zhí)行代碼。

function construct(fn, a) {
var list = [];
for (var i = 0; i < a.length; i++) {
list[i] = 'a[' + i + ']';
}
return eval('fn(' + list.join(', ') + ')');
}

但這種封裝成一個函數(shù)的寫法,會有 this 隱式丟失問題。比如執(zhí)行 construct(dog.bark, ['bark!']),執(zhí)行時 this 將不再指向?qū)ο?dog。

關(guān)于 this 的指向問題還是比較復(fù)雜的,以后我會專門寫一篇文章來講解 this。

call 的 ES5 實現(xiàn)

Function.prototype.myCall = function(thisArg/*, ...args */) {
var context = Object(thisArg) || window;
context.fn = this;
// 偷懶用了 Array.prototype.slice + 原生 call
// 請讀者自行實現(xiàn) slice
var a = Array.prototype.slice.call(arguments, 1);
var list = [];
for (var i = 0; i < a.length; i++) {
list[i] = 'a[' + i + ']';
}
var ret = eval('context.fn(' + list.join(',') + ')');
delete context.fn; // 復(fù)原
return ret;
}

為了不被干擾,上面的代碼實現(xiàn) 忽略掉了一些細節(jié)。

  • 這里我沒有用前面實現(xiàn)的 construct 方法,因為會丟失 this,所以直接用了 eval。
  • slice 請自行實現(xiàn),不能用 Array.prototype.slice.call,因為用了原生的 call。
  • 我們用了一個字符串 'fn'來臨時掛載函數(shù),可能會和 thisArg 上的屬性名沖突,但 ES5 又不能用 Symbol,這種情況下。更好的做法是生成一個隨機的長字符串,用hasOwnProperty判斷對象是否存在該屬性,如果不存在就使用它。
  • this 不可調(diào)用時(即不是函數(shù)時),要拋出錯誤。

另外我的實現(xiàn),沒有考慮嚴格模式。嚴格模式下,如果 thisArg 是 undefined 或 null,直接執(zhí)行原函數(shù)就行了,不需要拼裝成 obj.fn 形式。

結(jié)尾

手寫 call,核心在于通過另一種修改 this 指向的方式:obj.fn() 執(zhí)行時 this 會指向 obj 對象。

手寫 apply 也是一樣的邏輯,還能少寫一個 slice 方法。

責任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2023-08-27 15:57:28

前端開發(fā)

2022-02-09 07:40:42

JavaScript前端面試題

2022-01-18 08:16:52

Web 前端JavaScript

2022-07-08 08:21:26

JSbind 方法

2023-05-19 08:21:40

MarginCSS

2019-02-21 14:12:26

前端面試題Vue

2021-02-02 06:12:39

JavaScript 前端面試題

2018-03-08 18:40:47

Java百度面試題

2017-09-06 09:13:24

2023-12-12 07:40:52

JavaScript面試題前端

2021-03-15 09:53:37

計算機網(wǎng)絡(luò)面試題

2015-07-23 14:13:43

前端開發(fā)面試題

2023-04-27 09:08:19

JavaScript隱式類型轉(zhuǎn)換

2020-11-06 09:05:18

前端web開發(fā)

2012-06-26 11:09:07

Web

2011-05-19 16:30:38

軟件測試

2009-06-16 13:41:19

Hibernate面試Hibernate面試

2024-02-26 15:35:44

2020-06-04 14:40:40

面試題Vue前端

2014-09-19 11:17:48

面試題
點贊
收藏

51CTO技術(shù)棧公眾號