面試官問:能否模擬實現JS的call和apply方法

2021-02-21 若川視野

寫於2018年11月30日,發布在掘金上閱讀量近一萬,現在發布到微信公眾號申明原創。相對比較基礎的知識,雖然日常開發可能用得比較少,各種源碼中有很多call和apply,需要掌握。

前言

這是面試官問系列的第三篇,旨在幫助讀者提升JS基礎知識,包含new、call、apply、this、繼承相關知識。
面試官問系列文章如下:感興趣的讀者可以點擊閱讀。
1.面試官問:能否模擬實現JS的new操作符
2.面試官問:能否模擬實現JS的bind方法
3.面試官問:能否模擬實現JS的call和apply方法
4.面試官問:JS的this指向
5.面試官問:JS的繼承

之前寫過兩篇《面試官問:能否模擬實現JS的new操作符》和《面試官問:能否模擬實現JS的bind方法》

其中模擬bind方法時是使用的call和apply修改this指向。但面試官可能問:能否不用call和apply來實現呢。意思也就是需要模擬實現call和apply的了。

附上之前寫文章寫過的一段話:已經有很多模擬實現call和apply的文章,為什麼自己還要寫一遍呢。學習就好比是座大山,人們沿著不同的路登山,分享著自己看到的風景。你不一定能看到別人看到的風景,體會到別人的心情。只有自己去登山,才能看到不一樣的風景,體會才更加深刻。

先通過MDN認識下call和apply

MDN 文檔:Function.prototype.call()
語法

fun.call(thisArg, arg1, arg2, ...)

thisArg
在fun函數運行時指定的this值。需要注意的是,指定的this值並不一定是該函數執行時真正的this值,如果這個函數處於非嚴格模式下,則指定為null和undefined的this值會自動指向全局對象(瀏覽器中就是window對象),同時值為原始值(數字,字符串,布爾值)的this會指向該原始值的自動包裝對象。
arg1, arg2, ...
指定的參數列表
返回值
返回值是你調用的方法的返回值,若該方法沒有返回值,則返回undefined。

MDN 文檔:Function.prototype.apply()

func.apply(thisArg, [argsArray])

thisArg
可選的。在 func 函數運行時使用的 this 值。請注意,this可能不是該方法看到的實際值:如果這個函數處於非嚴格模式下,則指定為 null 或 undefined 時會自動替換為指向全局對象,原始值會被包裝。
argsArray
可選的。一個數組或者類數組對象,其中的數組元素將作為單獨的參數傳給 func 函數。如果該參數的值為 null 或 undefined,則表示不需要傳入任何參數。從ECMAScript 5 開始可以使用類數組對象。
返回值
調用有指定this值和參數的函數的結果。直接先看例子1

call 和 apply 的異同

相同點:
1、call和apply的第一個參數thisArg,都是func運行時指定的this。而且,this可能不是該方法看到的實際值:如果這個函數處於非嚴格模式下,則指定為 null 或 undefined 時會自動替換為指向全局對象,原始值會被包裝。
2、都可以只傳遞一個參數。
不同點:apply只接收兩個參數,第二個參數可以是數組也可以是類數組,其實也可以是對象,後續的參數忽略不計。call接收第二個及以後一系列的參數。
看兩個簡單例子1和2**:

// 例子1:瀏覽器環境 非嚴格模式下var doSth = function(a, b){
console.log(this);
console.log([a, b]);
}
doSth.apply(null, [1, 2]); // this是window // [1, 2]
doSth.apply(0, [1, 2]); // this 是 Number(0) // [1, 2]
doSth.apply(true); // this 是 Boolean(true) // [undefined, undefined]
doSth.call(undefined, 1, 2); // this 是 window // [1, 2]
doSth.call('0', 1, {a: 1}); // this 是 String('0') // [1, {a: 1}]

// 例子2:瀏覽器環境 嚴格模式下'use strict';
var doSth2 = function(a, b){
console.log(this);
console.log([a, b]);
}
doSth2.call(0, 1, 2); // this 是 0 // [1, 2]
doSth2.apply('1'); // this 是 '1' // [undefined, undefined]
doSth2.apply(null, [1, 2]); // this 是 null // [1, 2]

typeof 有7種類型(undefined number string boolean symbol object function),筆者都驗證了一遍:更加驗證了相同點第一點,嚴格模式下,函數的this值就是call和apply的第一個參數thisArg,非嚴格模式下,thisArg值被指定為 null 或 undefined 時this值會自動替換為指向全局對象,原始值則會被自動包裝,也就是new Object()

