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
相关推荐
t***5442 小时前
如何在 Dev-C++ 中设置 MinGW 和 Clang 的路径
java·前端·c++
拜托啦!狮子2 小时前
安装EnsDb.Hsapiens.v86
java·服务器·前端
金玉满堂@bj2 小时前
playwright使用教程总结
前端
scheduleTTe2 小时前
Nginx
服务器·前端·nginx
techdashen2 小时前
不开端口,不配 DNS,用树莓派在家搭一个公网可访问的 Web 服务
前端·网络·智能路由器
早起傻一天~G3 小时前
vue2+element-UI表单封装
前端·vue.js·ui
pixcarp3 小时前
Nginx实战部署与踩坑总结 附带详细配置教程
服务器·前端·后端·nginx·golang
Live&&learn3 小时前
Vue项目打包后内联字符串不显示的原因
前端·javascript·vue.js
爱上好庆祝3 小时前
学习js的第三天
前端·css·人工智能·学习·计算机外设·js