成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

日積跬步,apply/call/bind 自我實現(xiàn)

denson / 3234人閱讀

摘要:日常編碼中被開發(fā)者用來實現(xiàn)對象冒充,也即顯示綁定。面試題源碼實現(xiàn),事實上是對基礎(chǔ)知識的一個綜合考核。原型鏈終端指向,不會有構(gòu)造函數(shù),也不會有等屬性,這些屬性來自。

call/apply/bind 日常編碼中被開發(fā)者用來實現(xiàn) “對象冒充”,也即 “顯示綁定 this“。

面試題:“call/apply/bind源碼實現(xiàn)”,事實上是對 JavaScript 基礎(chǔ)知識的一個綜合考核。

相關(guān)知識點:

作用域;

this 指向;

函數(shù)柯里化;

原型與原型鏈;

call/apply/bind 的區(qū)別

三者都可用于顯示綁定 this;

call/apply 的區(qū)別方式在于參數(shù)傳遞方式的不同;

fn.call(obj, arg1, arg2, ...), 傳參數(shù)列表,以逗號隔開;

fn.call(obj, [arg1, arg2, ...]), 傳參數(shù)數(shù)組;

bind 返回的是一個待執(zhí)行函數(shù),是函數(shù)柯里化的應(yīng)用,而 call/apply 則是立即執(zhí)行函數(shù)

思路初探
Function.prototype.myCall = function(context) {
    // 原型中 this 指向的是實例對象,所以這里指向 [Function: bar]
    console.log(this);  // [Function: bar]
    // 在傳入的上下文對象中,創(chuàng)建一個屬性,值指向方法 bar
    context.fn = this;  // foo.fn = [Function: bar]
    // 調(diào)用這個方法,此時調(diào)用者是 foo,this 指向 foo
    context.fn();
    // 執(zhí)行后刪除它,僅使用一次,避免該屬性被其它地方使用(遍歷)
    delete context.fn;
};

let foo = {
    value: 2
};

function bar() {
    console.log(this.value);
}
// bar 函數(shù)的聲明等同于:var bar = new Function("console.log(this.value)");

bar.call(foo);   // 2;
call 的源碼實現(xiàn)

初步思路有個大概,剩下的就是完善代碼。

// ES6 版本
Function.prototype.myCall = function(context, ...params) {
  // ES6 函數(shù) Rest 參數(shù),使其可指定一個對象,接收函數(shù)的剩余參數(shù),合成數(shù)組
  if (typeof context === "object") {
    context = context || window;
  } else {
    context = Object.create(null);
  }

  // 用 Symbol 來作屬性 key 值,保持唯一性,避免沖突
  let fn = Symbol();
  context[fn] = this;
  // 將參數(shù)數(shù)組展開,作為多個參數(shù)傳入
  const result = context[fn](...params);
  // 刪除避免永久存在
  delete(context[fn]);
  // 函數(shù)可以有返回值
  return result;            
}

// 測試
var mine = {
    name: "以樂之名"
}

var person = {
  name: "無名氏",
  sayHi: function(msg) {
    console.log("我的名字:" + this.name + ",", msg);
  }
}

person.sayHi.myCall(mine, "很高興認(rèn)識你!");  
// 我的名字:以樂之名,很高興認(rèn)識你!

知識點補(bǔ)充:

ES6 新的原始數(shù)據(jù)類型 Symbol,表示獨(dú)一無二的值;

Object.create(null) 創(chuàng)建一個空對象

// 創(chuàng)建一個空對象的方式

// eg.A 
let emptyObj = {};

// eg.B
let emptyObj = new Object();

// eg.C
let emptyObj = Object.create(null);

使用 Object.create(null) 創(chuàng)建的空對象,不會受到原型鏈的干擾。原型鏈終端指向 null,不會有構(gòu)造函數(shù),也不會有 toString、 hasOwnProperty、valueOf 等屬性,這些屬性來自 Object.prototype。有原型鏈基礎(chǔ)的伙伴們,應(yīng)該都知道,所有普通對象的原型鏈都會指向 Object.prototype。

所以 Object.create(null) 創(chuàng)建的空對象比其它兩種方式,更干凈,不會有 Object 原型鏈上的屬性。

ES5 版本:

自行處理參數(shù);

自實現(xiàn) Symobo

// ES5 版本

// 模擬Symbol
function getSymbol(obj) {
  var uniqAttr = "00" + Math.random();
  if (obj.hasOwnProperty(uniqAttr)) {
    // 如果已存在,則遞歸自調(diào)用函數(shù)
    arguments.callee(obj);
  } else {
    return uniqAttr;
  }
}

Function.prototype.myCall = function() {
  var args = arguments;
  if (!args.length) return;

  var context = [].shift.apply(args);
  context = context || window;

  var fn = getSymbol(context);
  context[fn] = this;

  // 無其它參數(shù)傳入
  if (!arguments.length) {
    return context[fn];
  }

  var param = args[i];
  // 類型判斷,不然 eval 運(yùn)行會出錯
  var paramType = typeof param;
  switch(paramType) {
    case "string":
      param = """ + param + """
    break;
    case "object":
      param = JSON.stringify(param);
    break;
  } 

  fnStr += i == args.length - 1 ? param : param + ",";

  // 借助 eval 執(zhí)行
  var result = eval(fnStr);
  delete context[fn];
  return result;
}

// 測試
var mine = {
    name: "以樂之名"
}

var person = {
  name: "無名氏",
  sayHi: function(msg) {
    console.log("我的名字:" + this.name + ",", msg);
  }
}

person.sayHi.myCall(mine, "很高興認(rèn)識你!");  
// 我的名字:以樂之名,很高興認(rèn)識!
apply 的源碼實現(xiàn)