重新認識了call和apply會發現:它們作用都是一樣的,改變函數裡的this指向為第一個參數thisArg,如果明確有多少參數,那可以用call,不明確則可以使用apply。也就是說完全可以不使用call,而使用apply代替。
也就是說,我們只需要模擬實現apply,call可以根據參數個數都放在一個數組中,給到apply即可。

模擬實現 apply

既然準備模擬實現apply,那先得看看ES5規範。ES5規範 英文版,ES5規範 中文版。apply的規範下一個就是call的規範,可以點擊打開新標籤頁去查看,這裡摘抄一部分。

Function.prototype.apply (thisArg, argArray)
當以 thisArg 和 argArray 為參數在一個 func 對象上調用 apply 方法,採用如下步驟:

1.如果 IsCallable(func) 是 false, 則拋出一個 TypeError 異常。
2.如果 argArray 是 null 或 undefined, 則返回提供 thisArg 作為 this 值並以空參數列表調用 func 的 [[Call]] 內部方法的結果。
3.返回提供 thisArg 作為 this 值並以空參數列表調用 func 的 [[Call]] 內部方法的結果。
4.如果 Type(argArray) 不是 Object, 則拋出一個 TypeError 異常。
5~8 略
9.提供 thisArg 作為 this 值並以 argList 作為參數列表,調用 func 的 [[Call]] 內部方法,返回結果。
apply 方法的 length 屬性是 2。

在外面傳入的 thisArg 值會修改並成為 this 值。thisArg 是 undefined 或 null 時它會被替換成全局對象,所有其他值會被應用 ToObject 並將結果作為 this 值,這是第三版引入的更改。

結合上文和規範,如何將函數裡的this指向第一個參數thisArg呢,這是一個問題。這時候請出例子3

// 瀏覽器環境 非嚴格模式下var doSth = function(a, b){
console.log(this);
console.log(this.name);
console.log([a, b]);
}
var student = {
name: '若川',
doSth: doSth,
};
student.doSth(1, 2); // this === student // true // '若川' // [1, 2]
doSth.apply(student, [1, 2]); // this === student // true // '若川' // [1, 2]

可以得出結論1:在對象student上加一個函數doSth,再執行這個函數,這個函數裡的this就指向了這個對象。那也就是可以在thisArg上新增調用函數,執行後刪除這個函數即可。知道這些後,我們試著容易實現第一版本:

// 瀏覽器環境 非嚴格模式function getGlobalObject(){
returnthis;
}
Function.prototype.applyFn = function apply(thisArg, argsArray){ // `apply` 方法的 `length` 屬性是 `2`。// 1.如果 `IsCallable(func)` 是 `false`, 則拋出一個 `TypeError` 異常。if(typeofthis !== 'function'){
thrownewTypeError(this + ' is not a function');
}

// 2.如果 argArray 是 null 或 undefined, 則// 返回提供 thisArg 作為 this 值並以空參數列表調用 func 的 [[Call]] 內部方法的結果。if(typeof argsArray === 'undefined' || argsArray === null){
argsArray = [];
}

// 3.如果 Type(argArray) 不是 Object, 則拋出一個 TypeError 異常 .if(argsArray !== newObject(argsArray)){
thrownewTypeError('CreateListFromArrayLike called on non-object');
}

if(typeof thisArg === 'undefined' || thisArg === null){
// 在外面傳入的 thisArg 值會修改並成為 this 值。// ES3: thisArg 是 undefined 或 null 時它會被替換成全局對象 瀏覽器裡是window
thisArg = getGlobalObject();
}

// ES3: 所有其他值會被應用 ToObject 並將結果作為 this 值,這是第三版引入的更改。
thisArg = newObject(thisArg);
var __fn = '__fn';
thisArg[__fn] = this;
// 9.提供 thisArg 作為 this 值並以 argList 作為參數列表,調用 func 的 [[Call]] 內部方法,返回結果var result = thisArg[__fn](...argsArray);
delete thisArg[__fn];
return result;
};

實現第一版後,很容易找出兩個問題:[ ] 1.__fn 同名覆蓋問題,thisArg對象上有__fn,那就被覆蓋了然後被刪除了。

針對問題1 解決方案一:採用ES6 Sybmol() 獨一無二的。可以本來就是模擬ES3的方法。如果面試官不允許用呢。解決方案二:自己用Math.random()模擬實現獨一無二的key。面試時可以直接用生成時間戳即可。

