apply call bind 簡介

Function.prototype.call(thisArg [, arg1, arg2, ...])

call() 簡述

  • call() 方法 調用一個函數, 其具備一個指定的 this 值和分別地提供的參數(參數的列表)。
  • 當第一個參數爲 null、undefined 的時候, 默認 this 上下文指向window。

call() 簡單實例

const name = 'qianyin';
const product = {
  name: 'linheng',
};

function log(...args){
  console.log(this.name, ...args);
}

log(1, 2, 3);               // qianyin 1 2 3
log.call(null, 1, 2, 3);    // qianyin 1 2 3
log.call(product, 1, 2, 3); // linheng 1 2 3
複製代碼

call() 對箭頭函數無效

const name = 'qianyin';
const product = {
  name: 'linheng',
};

const log = (...args) => {
  console.log(this.name, ...args);
}

log(1, 2, 3);               // qianyin 1 2 3
log.call(null, 1, 2, 3);    // qianyin 1 2 3
log.call(product, 1, 2, 3); // qianyin 1 2 3

複製代碼

補充:

  • 箭頭函數做爲函數的一種形式, 對於this的處理和普通函數有所區別, 其沒有本身的 this 上下文,也就是說經過 bind/call/apply 函數方法設置 this 值時無效的,會被忽略;數組

  • 由於箭頭函數沒有本身的 this 上下文, 因此箭頭函數的 this 上下文等於定義函數處的this上下文,也就是最近的一個 this 上下文;app

  • 你能夠認爲箭頭函數的 this 和調用者無關,只和其定義時所在的 this 上下文相關;函數

  • 以下代碼: 在對象 obj 中使用箭頭函數定義 log 函數, 那麼由於箭頭函數沒有本身的 this 上下文, 因此 log 函數的 this 上下文等於定義箭頭函數處的 this 上下文, 等於 對象 obj 所處的 this 上下文(window)ui

const name = 'linheng';
const obj = {
  name: 'qianyin',
  log: () => {
    console.log(this.name);
  }
};
obj.log();  // linheng
複製代碼
  • 那麼若是我必定要在 obj 中定義一個 log 函數而且使得 this 指向對象 obj 呢?
  • 方法一: 使用 function 定義函數
const name = 'linheng';
const obj = {
  name: 'qianyin',
  log: function(){
    console.log(this.name);
  }
};
obj.log();  // qianyin
複製代碼
  • 方法二: 畫蛇添足, 在函數 log 中聲明箭頭函數並調用, 那麼箭頭函數的 this 上下文等於定義箭頭函數處的 this 上下文, 等於 log 函數的上下文(對象 obj )
const name = 'linheng';
const obj = {
  name: 'qianyin',
  log: function(){
    (() => {
      console.log(this.name);
    })();
  },
};
obj.log();  // qianyin
複製代碼

Function.prototype.apply(thisArg [, Array])

Apply() 簡述

  • apply() 方法 調用 一個具備給定 this 值的函數,以及做爲一個 數組(或相似數組對象) 提供的參數
  • call() 方法的做用和 apply() 方法相似,區別就是除第一參數 call() 方法接受的是 參數列表 ,而apply()方法接受的是一個參數 數組(或類數組)

Apply() 簡單實例

const name = 'qianyin';
const product = {
  name: 'linheng',
};

function log(...args){
  console.log(this.name, ...args);
}

log([1, 2, 3]);                 // qianyin [1 2 3]
log.apply(null, [1, 2, 3]);     // qianyin 1 2 3
log.apply(product, [1, 2, 3]);  // linheng 1 2 3
複製代碼

Apply() 對箭頭函數無效

const name = 'qianyin';
const product = {
  name: 'linheng',
};

const log = (...args) => {
  console.log(this.name, ...args);
}

log([1, 2, 3]);                 // qianyin [1 2 3]
log.apply(null, [1, 2, 3]);     // qianyin 1 2 3
log.apply(product, [1, 2, 3]);  // qianyin 1 2 3
複製代碼

Function.prototype.bind(thisArg [, arg1, arg2, ...])

bidn() 簡述

  • bind() 方法 建立(拷貝)一個新的函數 , 當這個新函數被調用時 this 指向 thisArg,其 參數列表前幾項值 爲建立時指定的 參數序列
  • thisArg: 綁定函數被調用時,該參數會做爲原函數運行時的 this 指向。當使用 new 操做符調用綁定函數時,該參數無效。

