call(), appy(),bind() 之间的区别与使用方法,自己实现这三个函数

call: 调用一个函数时,指定this,并分别传入参数

js 复制代码
Function.prototype.call(thisArg, arg1, arg2, /* ..., */ argN) 

演示代码:

js 复制代码
function sayName(...args) {
  console.log(this.name, ...args);
}

let person = {
  name: '张三',
};

sayName();

sayName.call(person, '参数1', '参数2');

自己实现call:

核心原理:把函数挂载 到要指向的对象上,执行这个函数,然后删掉这个临时属性

js 复制代码
Function.prototype.call2 = function (thisArg, ...args) {
  // 第一步:获取call绑定的对象
  let context = thisArg ?? globalThis;
  // 第二步:将被调用函数设置为对象的属性
  let fnName = Symbol('fn');
  context[fnName] = this;
  // 第三步:调用函数
  let result = context[fnName](...args);
  // 第四步:删除属性
  delete context[fnName];
  // 第五步:返回结果
  return result;
};

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

sayName();

let person = {
  name: '张三',
};

sayName.call2(person, '参数1', '参数2');

apply: 调用一个函数时,指定this,将参数封装为数组或者类数组传入

js 复制代码
Function.prototype.call(thisArg, argsArray) 

演示代码:

js 复制代码
function sayName(a, b) {
  console.log(this.name, a, b);
}

sayName('参数1', '参数2');

let person = {
  name: '张三',
};

sayName.apply(person, ['参数1', '参数2']);

自己实现apply:

核心原理:把函数挂载 到要指向的对象上,执行这个函数,然后删掉这个临时属性

globalThis:globalThis 是 JS 官方的 "全局对象,览器里 → globalThis === window,Node 里 → globalThis === global

js 复制代码
Function.prototype.apply2 = function (thisArg, argsArray) {
  // 第一步:获取apply绑定的对象
  let context = thisArg ?? globalThis;
  // 第二步:判断第二个参数不传的情况
  const args = argsArray ? [...argsArray] : [];
  // 第三步:将被调用函数设置为对象的属性
  let fnName = Symbol('fn');
  context[fnName] = this;
  // 第四步:调用函数
  let result = context[fnName](...args);
  // 第五步:删除属性
  delete context[fnName];
  // 第六步:返回结果
  return result;
};

function sayName(a, b) {
  console.log(this.name, a, b);
}

sayName('参数1', '参数2');

let person = {
  name: '张三',
};

sayName.apply2(person, ['参数1', '参数2']);

bind: 创建一个新函数,并指定该新函数运行时的this,并将指定的参数一一插入到原函数参数的前面

js 复制代码
let newFunc = Function.prototype.bind(thisArg,  arg1, arg2, /* ..., */ argN)

演示代码:

js 复制代码
function sayName(a, b, c, ...args) {
  console.log(this.name, a, b, c, ...args);
}

let person = {
  name: '张三',
};

let newSayName = sayName.bind(person, '参数1', '参数2', '参数3');

newSayName('参数4', '参数5', '参数6');

自己实现bind:

注意点:

  1. bind用于创建一个新函数,故应该返回一个函数,第一个参数是被绑定对象,后续参数用剩余参数接收

  2. bind返回的新函数若通过new关键字调用,则其this应该绑定到新创建的对象上而不是调用bind时指定的。所以需要通过new.target去判断函数的调用方式。

  3. 可以通过call()函数完成一部分bind函数的实现。

js 复制代码
Function.prototype.bind2 = function (thisArg, ...args) {
  // 第一步:获取bind绑定的对象
  let context = thisArg ?? globalThis;
  // 保存原函数
  let fn = this;

  return function (...newArgs) {
    // 如果函数是通过 new 调用的,this 应该指向新创建的对象,而不是 thisArg
    context = new.target ? this : context;
    // 调用原函数
    return fn.call(context, ...args, ...newArgs);
  };
};

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

let person = {
  name: '张三',
};

let newSayName = sayName.bind2(person, '参数1', '参数2');

newSayName('参数3', '参数4'); // false 张三 参数1 参数2 参数3 参数4

new newSayName('参数5', '参数6'); // true undefined 参数1 参数2 参数5 参数6

new.target

ES6新增new.target,用于判断当前函数是被new构造调用还是被普通调用。

被new构造调用时,new.target的值是构造函数本身

被当作普通函数调用时,new.target的值是undefined

js 复制代码
function person(name, age) {
  this.name = name;
  this.age = age;
  console.log(new.target);
}

const p1 = new person('summer', 18); // [Function: person]

person('summer', 18); // undefined
相关推荐
pkowner4 分钟前
若依分页问题及解决方法
java·前端·算法
golang学习记12 分钟前
Cursor官方团队的AI指南:Cursor Team Kit
前端·cursor
Lee川17 分钟前
RAG 知识库问答:从概念到代码的完整实现
前端·人工智能·后端
计算机安禾1 小时前
【c++面向对象编程】第22篇:输入输出运算符重载:<< 与 >> 的友元实现
java·前端·c++
redreamSo1 小时前
14 小时烧光 200 美金:Codex 和 Claude 的 /goal 命令打开了"放手跑"模式
前端
TingTing1 小时前
Webpack5 前端工程化建设
前端
A不落雨滴AI1 小时前
DKERP客户端重构纪实:4天自研控件库的“短命”教训,以及为什么我坚定选择原生Qt
前端
我叫黑大帅1 小时前
通过白名单解决 pnpm i 报错 Ignored build scripts
前端·javascript·面试
风止何安啊1 小时前
用 APP 背单词太无聊?我用 Trae Solo 移动端写个小游戏来准备 6级
前端·人工智能·trae
Summer不秃1 小时前
深入理解 Token 无感刷新:从并发雪崩到单例锁 + 请求队列的完整实现
前端·http