// 生成UUID 通用唯一識別碼// 大概生成 這樣一串 '18efca2d-6e25-42bf-a636-30b8f9f2de09'function generateUUID(){
var i, random;
var uuid = '';
for (i = 0; i < 32; i++) {
random = Math.random() * 16 | 0;
if (i === 8 || i === 12 || i === 16 || i === 20) {
uuid += '-';
}
uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random))
.toString(16);
}
return uuid;
}
// 簡單實現// '__' + new Date().getTime();

如果這個key萬一這對象中還是有,為了保險起見,可以做一次緩存操作。比如如下代碼:

var student = {
name: '若川',
doSth: 'doSth',
};
var originalVal = student.doSth;
var hasOriginalVal = student.hasOwnProperty('doSth');
student.doSth = function(){};
delete student.doSth;
// 如果沒有,`originalVal`則為undefined,直接賦值新增了一個undefined,這是不對的,所以需判斷一下。if(hasOriginalVal){
student.doSth = originalVal;
}
console.log('student:', student); // { name: '若川', doSth: 'doSth' }

[ ] 2.使用了ES6擴展符...
解決方案一:採用eval來執行函數。

eval把字符串解析成代碼執行。
MDN 文檔:eval
語法

eval(string)

參數
string
表示JavaScript表達式,語句或一系列語句的字符串。表達式可以包含變量以及已存在對象的屬性。
返回值
執行指定代碼之後的返回值。如果返回值為空,返回undefined
解決方案二:但萬一面試官不允許用eval呢,畢竟eval是魔鬼。可以採用new Function()來生成執行函數。MDN 文檔:Function
語法

newFunction ([arg1[, arg2[, ...argN]],] functionBody)

參數
arg1, arg2, ... argN
被函數使用的參數的名稱必須是合法命名的。參數名稱是一個有效的JavaScript標識符的字符串,或者一個用逗號分隔的有效字符串的列表;例如「×」,「theValue」,或「A,B」。
functionBody
一個含有包括函數定義的JavaScript語句的字符串。
接下來看兩個例子:

簡單例子:
var sum = newFunction('a', 'b', 'return a + b');
console.log(sum(2, 6));

// 稍微複雜點的例子:var student = {
name: '若川',
doSth: function(argsArray){
console.log(argsArray);
console.log(this.name);
}
};
// var result = student.doSth(['若川i', 18]);// 用new Function()生成函數並執行返回結果var result = newFunction('return arguments[0][arguments[1]](arguments[2][0], arguments[2][1])')(student, 'doSth', ['若川i', 18]);
// 個數不定// 所以可以寫一個函數生成函數代碼:function generateFunctionCode(argsArrayLength){
var code = 'return arguments[0][arguments[1]](';
for(var i = 0; i < argsArrayLength; i++){
if(i > 0){
code += ',';
}
code += 'arguments[2][' + i + ']';
}
code += ')';
// return arguments[0][arguments[1]](arg1, arg2, arg3...)return code;
}

你可能不知道在ES3、ES5中 undefined 是能修改的

可能大部分人不知道。ES5中雖然在全局作用域下不能修改,但在局部作用域中也是能修改的,不信可以複製以下測試代碼在控制臺執行下。雖然一般情況下是不會的去修改它。

function test(){
varundefined = 3;
console.log(undefined); // chrome下也是 3
}
test();

所以判斷一個變量a是不是undefined,更嚴謹的方案是typeof a === 'undefined'或者a === void 0; 這裡面用的是void,void的作用是計算表達式,始終返回undefined,也可以這樣寫void(0)。更多可以查看韓子遲的這篇文章:為什麼用「void 0」代替「undefined」 解決了這幾個問題,比較容易實現如下代碼。