call 的源碼實現(xiàn),那么 apply 就簡單,兩者只是傳遞參數(shù)方式不同而已。

Function.prototype.myApply = function(context, params) {
    // apply 與 call 的區(qū)別,第二個參數(shù)是數(shù)組,且不會有第三個參數(shù)
    if (typeof context === "object") {
        context = context || window;
    } else {
        context = Object.create(null);
    }

    let fn = Symbol();
    context[fn] = this;
    const result context[fn](...params);
    delete context[fn];
    return result;
}
bind 的源碼實現(xiàn)

bindcall/apply 的區(qū)別就是返回的是一個待執(zhí)行的函數(shù),而不是函數(shù)的執(zhí)行結(jié)果;

bind 返回的函數(shù)作為構(gòu)造函數(shù)與 new 一起使用,綁定的 this 需要被忽略;

調(diào)用綁定函數(shù)時作為this參數(shù)傳遞給目標(biāo)函數(shù)的值。 如果使用new運(yùn)算符構(gòu)造綁定函數(shù),則忽略該值。 —— MDN
Function.prototype.bind = function(context, ...initArgs) {
    // bind 調(diào)用的方法一定要是一個函數(shù)
    if (typeof this !== "function") {
      throw new TypeError("not a function");
    }
    let self = this;  
    let F = function() {};
    F.prototype = this.prototype;
    let bound = function(...finnalyArgs) {
      // 將前后參數(shù)合并傳入
      return self.call(this instanceof F ? this : context || this, ...initArgs, ...finnalyArgs);
    }
    bound.prototype = new F();
    return bound;
}

不少伙伴還會遇到這樣的追問,不使用 call/apply,如何實現(xiàn) bind ?

騷年先別慌,不用 call/apply,不就是相當(dāng)于把 call/apply 換成對應(yīng)的自我實現(xiàn)方法,算是偷懶取個巧吧。

本篇 call/apply/bind 源碼實現(xiàn),算是對之前文章系列知識點的一次加深鞏固。

“心中有碼,前路莫慌?!?/strong>

參考文檔:

MDN - Function.prototype.bind()

不用call和apply方法模擬實現(xiàn)ES5的bind方法

更多前端基石搭建,盡在 Github,期待 Star!
https://github.com/ZengLingYong/blog

作者:以樂之名
本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。轉(zhuǎn)載請指明出處。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/106634.html

相關(guān)文章

  • JavaScript的作用域、閉包、(apply, call, bind

    摘要:閉包閉包的概念與詞法域關(guān)系緊密。閉包甚至在函數(shù)已經(jīng)返回后也可以獲取其外部函數(shù)的變量。一種常見的閉包導(dǎo)致的由立即調(diào)用函數(shù)表達(dá)式解決的例子事實上結(jié)果的所有都是,而不是按順序得出的,。 介紹 JavaScript 有一個特征————作用域。理解作用域scope可以使你的代碼脫穎而出,減少錯誤,幫助你用它構(gòu)造強(qiáng)大的設(shè)計模式。 什么是作用域 作用域就是在代碼執(zhí)行期間變量,函數(shù)和對象能被獲取到的特...

    tyheist 評論0 收藏0
  • JS中的callapply、bind方法詳解

    摘要:不能應(yīng)用下的等方法。首先我們可以通過給目標(biāo)函數(shù)指定作用域來簡單實現(xiàn)方法保存,即調(diào)用方法的目標(biāo)函數(shù)考慮到函數(shù)柯里化的情況,我們可以構(gòu)建一個更加健壯的這次的方法可以綁定對象,也支持在綁定的時候傳參。原因是,在中,多次是無效的。 bind 是返回對應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。 apply、call 在 javascript 中,call 和 apply 都是...

    zombieda 評論0 收藏0
  • JS基礎(chǔ)篇--call、apply、bind方法詳解

    摘要:首先我們可以通過給目標(biāo)函數(shù)指定作用域來簡單實現(xiàn)方法保存,即調(diào)用方法的目標(biāo)函數(shù)考慮到函數(shù)柯里化的情況,我們可以構(gòu)建一個更加健壯的這次的方法可以綁定對象,也支持在綁定的時候傳參。原因是,在中,多次是無效的。而則會立即執(zhí)行函數(shù)。 bind 是返回對應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。 apply、call 在 javascript 中,call 和 apply 都是...

    lastSeries 評論0 收藏0
  • call,apply and bind in JavaScript

    摘要:文章盡量使用大量實例進(jìn)行講解,它們的使用場景。在嚴(yán)格模式下,函數(shù)被調(diào)用后,里面的默認(rèn)是后面通過調(diào)用函數(shù)上的和方法,該變指向,函數(shù)里面的指向。利用,可以傳入外層的上下文。同樣適用的還有,里面的對象,它也是一種類數(shù)組對象。 call,apply and bind in JavaScript 在ECMAScript中,每個函數(shù)都包含兩個繼承而來的方法:apply() 和 call(),這兩個...

    JohnLui 評論0 收藏0
  • 前端基礎(chǔ):call,apply,bind的的理解

    摘要:和區(qū)別其實他們的作用是一樣的,只是傳遞的參數(shù)不一樣而已。接受個參數(shù),第一個參數(shù)指定了函數(shù)體內(nèi)對象的指向,第二個參數(shù)為數(shù)組或者一個類數(shù)組。看個栗子一個有意思的事在中,多次是無效的。而則會立即執(zhí)行函數(shù)。 背景 前兩天在做小程序的需求的時候用到bind的時候才想起自己對這三的東西的了解比較淺薄,這個時候用的時候就有點怕。時候還是要好好學(xué)習(xí)下,理解下怎么玩。 正文 先說call 和 apply...

    netmou 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<