Shane Jix

Implementation fn.apply() fn.call() And fn.bind()

create:August 09, 2019  update:April 12, 2022  ☕️ 3 min read

同步链接: https://www.shanejix.com/posts/Implementation fn.apply() fn.call() And fn.bind()/

why

demo:

const name = "foo";

const obj = {
  name: "bar",
  say: function () {
    console.log(this.name);
  },
};

obj.say(); // output: bar // this => obj

const sayName = obj.say;

sayName(); // output: foo // this => window

函数作为 JavaScript 的一等公民,可以作为值任意传递;在不同执行上下文中,this 的值是被动态计算出来的。有时为了绑定函数的执行环境,fn.apply(), fn.call(),fn.bind()就起到至关重要的作用

how?如何改变函数的 this 呢?看个例子:

var sData = "display() Wisen"; //not let

function display() {
  console.log("sData value is %s ", this.sData);
}

display(); //sData value is display() Wisen // this => Window

object wrap:

let displayWrap = {
  sData: "displayWrap() Wisen",
  display() {
    console.log("sData value is %s ", this.sData);
  },
};

displayWrap.display(); //sData value is displayWrap() Wisen //this => displayWrap

没错正是利用:obj.fn()的 this 指向 obj 的特点

fn.apply()

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

let obj = {};

fn(1, 2); // this => window
fn.apply(obj, [1, 2]); // this => obj
fn.apply(null, [1, 2]); // this => window
fn.apply(undefined, [1, 2]); // this => window

可以看出:

  • apply 接受两个参数,第一个参数是 this 的指向,第二个参数是数组
  • 当第一个参数为 null、undefined 的时候,默认指向 window(在浏览器中)
  • 原函数会立即执行

fn.call()

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

let obj = {};

fn(1, 2); // this => window
fn.call(obj, [1, 2]); // this => obj
fn.call(null, [1, 2]); // this => window
fn.call(undefined, [1, 2]); // this => window

可以看出:

  • call 接受两个参数,第一个参数是 this 的指向,第二个参数是参数列表
  • 当第一个参数为 null、undefined 的时候,默认指向 window(在浏览器中)
  • 原函数会立即执行

和 apaly 唯一的区别就是 第二个参数不同

fn.bind()

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

let obj = {};

const bindFn = fn.bind(obj); // 需要执行依次 fn.bind()

fn(1,2) // this => window
bindFn.([1,2]); // this => obj
fn.bind()([1,2]); // this => window
fn.bind(null)([1,2]); // this => window

可以看出:

  • bind 接受两个参数,第一个参数是 this 的指向,第二个参数是参数列表
  • 当第一个参数为 null、undefined 的时候,默认指向 window(在浏览器中)
  • 返回一个改变 this 指向的函数

implement

fn.apply

Function.prototype.apply = function (thisArg, argsArray) {
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  const func = Symbol("func");
  thisArg[func] = this;

  let result;

  if (argsArray && typeof argsArray === "object" && "length" in argsArray) {
    result = thisArg[func](...Array.from(argsArray));
  } else {
    result = thisArg[func]();
  }

  delete thisArg[func];

  return result;
};

fn.call

Function.prototype.call = function (thisArg, ...argsArray) {
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  const func = Symbol("func");
  thisArg[func] = this;

  let result;

  if (argsArray.length) {
    result = thisArg[func](...Array.from(argsArray));
  } else {
    result = thisArg[func]();
  }

  delete thisArg[func];

  return result;
};

fn.bind()

Function.prototype.call = function (thisArg, ...argsArray) {
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  const func = this;

  const bound = function (...boundArgsArray) {
    return func.apply(
      this instanceof func ? this : thisArg,
      argsArray.concat(boundArgsArray)
    );
  };

  return bound;
};

references

  1. Function.prototype.apply()
  2. Function.prototype.call()
  3. Function.prototype.bind()

作者:shanejix 出处:https://www.shanejix.com/posts/Implementation fn.apply() fn.call() And fn.bind()/ 版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。 声明:转载请注明出处!

Edit on GitHubDiscuss on GitHub


Shane Jix

Personal blog by Shane Jix. I explain with words and code.

LinksTools
© 2019 - 2022, Built withGatsby