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
相关推荐
鸽鸽30 分钟前
别急着装第三方库,这些事浏览器原生就能做到——Web Native API 实战指南
前端
做前端的娜娜子32 分钟前
从 0 到 1 打造一个配置可视化后台:一次前端配置中心化改造的实战复盘
前端·设计模式·数据可视化
南雨北斗37 分钟前
vue3 RouterLink链接颜色修改的方法
前端
Richard.Wong37 分钟前
Windows IIS 服务器部署 Vue3 前端项目详细流程
服务器·前端·windows
数据知道38 分钟前
XSS 攻防全解:反射型、存储型、DOM 型实战演示
前端·安全·web安全·网络安全·xss
Nemo_XP39 分钟前
C# gridlookupedit选中内容重复还原操作
服务器·前端·c#
大家的林语冰1 小时前
🎉 Vercel 官宣 Next 16.3 正式发布,GitHub 第一全栈框架再次进化!
前端·javascript·前端框架
不爱说话郭德纲2 小时前
我只给了 TRAE Work 一张差评截图,它最后却把自己的 P0 结论推翻了?
前端·后端·架构
用户938515635072 小时前
从 DOM 编程到声明式 UI:React useRef 与 useState 底层全解析
前端·javascript·react.js
kisshyshy2 小时前
前端路由进化史:从刷新白屏到SPA,手写一个Hash路由就懂了!
前端·javascript·react.js