call,apply,bind 梳理

闲言碎语我要讲

  • call,apply,bind 都是改变 this 指向的方法

  • 用的时候不知道该用哪一个,在此梳理一下,加深印象

call

  • call() 函数接受多个参数,第一个参数为 this 指向对象,后面参数为传给调用函数的参数,返回值是使用指定的 this 值和参数调用函数后的结果。
scss 复制代码
call(thisArg, arg1, arg2, /* ..., */ argN)
  • 如果省略第一个 thisArg 参数,则默认为 undefined。在非严格模式下,this 值将被替换为 globalThis(类似于全局对象)。
js 复制代码
var animal = '狗'
var sleepDuration = "6 到 8 小时"

function greet() {
    console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间");
}

const obj = {
    animal: "猫",
    sleepDuration: "12 到 16 小时",
};

greet.call(obj); // 猫 的睡眠时间一般在 12 到 16 小时 之间
greet.call(); // 狗 的睡眠时间一般在 6 到 8 小时 之间


### 在greet.call(obj)执行时,greet()函数内的this指向了obj这个对象
### 就像是obj添加了一个属性greet,值就是greet函数,obj.greet = greet
### greet.call(obj) 就变成了 obj.greet()
  • 手写 call
js 复制代码
Function.prototype.zyCall = function(thisArg,...args){
    const fn = this
    // 使用时 fn.zyCall(obj),this始终指向调用它的对象,this == fn
    
    thisArg = (thisArg !== null && thisArg !==undefined) ? Object(thisArg) : window

    thisArg.fn = fn
    const res = thisArg.fn(...args)
    delete thisArg.fn

    return res
}

apply

  • 这个函数与 call() 几乎完全相同,只是函数参数在 call() 中逐个作为列表传递,而在 apply() 中它们会组合在一个类数组对象中,用于指定调用 func 时的参数,或者如果不需要向函数提供参数,则为nullundefined

  • 手写 apply

js 复制代码
Function.prototype.zyApply = function(thisArg,argArray){
    const fn = this
    // 使用时 fn.zyCall(obj),this始终指向调用它的对象,this == fn
    
    thisArg = (thisArg !== null && thisArg !==undefined) ? Object(thisArg) : window

    thisArg.fn = fn
    const res = thisArg.fn(...argArray)
    delete thisArg.fn

    return res
}

bind

  • bind() 方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。(这里有两个参数,一个是调用bind()时的参数arg1,一个是调用新函数时的参数arg2,新函数的实际参数是arg1和arg2两个)
js 复制代码
bind(thisArg, arg1, arg2, /* ..., */ argN)

// 顶级的"this"绑定到"globalThis"。
var x = 9;
const module = {
    x: 81,
    getX() {
         return this.x;
    },
};

// "getX"的"this"参数绑定到"module"。
console.log(module.getX()); // 81

const retrieveX = module.getX;
// "retrieveX"的"this"参数在非严格模式下绑定到"globalThis"。
console.log(retrieveX()); // 9

// 创建一个新函数"boundGetX",并将"this"参数绑定到"module"。
const boundGetX = retrieveX.bind(module);
console.log(boundGetX()); // 81
  • bind() 函数创建一个新的绑定函数(bound function) 。调用绑定函数通常会执行其所包装的函数,也称为目标函数(target function) 。绑定函数将绑定时传入的参数(包括 this 的值和前几个参数)提前存储为其内部状态。而不是在实际调用时传入。通常情况下,你可以将 const boundFn = fn.bind(thisArg, arg1, arg2)const boundFn = (...restArgs) => fn.call(thisArg, arg1, arg2, ...restArgs) 构建的绑定函数的调用效果视为等效(但就构建 boundFn 的过程而言,不是二者等效的)。

  • 绑定函数可以通过调用 boundFn.bind(thisArg, /* more args */) 进一步进行绑定,从而创建另一个绑定函数 boundFn2。新绑定的 thisArg 值会被忽略,因为 boundFn2 的目标函数是 boundFn,而 boundFn 已经有一个绑定的 this 值了。当调用 boundFn2 时,它会调用 boundFn,而 boundFn 又会调用 fnfn 最终接收到的参数按顺序为:boundFn 绑定的参数、boundFn2 绑定的参数,以及 boundFn2 接收到的参数。

js 复制代码
"use strict"; // 防止 `this` 被封装到到包装对象中

function log(...args) {
  console.log(this, ...args);
}
const boundLog = log.bind("this value", 1, 2);
const boundLog2 = boundLog.bind("new this value", 3, 4);
boundLog2(5, 6); // "this value", 1, 2, 3, 4, 5, 6
  • 手写 bind
js 复制代码
Function.proptype.zyBind = function (thisArg, ...argArray) {
     //1.获取真实需要调用的函数
     const fn = this
     //2. 绑定this
     thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

     function proxyFn(...args) {
         //3.将函数放到thisArg中进行调用
         thisArg.fn = fn
         //特殊:对两个传入的参数进行合并
         const finalArgs = [...argArray, ...args]
         const res = thisArg.fn(...finalArgs)
         delete thisArg.fn
         //4.返回结果
         return res
      }
      //返回要调用的函数本身,这里是添加过this绑定值的函数
      return proxyFn
 }

总结

  • call, apply 要稍微简单一点,bind 难一点,讲得不是很清楚,具体戳这
相关推荐
小二·11 分钟前
layui树形组件点击树节点后高亮的解决方案
前端·javascript·layui
Minions_Fatman12 分钟前
【layui】table的switch、edit修改
前端·javascript·layui
小孙姐28 分钟前
4——单页面应用程序,vue-cli脚手架
前端·javascript·vue.js
生椰拿铁You29 分钟前
15 —— Webpack中的优化——前端项目使用CDN技术
前端·webpack
生椰拿铁You29 分钟前
13 —— 开发环境调错-source map
前端
知野小兔42 分钟前
【Angular】async详解
前端·javascript·angular.js
来啦来啦~1 小时前
vue项目实现动效交互---lottie动画库
前端·vue.js·交互
没了对象省了流量ii1 小时前
11.9K Star!强大的 Web 爬虫工具 FireCrawl:为 AI 训练与数据提取提供全面支持
前端·人工智能·爬虫
我的div丢了肿么办1 小时前
vue项目中如何加载markdown作为组件
前端·javascript·vue.js
南山十一少1 小时前
Spring Boot 实战:基于 Validation 注解实现分层数据校验与校验异常拦截器统一返回处理
java·前端·spring boot·后端