關於實現js中一些常見的方法屬於面試中的常問問題,可能剛開始接觸的時候會束手無策。知道和理解其中的原理可以在平常開發中更如魚得水,面對面試也不成問題。另外,學會以目的(實現的功能)爲導向一層一層反推,總結出實現的思路就能按照步驟直接實現或者曲線實現(整理不易記得點贊
哈)。javascript
call()
方法:讓call()中的對象調用當前對象所擁有的function。例如:test.call(obj,arg1,arg2,···) 等價於 obj.test(arg1,arg2,···)
;在手寫實現call()
方法前咱們先進行分析,test
調用call
方法能夠看做將test
方法做爲obj
的一個屬性(方法)調用,等obj.test()
執行完畢後,再從obj
屬性上刪除test
方法:java
myCall:node
function test(a, b) { console.log(a); console.log(b); console.log(this.c); } let obj = { c: "hello", }; //myCall Function.prototype.myCall = function () { //聲明傳入上下文爲傳入的第一個參數,若是沒有傳參默認爲global(node環境),若是是瀏覽器環境則爲 window; let context = arguments[0] || global; //將調用myCall方法函數(this)設置爲 聲明的傳入上下文中的fn函數; context.fn = this; //對函數參數進行處理 var args = []; for (let index = 0; index < arguments.length; index++) { index > 0 && args.push(arguments[index]); } //執行fn,也就是調用myCall方法的函數 context.fn(...args); //執行完畢後刪除傳入上下文的fn,不改變原來的對象 delete context.fn; }; test.myCall(obj, "a", 123); console.log(obj)
打印的結果:面試
a 123 hello { c: 'hello' }
從結果能夠看出:test
中this.c
輸出爲hello
,說明this
爲obj
;最後輸出的obj
也沒有改變。數組
apply()
方法做用和call()
徹底同樣,只是apply
的參數第一個爲須要指向的對象,第二個參數以數組形式傳入。例如:test.apply(obj,[arg1,arg2,···]) 等價於 obj.test(arg1,arg2,···)
;promise
myApply:瀏覽器
//myApply Function.prototype.myApply = function(){ let context = arguments[0] || global; context.fn = this; var args = arguments.length > 1 ? arguments[1] : []; context.fn(...args); delete context.fn; } test.myApply(obj, ["world", 123]); console.log(obj)
打印的結果:app
world 123 hello { c: 'hello' }
bind
方法:建立一個新的函數, 當被調用時,將其this關鍵字設置爲提供的值,在調用新函數時,在任何提供以前提供一個給定的參數序列。例如:let fn = test.bind(obj,arg1,arg2,···); fn() 等價於 let fn = obj.test; fn(arg1,arg2,···)
;實現思路爲:異步
myBind:ide
Function.prototype.myBind = function(){ //1.一、聲明傳入上下文爲傳入的第一個參數,若是沒有傳參默認爲global(node環境),若是是瀏覽器環境則爲 window; let context = arguments[0] || global; //1.二、將調用myBind方法函數(this)設置爲 聲明的傳入上下文中的fn函數; context.fn = this; //2.一、對調用myBind的函數參數進行處理 var args = []; for (let index = 0; index < arguments.length; index++) { index > 0 && args.push(arguments[index]); } //三、聲明和定義函數變量F,用於返回給外層 let F = function (){ //2.二、對再次傳入的參數進行處理,追加到 for (let index = 0; index < arguments.length; index++) { args.push(arguments[index]); } //4.二、執行實際的調用myBind方法函數 context.fn(...args); //五、執行完畢後刪除傳入上下文的fn,不改變原來的對象 delete context.fn; } return F; } var f = test.myBind(obj, "a") //4.一、執行返回的函數 f(9527);
打印的結果:
a 9527 hello { c: 'hello' }
MDN中關Promise
的定義以下:
Promise 對象用於表示一個異步操做的最終完成 (或失敗)及其結果值。<br/>一個 Promise 對象表明一個在這個 promise 被建立出來時不必定已知的值。它讓您可以把異步操做最終的成功返回值或者失敗緣由和相應的處理程序關聯起來。 這樣使得異步方法能夠像同步方法那樣返回值:異步方法並不會當即返回最終的值,而是會返回一個 promise,以便在將來某個時候把值交給使用者。<br/>
<br/>一個 Promise 必然處於如下幾種狀態之一:<br/>
<br/>待定(pending): 初始狀態,既沒有被兌現,也沒有被拒絕。<br/>
<br/>已兌現(fulfilled): 意味着操做成功完成。<br/>
<br/>已拒絕(rejected): 意味着操做失敗。<br/>
<br/>待定狀態的 Promise 對象要麼會經過一個值被兌現(fulfilled),要麼會經過一個緣由(錯誤)被拒絕(rejected)。當這些狀況之一發生時,咱們用 promise 的 then 方法排列起來的相關處理程序就會被調用。若是 promise 在一個相應的處理程序被綁定時就已經被兌現或被拒絕了,那麼這個處理程序就會被調用,所以在完成異步操做和綁定處理方法之間不會存在競爭狀態<br/>
new Promise((resolve, reject) => { //異步操做 //··· //執行完後調用resolve和reject輸出兩種不一樣結果 if (true) { resolve("res"); } else { reject("err"); } }) .then((res) => { //then接受resolve中的結果 console.log(res); }) .catch((err) => { //catch接受reject中的結果 console.log(err); });
上述新建實例代碼能夠轉化爲:
function fn(resolve, reject) { //異步操做 //··· //執行完後調用resolve和reject輸出兩種不一樣結果 if (true) { resolve("res"); } else { reject("err"); } } let p = new Promise(fn); p.then((res) => { //then接受resolve中的結果 console.log(res); }) p.catch((err) => { //catch接受reject中的結果 console.log(err); });
上述中的使用者就是then和catch
,結合代碼中的使用方式,簡單來講就是Promise中執行異步操做,then和catch只會在異步執行完後纔會接到返回結果繼續執行!
瞭解了Promise的定義和使用步驟後,接下來直接手撕Promise的實現,直接上實現Promise的代碼(內涵大量註釋,基本一句一解釋,可是邏輯仍是得第三部分來說
):
// 定義promise中的三種狀態 const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; // 定義promise的類 class myPromise { //class的構造函數,接受新建實例時的參數:executor在promise中是一個函數 constructor(executor) { //初始化該class中的初始狀態 this.status = STATUS_PENDING; //定義class中成功(res)和失敗(err)時的變量值 this.res = ""; this.err = ""; //promis異步中最重要的異步,定義成功和錯誤函數存儲的數組,存放異步時尚未執行的操做 this.onResCallbacks = []; this.onErrCallbacks = []; //定義該構造函數constructor定義域中的變量resolve let resolve = (res) => { // 首先判斷該class中的狀態,只有狀態爲pending時才能轉化class轉態爲fulfilled或者rejected if (this.status === STATUS_PENDING) { //修改class的轉態爲fulfilled,也就表示不會轉進行其餘轉態的轉化了 this.status = STATUS_FULFILLED; //將成功(resolve)狀態下的值賦給class的成功返回res this.res = res; //此時狀態由pending轉爲fulfilled,執行以前在then中存放的須要執行的異步操做,promise的then中參數res接受結果 this.onResCallbacks.forEach((fn) => { fn(); }); } }; //定義該構造函數constructor定義域中的變量reject let reject = (err) => { // 首先判斷該class中的狀態,只有狀態爲pending時才能轉化class轉態爲fulfilled或者rejected if (this.status === STATUS_PENDING) { //修改class的轉態爲rejected,也就表示不會轉進行其餘轉態的轉化了 this.status = STATUS_REJECTED; //將失敗(reject)狀態下的值賦給class的失敗返回err this.err = err; //此時狀態由pending轉爲rejected,執行以前在catch中存放的須要執行的異步操做,promise的catch中參數err接受結果 this.onErrCallbacks.forEach((fn) => { fn(); }); } }; //按照promise中的邏輯,在調用時就當即執行了,因此在手寫的myPromise建立構造函數constructor時就執行executor try { //執行參入的函數,並將上述定義的resolve和reject做爲參數傳入 executor(resolve, reject); } catch (err) { //報錯時調用失敗的狀態函數 reject(err); } } //在class中定義promise的成功狀態接收函數then,按照promise邏輯,then中傳入的通常都是一個函數 then(onRes = () => {}) { //若是是異步的,此時在constructor中status的狀態還沒變成fulfilled,因此會跳過onRes調用,沒有返回 if (this.status === STATUS_FULFILLED) { onRes(this.res); } //可是咱們將此時的異步放入數組存放 if (this.status === STATUS_PENDING) { this.onResCallbacks.push(() => onRes(this.res)); } //這步操做保證了then和catch可以在同級一塊兒"."調起,當then上述操做完後,返回class實例,即可以接在後面繼續調用catch return this; } //在class中定義promise的失敗狀態接收函數catch,按照promise邏輯,catch中傳入的通常都是一個函數 catch(onErr = () => {}) { //若是是異步的,此時在constructor中status的狀態還沒變成rejected,因此會跳過onErr調用,沒有返回 if (this.status === STATUS_REJECTED) { onErr(this.err); } //可是咱們將此時的異步放入數組存放 if (this.status === STATUS_PENDING) { this.onErrCallbacks.push(() => onErr(this.err)); } //這步操做保證了then和catch可以在同級一塊兒"."調起,當catch上述操做完後,返回class實例,即可以接在後面繼續調用then return this; } } //調用本身手寫的promise new myPromise((resolve, reject) => { console.log("進入了手寫的promise"); //用setTimeOut模擬異步操做 setTimeout(() => { if (false) { resolve("輸出成功結果resolve"); } else { reject("輸出失敗結果reject"); } }, 2000); //按照js的特性,此時不會等待異步完成,直接調用then或者catch }) .then((res) => { console.log("then:", res); }) .catch((err) => { //return this具體做用體如今這裏 console.log("catch:", err); });