自定義call、apply方法

call和apply的做用

call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數,該方法的語法和做用與 apply() 方法相似,只有一個區別,就是 call() 方法接受的是一個參數列表,而 apply() 方法接受的是一個包含多個參數的數組
在說實現自定義call、apply以前,咱們首先看下一段代碼es6

var age = 6;
var person =  {
    age: 3,
    fn: function() {
        console.log(this.age);
    }
}
var fn = person.fn;
fn();
person.fn();

結果是數組

6
3

爲何呢?記住一句話:this的最終指向的是那個調用它的對象
var fn = person.fn,將personfn方法賦值給fn,但未調用,而fn()調用的時候實際等於window.fn(),因此fn中的this指向window,因此fn()的結果是window.age = 6
person.fn()person這個對象調用的,因此此時fn函數中this的指向是person這個對象。因此this.age = 3app

咱們本身實現call方法,思路就是基於此函數

Function.prototype.myCall = function(_context) {
  // 當_context未傳值或者爲null或者undefined,context指向window
  var context = _context || window;
  // 給對象添加一個方法,這個this就是使用call使用的函數
  context.fn = this;
  // 拼接執行eval時候的參數
  var args = [];
  for(var i = 1; i < arguments.length; i++) {
    args.push('arguments['+ i +']');
  }
  // 字符串拼接數組,數組隱式轉換成args.toString()即'context.fn('+ args +')', => 'context.fn(arguments[1])'
  var ret = eval('context.fn('+ args +')');
  delete context.fn;
  return ret;
}
Function.prototype.myCallEs6 = function(context = window) {
  // 給對象添加一個方法,這個this就是使用call使用的函數
  context.fn = this;
  // 獲取除了第一個之外參數
  const args = [...arguments].slice(1);
  // 執行方法,此時執行方法的this指向的是context
  const ret = context.fn(...args);
  delete context.fn;
  // 返回執行結果
  return ret;
}
const person = {
  age: 3
};
function fn(name) {
  this.name = name;
  console.log(this);
}
fn.myCall(person, 'xzf');
fn.call(person, 'xzf');

call和apply做用相同,僅僅只是參數不一樣,apply的參數以數組的形式傳入,因此咱們只要作一下修改this

Function.prototype.myApply = function(_context) {
  // 當_context未傳值或者爲null或者undefined,context指向window
  var context = _context || window;
  // 給對象添加一個方法,這個this就是使用call使用的函數
  context.fn = this;
  var ret;
  var args = arguments[1];
  if(args) {
    // 假設args爲['123','abc'],['123','abc'].toString()會轉換成'123,abc'
    // 執行eval的時候,會變成一個數字,一個變量,此時是不存在變量abc的
    // 須要經過上面mycall相似的方法進行轉換
    var argsArr = [];
    for(var i = 0; i < arguments.length; i++) {
      argsArr.push('args['+ i +']');
    }
    ret = eval('context.fn('+ argsArr +')');
  } else {
    ret = context.fn();
  }
  delete context.fn;
  return ret;
}
// es6
Function.prototype.myApplyEs6 = function(context = window) {
  // 當_context未傳值或者爲null或者undefined,context指向window
  // 給對象添加一個方法,這個this就是使用call使用的函數
  context.fn = this;
  let ret;
  // 若是有參數,展開數組參數傳入
  if(arguments[1]) {
    ret = context.fn(...arguments[1]);
  } else {
    ret = context.fn();
  }
  delete context.fn;
  return ret;
}
相關文章
相關標籤/搜索