使用 new Function() 模擬實現的apply
// 瀏覽器環境 非嚴格模式function getGlobalObject(){
returnthis;
}
function generateFunctionCode(argsArrayLength){
var code = 'return arguments[0][arguments[1]](';
for(var i = 0; i < argsArrayLength; i++){
if(i > 0){
code += ',';
}
code += 'arguments[2][' + i + ']';
}
code += ')';
// return arguments[0][arguments[1]](arg1, arg2, arg3...)return code;
}
Function.prototype.applyFn = function apply(thisArg, argsArray){ // `apply` 方法的 `length` 屬性是 `2`。// 1.如果 `IsCallable(func)` 是 `false`, 則拋出一個 `TypeError` 異常。if(typeofthis !== 'function'){
thrownewTypeError(this + ' is not a function');
}
// 2.如果 argArray 是 null 或 undefined, 則// 返回提供 thisArg 作為 this 值並以空參數列表調用 func 的 [[Call]] 內部方法的結果。if(typeof argsArray === 'undefined' || argsArray === null){
argsArray = [];
}
// 3.如果 Type(argArray) 不是 Object, 則拋出一個 TypeError 異常 .if(argsArray !== newObject(argsArray)){
thrownewTypeError('CreateListFromArrayLike called on non-object');
}
if(typeof thisArg === 'undefined' || thisArg === null){
// 在外面傳入的 thisArg 值會修改並成為 this 值。// ES3: thisArg 是 undefined 或 null 時它會被替換成全局對象 瀏覽器裡是window
thisArg = getGlobalObject();
}
// ES3: 所有其他值會被應用 ToObject 並將結果作為 this 值,這是第三版引入的更改。
thisArg = newObject(thisArg);
var __fn = '__' + newDate().getTime();
// 萬一還是有 先存儲一份,刪除後,再恢復該值var originalVal = thisArg[__fn];
// 是否有原始值var hasOriginalVal = thisArg.hasOwnProperty(__fn);
thisArg[__fn] = this;
// 9.提供 `thisArg` 作為 `this` 值並以 `argList` 作為參數列表,調用 `func` 的 `[[Call]]` 內部方法,返回結果。// ES6版// var result = thisArg[__fn](...args);var code = generateFunctionCode(argsArray.length);
var result = (newFunction(code))(thisArg, __fn, argsArray);
delete thisArg[__fn];
if(hasOriginalVal){
thisArg[__fn] = originalVal;
}
return result;
};

利用模擬實現的apply模擬實現call
Function.prototype.callFn = function call(thisArg){
var argsArray = [];
var argumentsLength = arguments.length;
for(var i = 0; i < argumentsLength - 1; i++){
// argsArray.push(arguments[i + 1]);
argsArray[i] = arguments[i + 1];
}
console.log('argsArray:', argsArray);
returnthis.applyFn(thisArg, argsArray);
}
// 測試例子var doSth = function (name, age){
var type = Object.prototype.toString.call(this);
console.log(typeof doSth);
console.log(this === firstArg);
console.log('type:', type);
console.log('this:', this);
console.log('args:', [name, age], arguments);
return'this--';
};

var name = 'window';

var student = {
name: '若川',
age: 18,
doSth: 'doSth',
__fn: 'doSth',
};
var firstArg = student;
var result = doSth.applyFn(firstArg, [1, {name: '若川i'}]);
var result2 = doSth.callFn(firstArg, 1, {name: '若川i'});
console.log('result:', result);
console.log('result2:', result2);

細心的你會發現注釋了這一句argsArray.push(arguments[i + 1]);,事實上push方法,內部也有一層循環。所以理論上不使用push性能會更好些。面試官也可能根據這點來問時間複雜度和空間複雜度的問題。

// 看看V8引擎中的具體實現:function ArrayPush() {
var n = TO_UINT32( this.length ); // 被push的對象的lengthvar m = %_ArgumentsLength(); // push的參數個數for (var i = 0; i < m; i++) {
this[ i + n ] = %_Arguments( i ); // 複製元素 (1)
}
this.length = n + m; // 修正length屬性的值 (2)returnthis.length;
};

行文至此,就基本結束了,你可能還發現就是寫的非嚴格模式下,thisArg原始值會包裝成對象,添加函數並執行,再刪除。而嚴格模式下還是原始值這個沒有實現,而且萬一這個對象是凍結對象呢,Object.freeze({}),是無法在這個對象上添加屬性的。所以這個方法只能算是非嚴格模式下的簡版實現。最後來總結一下。

總結

通過MDN認識call和apply,閱讀ES5規範,到模擬實現apply,再實現call。
就是使用在對象上添加調用apply的函數執行,這時的調用函數的this就指向了這個thisArg,再返回結果。引出了ES6 Symbol,ES6的擴展符...、eval、new Function(),嚴格模式等。
事實上,現實業務場景不需要去模擬實現call和apply,畢竟是ES3就提供的方法。但面試官可以通過這個面試題考察候選人很多基礎知識。如:call、apply的使用。ES6 Symbol,ES6的擴展符...,eval,new Function(),嚴格模式,甚至時間複雜度和空間複雜度等。
讀者發現有不妥或可改善之處,歡迎指出。另外覺得寫得不錯,可以點個讚,也是對筆者的一種支持。

