深入淺出FE(二)call/apply和bind

目錄

一、定義

1、call

2、apply

3、bind

二、 關於call、apply和bind

1、call、apply、bind方法的共同點:

2、call、apply、bind方法的不同點:

三、拓展

1、模擬實現call

2、模擬實現apply,和call類似

3、模擬實現bind

4、應用

四、瀏覽器兼容性

五、參考資料


一、定義

1、call

call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。

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

參數:

thisArg

可選的。在 function 函數運行時使用的 this 值。請注意,this可能不是該方法看到的實際值:如果這個函數處於非嚴格模式下,則指定爲 null 或 undefined 時會自動替換爲指向全局對象,原始值會被包裝。

arg1, arg2, ...

指定的參數列表。

2、apply

apply() 方法調用一個具有給定this值的函數,以及作爲一個數組(或類似數組對象,ES5之後支持傳類數組,如dom中的一些類數組結果或者arguments)提供的參數。

function.apply(thisArg, [argsArray])

參數:

thisArg

必選的。在 func 函數運行時使用的 this 值。請注意,this可能不是該方法看到的實際值:如果這個函數處於非嚴格模式下,則指定爲 null 或 undefined 時會自動替換爲指向全局對象,原始值會被包裝。

argsArray

可選的。一個數組或者類數組對象,其中的數組元素將作爲單獨的參數傳給 func 函數。如果該參數的值爲 null 或  undefined,則表示不需要傳入任何參數。從ECMAScript 5 開始可以使用類數組對象。 瀏覽器兼容性 請參閱本文底部內容。

返回值

調用有指定this值和參數的函數的結果。

注意:call()方法的作用和 apply() 方法類似,區別就是call()方法接受的是參數列表,而apply()方法接受的是一個參數數組

3、bind

bind() 方法創建一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定爲 bind() 的第一個參數,而其餘參數將作爲新函數的參數,供調用時使用。

function.bind(thisArg[, arg1[, arg2[, ...]]])

參數:

thisArg

調用綁定函數時作爲 this 參數傳遞給目標函數的值。 如果使用new運算符構造綁定函數,則忽略該值。當使用 bind 在 setTimeout 中創建一個函數(作爲回調提供)時,作爲 thisArg 傳遞的任何原始值都將轉換爲 object。如果 bind 函數的參數列表爲空,執行作用域的 this 將被視爲新函數的 thisArg

arg1, arg2, ...

當目標函數被調用時,被預置入綁定函數的參數列表中的參數。

bind是ES5新增的一個方法,不會執行對應的函數(call或apply會自動執行對應的函數),而是返回對綁定函數的引用

二、 關於call、apply和bind

1、callapplybind方法的共同點:

apply 、 call 、bind 三者都是Function原型(即Function.prototype)的方法
apply 、 call 、bind 三者都是用來改變函數的this對象的指向的;
apply 、 call 、bind 三者第一個參數都是this要指向的對象,也就是想指定的執行上下文環境;
apply 、 call 、bind 三者都可以利用後續參數傳參;

2、callapplybind方法的不同點:

bind 是返回對應函數,便於稍後調用;apply 、call 則是立即調用 。

call/apply的用法相同,區別在於傳參數方式不同,call接受一個參數列表,apply接受一個參數數組,call是apply的語法糖

三、拓展

1、模擬實現call

Function.prototype.myCall = function (ctx) {
    //獲取上下文ctx
    let ctx = ctx || window;
    //獲取當前將要執行的函數
    ctx.fn = this;
    //截取參數
    let args = [...arguments].slice(1);
    //執行函數
    let res = ctx.fn(...args);
    //去除副作用
    delete ctx.fn;
    return res;
}

2、模擬實現apply,和call類似

Function.prototype.myApply = (ctx){
    var ctx = ctx || window;
    ctx.fn = this;
    var res;
    if (arguments[1]) {
        re s = ctx.fn(...arguments[1]);
    } else {
        res = ctx.fn();
    }
    delete ctx.fn;
    return res
}

3、模擬實現bind

//bind() 方法創建一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定爲 bind() 的第一個參數,
//而其餘參數將作爲新函數的參數,供調用時使用。
Function.prototype.bind = Function.prototype.bind || function (context) {
    if (typeof this !== "function") {
        throw new Error("Function.prototype.bind is not a function")
    }
    var self = this;
    var args = [...arguments].slice(1);

    return function F() {
        if (self instanceof F) {
            return new self(...args, ...arguments);
        }
        return new self.apply(context, args.concat(...arguments));
    }
};

4、應用

(1)call可以用作檢測數據類型

const getVarType = (target) => {
  var classToType = {};
  "Array Date RegExp Object Error".split(" ").forEach(function(item) {
    classToType["[object " + item + "]"] = item.toLowerCase();
  });

  function type(target) {
    if (target === null) {
      return String(target);
    }
    return typeof target === "object"
      ? classToType[object.prototype.toString.call(target)] || "object"
      : typeof target;
  }
  return type(target);
};

2、apply可以用作柯里化

函數柯里化是把接受多個參數的函數轉變成接受一個單一參數(最初函數的第一個參數),並且返回接受餘下的參數而且返回結果的新函數的技術。

柯里化其實本身是固定一個可以預期的參數,並返回一個特定的函數,處理批特定的需求。這增加了函數的適用性,但同時也降低了函數的適用範圍。

這是以前看《javascript設計模式與開發實踐》做的筆記,

var currying = function(fn) {
  var args = [];

  return function() {
    if (arguments.length === 0) {
      return fn.apply(this, args); // 沒傳參數時,調用這個函數
    } else {
      [].push.apply(args, arguments); // 傳入了參數,把參數保存下來
      return arguments.callee; // 返回這個函數的引用
    }
  }
}

var cost = (function() {
  var money = 0;
  return function() {
    for (var i = 0; i < arguments.length; i++) {
      money += arguments[i];
    }
    return money;
  }
})();

var cost = currying(cost);

cost(100); // 傳入了參數,不真正求值
cost(200); // 傳入了參數,不真正求值
cost(300); // 傳入了參數,不真正求值

console.log(cost()); // 求值並且輸出600

上面這種情況沒有考慮爲函數與傳參數情況,所以做了一點點修改

var currying = function(fn) {
  var args = [...argumengts].slice(1);

  return function() {
    if (arguments.length === 0) {
      return fn.apply(this, args); // 沒傳參數時,調用這個函數
    } else {
      [].push.apply(args, arguments); // 傳入了參數,把參數保存下來
      return arguments.callee; // 返回這個函數的引用
    }
  }
}

3、apply函數可以用來實現bind(參見上面bind的實現)

四、瀏覽器兼容性

詳見參考資料1、2、3-對應call、apply和bind的MDN文檔

五、參考資料

1、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call

2、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

3、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

發佈了380 篇原創文章 · 獲贊 115 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章