前言
極度投入,深度沉浸,邊界清晰
前端小菜雞一枚,分享的文章純屬個人見解,若有不正確或可待討論點可隨意評論,與各位同學一起學習~
歡迎關注
『前端進階圈』公眾號 ,一起探索學習前端技術......公眾號回覆
加羣或掃碼, 即可加入前端交流學習羣,長期交流學習......公眾號回覆
加好友,即可添加為好友
熱點面試題:JS 中 call, apply, bind 概念、用法、區別及實現?
概念:
function.call(thisArg, arg1, arg2, ...)function.apply(thisArg, [arg1, arg2, ...])function.bind(thisArg, arg1, arg2, ...)- 三者都是改變
this指向,通過一個參數或多個參數來調用一個函數的。
用法:
let obj = {
name: "哈哈",
sayName: function () {
console.log("sayName", this.name);
return this.name;
},
eat: function (food1, food2) {
console.log("eat", food1, food2);
},
};
let obj2 = {
name: "是的",
};
obj.sayName.call(obj2); // sayName 是的
obj.eat.call(obj2, "魚", "肉"); // eat 魚 肉
obj.eat.apply(obj2, ["魚", "肉"]); // e at 魚 肉
obj.eat.bind(obj2, "魚", "肉"); // 不會調用,需要一個結果來接收
let res = obj.eat.bind(obj2, "魚", "肉");
res(); // eat 魚 肉
區別:
-
call 與 bind 的區別?
call會直接調用,而bind會創建一個新的函數作為一個返回值進行調用, 而其餘參數將作為新函數的參數,供調用時使用
-
call 與 apply 的區別?
- 主要區別在第二個參數中,
call接受的是一個參數列表,也就是一個個參數,而apply接受的是一個包含多個參數的數組
- 主要區別在第二個參數中,
實現:
-
function.call(thisArg, arg1, arg2, ...)Function.prototype.myCall = function (context, ...args) { // 條件判斷,判斷當前調用的對象是否為函數, if (Object.prototype.toString.call(this).slice(8, -1) != "Function") throw new Error("type error"); // 判斷傳入上下文對象是否存在,如果不存在,則設置為 window if (!context || context === null) context = window; // 創建唯一的 key 值,作為構建的 context 內部方法名 let fn = Symbol(); // 將 this 指向調用的 call 函數 context[fn] = this; // 執行函數並返回結果 === 把自身作為傳入的 context 的方法進行調用 return context[fn](...args); }; let obj = { name: "哈哈", sayName: function () { console.log("sayName", this.name); return this.name; }, eat: function (food1, food2) { console.log("eat", food1, food2); }, }; let obj2 = { name: "是的", }; obj.sayName.myCall(obj2); -
function.apply(thisArg, [arg1, arg2, ...])Function.prototype.MyApply = function (context, args) { // 條件判斷,判斷當前調用的對象是否為函數, if (Object.prototype.toString.call(this).slice(8, -1) != "Function") throw new Error("type error"); // 判斷傳入上下文對象是否存在,如果不存在,則設置為 window if (!context || context === null) context = window; // 創建唯一的 key 值,作為構建的 context 內部方法名 let fn = Symbol(); // 將 this 指向調用的 call 函數 context[fn] = this; // 執行函數並返回結果 === 把自身作為傳入的 context 的方法進行調用 return context[fn](...args); }; let obj = { name: "哈哈", sayName: function () { console.log("sayName", this.name); return this.name; }, eat: function (food1, food2) { console.log("eat", food1, food2); }, }; let obj2 = { name: "是的", }; obj.sayName.MyApply(obj2, []); -
function.bind(thisArg, arg1, arg2, ...)Function.prototype.myBind = function (context, ...args) { if (!context || context === null) { context = window; } // 創造唯一的key值 作為我們構造的context內部方法名 let fn = Symbol(); context[fn] = this; let _this = this; // bind情況要複雜一點 const result = function (...innerArgs) { // 第一種情況: 若是將 bind 綁定之後的函數當作構造函數,通過 new 操作符使用,則不綁定傳入的 this,而是將 this 指向實例化出來的對象 // 此時由於new操作符作用 this指向result實例對象 而result又繼承自傳入的_this 根據原型鏈知識可得出以下結論 // this.__proto__ === result.prototype //this instanceof result =>true // this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype; //this instanceof _this =>true if (this instanceof _this === true) { // 此時this指向指向result的實例 這時候不需要改變this指向 this[fn] = _this; this[fn](...[...args, ...innerArgs]); //這裏使用es6的方法讓bind支持參數合併 } else { // 如果只是作為普通函數調用 那就很簡單了 直接改變this指向為傳入的context context[fn](...[...args, ...innerArgs]); } }; // 如果綁定的是構造函數 那麼需要繼承構造函數原型屬性和方法 // 實現繼承的方式: 使用Object.create result.prototype = Object.create(this.prototype); return result; }; //用法如下 function Person(name, age) { console.log(name); //'我是參數傳進來的name' console.log(age); //'我是參數傳進來的age' console.log(this); //構造函數this指向實例對象 } // 構造函數原型的方法 Person.prototype.say = function () { console.log(123); }; let obj = { objName: "我是obj傳進來的name", objAge: "我是obj傳進來的age", }; // 普通函數 function normalFun(name, age) { console.log(name); //'我是參數傳進來的name' console.log(age); //'我是參數傳進來的age' console.log(this); //普通函數this指向綁定bind的第一個參數 也就是例子中的obj console.log(this.objName); //'我是obj傳進來的name' console.log(this.objAge); //'我是obj傳進來的age' } // 先測試作為構造函數調用 let bindFun = Person.myBind(obj, "我是參數傳進來的name"); let a = new bindFun("我是參數傳進來的age"); a.say(); //123 // 再測試作為普通函數調用 // let bindFun = normalFun.myBind(obj, '我是參數傳進來的name') // bindFun('我是參數傳進來的age')
文章特殊字符描述:
- 問題標註
Q:(question) - 答案標註
R:(result) - 注意事項標準:
A:(attention matters) - 詳情描述標註:
D:(detail info) - 總結標註:
S:(summary) - 分析標註:
Ana:(analysis) - 提示標註:
T:(tips)
往期回顧:
- 熱點面試題:Virtual DOM 相關問題?
- 熱點面試題:什麼是粘包/半包問題,該如何解決?
- 熱點面試題:console.log()同異步問題?
- 熱點面試題:進程系列問題?
- 熱點面試題:Node.js 中的垃圾回收機制?
- 熱點面試題:簡述 http3.0~http1.0 分別有什麼改進?
- JavaScript中的AMD和CMD規範
- Vue數據監聽Object.definedProperty()方法的實現
最後:
- 歡迎關注
『前端進階圈』公眾號 ,一起探索學習前端技術...... - 公眾號回覆
加羣或掃碼, 即可加入前端交流學習羣,長期交流學習...... - 公眾號回覆
加好友,即可添加為好友