// 最終版版 刪除注釋版,詳細注釋看文章// 瀏覽器環境 非嚴格模式function getGlobalObject(){
returnthis;
}
function generateFunctionCode(argsArrayLength){
var code = 'return arguments[0][arguments[1]](';
for(var i = 0; i < argsArrayLength; i++){
if(i > 0){
code += ',';
}
code += 'arguments[2][' + i + ']';
}
code += ')';
return code;
}
Function.prototype.applyFn = function apply(thisArg, argsArray){
if(typeofthis !== 'function'){
thrownewTypeError(this + ' is not a function');
}
if(typeof argsArray === 'undefined' || argsArray === null){
argsArray = [];
}
if(argsArray !== newObject(argsArray)){
thrownewTypeError('CreateListFromArrayLike called on non-object');
}
if(typeof thisArg === 'undefined' || thisArg === null){
thisArg = getGlobalObject();
}
thisArg = newObject(thisArg);
var __fn = '__' + newDate().getTime();
var originalVal = thisArg[__fn];
var hasOriginalVal = thisArg.hasOwnProperty(__fn);
thisArg[__fn] = this;
var code = generateFunctionCode(argsArray.length);
var result = (newFunction(code))(thisArg, __fn, argsArray);
delete thisArg[__fn];
if(hasOriginalVal){
thisArg[__fn] = originalVal;
}
return result;
};
Function.prototype.callFn = function call(thisArg){
var argsArray = [];
var argumentsLength = arguments.length;
for(var i = 0; i < argumentsLength - 1; i++){
argsArray[i] = arguments[i + 1];
}
returnthis.applyFn(thisArg, argsArray);
}

