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()
posted @   shanejix  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示
主题色彩