bind() 綁定 this 上下文

  • bind() 最簡單的用法是建立一個函數,使這個函數不論怎麼調用都有一樣的 this 上下文。
  • JavaScript 新手常常犯的一個錯誤是將一個方法從對象中拿出來,而後再調用,卻又但願方法中的 this 是原來的對象(好比在回調中傳入這個方法)。
  • 若是不作特殊處理的話,通常會丟失原來的對象。從原來的函數和原來的對象建立一個綁定函數,則能很漂亮地解決這個問題:
  • 若是隻是單純綁定 this 上下文, 徹底可使用箭頭函數進行替代
// 例一
this.x = 9;

var module = {
  x: 81,
  getX: function() { return this.x; }
};
module.getX();  // 返回 81 (經過對象調用函數, 上下文爲該對象)
var retrieveX = module.getX;  // 獲取對象中函數的引用地址
retrieveX();    // 返回 9, 在這種狀況下, "this" 指向全局做用域(在全局對象下調用函數)
// 永久爲函數 boundGetX 綁定 this 上下文
var boundGetX = retrieveX.bind(module);
boundGetX();   // 返回 81 (函數 this 上下文永久綁定爲 module)
複製代碼
// 例二爲回調函數綁定 this 上下文
var x = 10;
var obj = {
  x: 20,
  get: ffunction(){
    console.log(this.x);
  }
};
// 將對象中方法取出(函數的引用地址),做爲回調函數, 又由於 setTimeout 回調函數執行的上下文是 window
setTimeout(obj.get, 1000);            // 打印 10
// 將對象中方法取出(函數的引用地址),做爲回調函數並綁定 this 上下文
setTimeout(obj.get.bind(obj), 1000);  // 打印 20
複製代碼

爲函數永久綁定固定參數

  • bind() 的另外一個最簡單的用法是使一個函數 擁有預設的初始參數
  • 這些參數(若是有的話)做爲bind()的第二個參數跟在 this(或其餘對象)後面。
  • 以後它們會 被插入到目標函數的參數列表的開始位置 ,傳遞給綁定函數的參數會跟在它們的後面。
function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// 爲拷貝 list 方法並綁定初始參數
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList();         // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
複製代碼

總結

  • 當咱們使用一個函數須要改變 this 指向的時候纔會用到 call() apply() bind() 固然也別忘記了箭頭函數
  • call() 和 apply() 是對函數的調用,在調用的同時綁定 this 上下文並傳遞參數列表
  • bind() 是對函數的拷貝進一步的封裝, 爲函數永久綁定 this 上下文並賦予固定參數
  • call() 和 bind() 以參數列表形式給函數指定參數, apply() 則以數組的形式給函數指定參數

apply call bind 的一些運用

類數組轉爲數組

  • 方法一:
const obj = {0: 'q', 1: 'i', 2: 'q', 3: 'a',  4:'n', 5: 'y', 6:'i', 7:'n', length: 8};
  const arr = [];
  Array.prototype.push.apply(arr, obj);
  console.log(arr); // ["q", "i", "q", "a", "n", "y", "i", "n"]
複製代碼
  • 方法二:
const obj = {0: 'q', 1: 'i', length: 2};
  const arr = Array.prototype.slice.call(obj);  // [q, i]
複製代碼

爲僞數組添加新的元素

  • 方法一: 固然你也可使用 apply
const obj = {0: 'q', length: 1};
  Array.prototype.push.call(obj, 'i', 'a', 'n');
  console.log(obj);   // {0: 'q', 1: 'i', 2: 'a', 3: 'n'}
複製代碼
  • 方法二:
const obj = {0: 'q', length: 1};
  const push = Array.prototype.push.bind(obj);
  push('i', 'a', 'n');
  console.log(obj);   // {0: 'q', 1: 'i', 2: 'a', 3: 'n'}
複製代碼

求數組中最大值(最小值同樣作法)

const arr = [1,2,3,4,5,6];
const max = Math.max.apply(null, arr);
// 或 const max = Math.max.call(null, ...arr)
console.log(max);    // 6
複製代碼

數組合並追加

const arr = [1, 2];
const brr = [3, 4];
Array.prototype.push.apply(arr, brr);
// 或者 Array.prototype.push.call(arr, ...brr);
// 固然還能夠這樣 arr.push(...brr);
console.log(arr);
複製代碼

使用 log 代理 console.log

function log(...args){
  // 在能夠經過配置, 或者判斷當前開發環境來控制是否須要在控制檯打印輸出
  if(true){
    console.log.apply(console, args);
  }
}
複製代碼
相關文章
相關標籤/搜索