相關焦點

  • 面試官問:能否模擬實現JS的bind方法(高頻考點)
    可以點擊上方的話題JS基礎系列,查看往期文章寫於2018年11月21日,發布在掘金閱讀量1.3w+前言這是面試官問系列的第二篇,旨在幫助讀者提升JS基礎知識,包含new、call、apply、this、繼承相關知識。面試官問系列文章如下:感興趣的讀者可以點擊閱讀。
  • 面試官問:能否模擬實現JS的new操作符(高頻考點)
    可以點擊上方的話題JS基礎系列,查看往期文章這篇文章寫於2018年11月05日,new模擬實現,Object.create是面試高頻考點,之前發布在掘金有近2萬人閱讀,現在發布到公眾號聲明原創。1.前言這是面試官問系列的第一篇,旨在幫助讀者提升JS基礎知識,包含new、call、apply、this、繼承相關知識。面試官問系列文章如下:感興趣的讀者可以點擊閱讀。
  • JavaScript 深入之call和apply的模擬實現
    一句話介紹 call:call() 方法在使用一個指定的 this 值和若干個指定的參數值的前提下調用某個函數或方法。,指向到 foobar 函數執行了模擬實現第一步那麼我們該怎麼模擬實現這兩個效果呢?
  • JS中apply()和call()方法的區別詳解
    區分apply,call就一句話,  foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3) call, apply都屬於Function.prototype的一個方法,它是JavaScript
  • 高級前端進階,為什麼要使用call、apply、bind
    前言:call、apply、bind這3個方法的用處都是更改this指向,在學習call、apply、bind之前,需要先了解this,所以本文會先對this進行講解。、apply、bind原生實現解析1.callfunction.call(thisArg, arg1, arg2, ...)。
  • call與apply方法的使用
    行內綁定中this指向了全局window對象動態綁定中this指向了當前正在操作的dom對象構造函數也存在一個this,誰調用了這個函數,函數內部的this就指向誰1)call([thisObj[,arg1[,arg2[,argN]]]]) :改變函數內部的this指向參數說明:thisObj:要指向的對象arg1,arg2,arg3…:參數列表,參數與參數之間通過逗號隔開2)apply([thisObj[,argArray
  • 深入淺出 妙用Javascript中apply、call、bind
    ,那麼我們可以通過 call 或apply 用 apple 的 say 方法:banana = { color: "yellow"}apple.say.call(banana); //My color is yellowapple.say.apply(banana); //My color is yellow  所以,可以看出 call 和 apply
  • js 手動實現bind方法,超詳細思路分析!
    說的通俗一點,bind與apply/call一樣都能改變函數this指向,但bind並不會立即執行函數,而是返回一個綁定了this的新函數,你需要再次調用此函數才能達到最終執行。boundFunction的this無法再被修改,使用call、apply也不行。
  • JS中數據類型的判斷
    面試季馬上就要過去了,很多小夥伴也是在努力的抓住金九銀十的小尾巴,今天來說一說面試中常見的一個問題:js中如何判斷數據類型?可能你會回答,我一般用typeof來進行判斷,可是面試官會問你,那如果遇到值為null的情況下typeof會返回什麼?對於初入江湖的新手來說可能在這個時候就敗下陣來,沒錯,我當初也是這樣,判斷數據為null或者undefined不是用if-else來判斷的嗎?
  • 理解this及call,apply和bind的用法
    隱式綁定,顯式綁定,new綁定和window綁定。在介紹這些時,你還將學習一些其他令人困惑的JavaScript部分,例如.call,.apply,.bind和new關鍵字。」前言在深入研究JavaScript中this關鍵字的細節之前,我們先退一步想一想,為什麼this關鍵字存在於第一位。this關鍵字允許你重用具有不同上下文的函數。
  • 手寫Vuex核心原理,再也不怕面試官問我Vuex原理
    手寫Vuex核心原理,再也不怕面試官問我Vuex原理一、核心原理三、剖析Vuex本質四、分析Vue.use五、完善install方法七、實現getter八、實現mutation九、實現actions(~~讓我想起了曾經面試官問我為什麼插件不會被重新加載!!!哭唧唧,現在總算明白了)五、完善install方法 我們前面提到 通過Vue.use(Vuex) 使得每個組件都可以擁有store實例。這是什麼意思呢???
  • Spring框架你敢寫精通,面試官就敢問@Autowired註解的實現原理
    面試官:Spring框架中的@Autowired註解可以標註在哪些地方?小小白:@Autowired註解可以被標註在構造函數、屬性、setter方法或配置方法上,用於實現依賴自動注入。面試官:有沒有研究過@Autowired註解的實現原理?
  • JavaScript學習- js中的typeof/instanceof
    之前我講了js中原型和原型鏈,那衍生出來了一些其他的知識面類型判斷。我主要講三個我們經常用到的,並且在面試中很大機率會被問到的知識。
  • JavaScript深入之bind的模擬實現
    JavaScript深入之bind的模擬實現JavaScript深入系列第十一篇,通過bind函數的模擬實現,帶大家真正了解bind的特性bind一句話介紹 bind:bind() 方法會創建一個新函數。當這個新函數被調用時,bind() 的第一個參數將作為它運行時的 this,之後的一序列參數將會在傳遞的實參前傳入作為它的參數。
  • JavaScript中apply、call、bind的區別與用法
    apply()、call()和bind()方法都是Function.prototype對象中的方法,而所有的函數都是Function的實例。
  • 2020 年前端面試複習必讀文章【超三百篇】
    深入之從ECMAScript規範解讀this: https://github.com/mqyqingfeng/Blog/issues/7[12]前端基礎進階(七):全方位解讀this: https://www.jianshu.com/p/d647aa6d1ae6[13]面試官問:JS的this指向: https://juejin.im/post/5c0c87b35188252e8966c78a
  • 2020年底面試題
    面試結果先說下面試結果:猿輔導、貝殼10月份拿到offer,但沒去,後來問了下可以去,但是薪資不調整,遂over,猿輔導的面試還是比較有技術含量的,當時其實想走了,後來想想等等年終獎吧,就沒去騰訊二面掛,難度適中。其實是有希望過,只是面的時候發現不是自己想去的部門,面試官說你可想好。。
  • JS 面試之數組騷操作
    上面這些應用場景你可以用一行代碼實現嗎?concat實現扁平。3.排序1.終極篇 [1,2,3,4].sort(); // [1, 2,3,4],默認是升序 [1,2,3,4].sort((a, b) => b - a); // [4,3,2,1] 降序sort是js內置的排序方法,參數為一個函數2.開始篇
  • js中對函數的深入理解(下)
    大家都知道,函數是對象,所以每個函數都有自己的屬性和方法,例如length和prototype屬性,length屬性表示函數接收的命名參數的個數;其中,最讓人難以理解的要數prototype屬性了,它保存了所有對象的實例方法和屬性。
  • 15 個常見的 Node.js 面試問題及答案
    對於成功的編程面試來說,準備和知識面一樣重要。準備使你有信心參加面試,而不用擔心莫名的緊張情緒。如果第一次參加編程面試,這一點尤其重要。為幫助 Node.js 開發人員更好的面試,我列出了 15 個常見的 Node.js 和網絡開發相關的面試問題。在本文中,我們將重點討論 Node.js 相關問題。