JavaScript之applye、bind和call方法详解

Question

  • Q1 apply()、bind()和call()方法的区别在哪?

  • Q2 apply()和call()的应用场景

  • Q3 apply()、bind()和call()方法手写实现逻辑

来源

继承自Function.prototype,属于实例方法

console.log(Function.prototype.hasOwnProperty('call')) //true
console.log(Function.prototype.hasOwnProperty('apply')) //true
console.log(Function.prototype.hasOwnProperty('bind')) //true

定义

apply()、bind()和call()方法的作用都是改变this指向,可以指定该函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

一般用法

// 限浏览器Console执行
var year = 2022 // 不能用let
function getDate(month, day) {
  console.log(this.year + '-' + month + '-' + day)
}

let obj = { year: 2023 }
getDate.call(this, 1, 1)    //2022-1-1 this|null|undefined均可
getDate.call(obj, 1, 1)     //2023-1-1
getDate.apply(obj, [1, 1])  //2023-1-1
getDate.bind(obj)(1, 1)     //2023-1-1

区别

  • apply()的参数为数组

  • apply()和call()立即执行,bind()返回新函数,并非立即执行

apply()

找出最大值和最小值
var arr = [5, 6, 2, 3, 7]
console.log(Math.max.apply(null, arr))
console.log(Math.min.apply(null, arr))

将数组的空元素变为undefined
// 空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined和null。因此,遍历内部元素的时候,会得到不同的结果
console.log(Array.apply(null, [1, , 3])) // [1, undefined, 3]
转换类似数组的对象
function keith(a, b, c) {
  return arguments
}

console.log(Array.prototype.slice.apply(keith(2, 3, 4)))    //[2,3,4]
console.log(Array.prototype.slice.call(keith(2, 3, 4)))   //[2,3,4]

bind()

var func = {
  a: 1,
  count: function () {
    console.log(this.a++)
  }
}

var ff = func.count.bind(func)

ff()

call()

调用对象的原生方法
var obj = {}
console.log(obj.hasOwnProperty('toString')) //false

obj.hasOwnProperty = function () {
  return true
}

console.log(obj.hasOwnProperty('toString')) //true

console.log(Object.prototype.hasOwnProperty.call(obj, 'toString')) //false

调用父构造函数
function Product(name, price){
  this.name = name
  this.price = price
}

// 调用父构造函数的call方法来实现继承
function Food(name, price){
  Product.call(this, name, price)
  this.category = 'food'
}

function Toy(name, price){
  Product.call(this, name, price)
  this.category = 'toy'
}

var cheese = new Food('feta', 5)
var fun = new Toy('robot', 40)

console.log(cheese)
console.log(fun)

手写

apply()
/**
 * 模拟 apply
 * 调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数
 * @param {object} ctx
 * @param {} args
 */
Function.prototype.__apply = function (ctx, args) {
    if (typeof this !== 'function') throw new TypeError('Error')

    // 考虑 null 情况,参数默认赋值会无效
    if (!ctx) ctx = window

    // 将 this 函数保存在 ctx 上
    ctx.fn = this

    // 传参执行并保存返回值
    const result = ctx.fn(...args)

    // 删除 ctx 上的 fn
    delete ctx.fn
  
    return result
}

// ------------------------------ 测试 ------------------------------

const numbers = [5, 6, 2, 3, 7]

// Function.prototype.__apply()
console.log('Function.prototype.__apply()')

const max = Math.max.__apply(null, numbers)
console.log(max) // 7

// Function.prototype.apply()
console.log('Function.prototype.apply()')
const min = Math.min.apply(null, numbers)
console.log(min) // 2
bind()
/**
 * 1. bind() 方法创建一个新的函数
 * 2. 在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数
 * 3. new 情况下忽略第一个参数
 * 4. 其余参数将作为新函数的参数,供调用时使用
 * @param {object} ctx
 * @param  {...any} args
 * @returns {function} 返回一个原函数的拷贝,并拥有指定 this 值和初始参数
 */
Function.prototype.__bind = function (ctx, ...args) {
    // 判断 this 是否为 function 类型
    if (typeof this !== 'function') throw new TypeError('Error')

    // 保存当前 this
    const __this = this

    return function F() {
        return this instanceof F
            ? new __this(...args, ...arguments) // new 
            : __this.apply(ctx, [...args, ...arguments]) // 直接调用时绑定 this
    }
}

// ------------------------------ 测试 ------------------------------

function print() {
    console.log(this.name, ...arguments)
}

const obj = {
    name: 'mxin',
}

// Function.prototype.__bind()
console.log('Function.prototype.__bind()')

// 直接调用,返回原函数拷贝,this 指向 obj
const F = print.__bind(obj, 26)
F(178) // mxin, 26, 178

// new 情况
const _obj = new F(145) // undefined, 26, 145
console.log(_obj) // print {}

// Function.prototype.bind()
console.log('Function.prototype.bind()')

const Fn = print.bind(obj, 26)
Fn(178) // mxin, 26, 178

const __obj = new Fn(145) // undefined, 26, 145
console.log(__obj) // print {}
call()
/**
 * 模拟 call
 * 使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数
 * @param {object} ctx
 * @param  {...any} args
 * @returns {any} 调用 this 的返回值,若无有返回值,则返回 undefined
 */
Function.prototype.__call = function (ctx, ...args) {
    if (typeof this !== 'function') throw new TypeError('Error')

    // 考虑 null 情况,参数默认赋值会无效
    if (!ctx) ctx = window

    // 将 this 函数保存在 ctx 上
    ctx.fn = this

    // 传参执行并保存返回值
    const res = ctx.fn(...args)

    // 删除 ctx 上的 fn
    delete ctx.fn
  
    return res
}

// ------------------------------ 测试 ------------------------------

function Product(name, price) {
    this.name = name
    this.price = price
}

// Function.prototype.__call()
console.log('Function.prototype.__call()')

function Food(name, price) {
    Product.__call(this, name, price)
    this.category = 'food'
}
const food = new Food('cheese', 5)
console.log(food)
// Food {name: "cheese", price: 5, category: "food"}
//   category: "food"
//   name: "cheese"
//   price: 5
//   __proto__:
//     constructor: ƒ Food(name, price)
//     __proto__: Object

// Function.prototype.call()
console.log('Function.prototype.call()')

function Toy(name, price) {
    Product.call(this, name, price)
    this.category = 'toy'
}
const toy = new Toy('car', 10)
console.log(toy)
// Toy {name: "car", price: 10, category: "toy"}
//   category: "toy"
//   name: "car"
//   price: 10
//   __proto__:
//     constructor: ƒ Toy(name, price)
//     __proto__: Object

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/792020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Fusion360修改嘉立创EDA专业版生成的3D外壳文件

需要第三方软件的原因 嘉立创EDA专业版生成电路板的3D外壳文件是比较快捷的,但如果侧面精密开孔或者添加其它非常规的元素还是有些局限。嘉立创EDA专业版可以把3D外壳文件导出,这就大大方便了第三方软件的修改。 本文是利用Fusion360修改3D外壳文件&…

Waifu2x:使用深度卷积神经网络的动漫风格艺术的图像超分辨率

Github网址:nagadomi/waifu2x:动漫风格艺术的图像超分辨率 (github.com) 该项目主要讲述的是如何利用预训练的深度学习模型来达到无损扩大收缩和去噪,对于一般训练图像的小伙伴应该很清晰图像经常要通过resize操作固定大小,然后c…

个人推荐Redis比较好的一种使用规范

随着对个人项目的不断开发、迭代和重构,博主在这个过程中总结出了一套使用redis的较好的规范。主要包含Redis的key命名规范和Redis代码规范。 主要内容 主要包含以下几个内容: 同一应用的key在最前面添加统一的前缀,如应用名; 案…

某华为hr擅自给我报名机

某华为hr擅自报名机考 上周,牛客网一位同学爆料: ❝ 今年一月份,华为一位 HR 通过某招聘网站联系到他,然后通过该 HR 加上了业务工程师的微信。随后,该工程师在「经过他本人同意」的情况下,在系统录入了他的…

CSGO比赛赛事大科普,Major并不是一个赛事!

关于CSGO比赛,有很多人都听过许多相关名词:Major、Minor、IEM、EPL、ESL ONE、Dreamhack、ESEA、Blast、EPICENTER等等,但大家有没有想过这些名词所代表的含义呢? Major、Minor严格意义上说,Major、Minor本身并不是赛事…

深入浅出 -- 系统架构之分布式架构

​​​​​​分布式架构: 根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务。 当垂直应用越来越多时,应用之间的交互不可避免,可将共用的基础服务或核心模块抽取出来作为独立服务&#xff0c…

接口练习题目

练习一 1、声明接口Eatable,包含抽象方法public abstract void eat(); 2、声明实现类中国人Chinese,重写抽象方法,打印用筷子吃饭 3、声明实现类美国人American,重写抽象方法,打印用刀叉吃饭 4、声明实现类印度人Indi…

GDPU 竞赛技能实践 天码行空6

📖 敌兵布阵 C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工…

使用PostgreSQL中的隐式转换解决,MybatisPlus插入数据库时的类型不一致的问题

使用PostgreSQL中的隐式转换解决,MybatisPlus插入数据库时的类型不一致的问题 问题描述 鄙人在使用 MybatisPlus插件开发一个SpringBoot项目时, 遇到数据库中employee表与Java实体对象中某个属性的类型不一致, 导致插入数据库失败. 具体问题截图如下: 具体原因在于, Java实体…

Java入门教程||Java 多线程编程

Java 多线程编程 Java 给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径。 多线程是多任务的一种特别的形式。多线程比多任务需要更小的开销。 这里定义和线程…

usbserial驱动流程解析_Part1_主要函数

本系列解析usbseiral ko的源码,记录主要函数,调用流程,USB一端和串口一端的注册流程,本节简介主要函数以及替换规则。 首先,usbserial是USB转串口驱动的一个基础模板,其中有许多默认函数,他们的…

书生·浦语训练营二期第二次课后作业

文章目录 基础作业使用 InternLM2-Chat-1.8B 模型生成 300 字的小故事 进阶作业熟悉 huggingface 下载功能完成 浦语灵笔2 的 图文创作 及 视觉问答 部署图文创作视觉问答 完成 Lagent 工具调用 数据分析 Demo 部署 作业详细操作步骤见第二次课笔记:https://blog.cs…

CSS层叠样式表学习(字体属性)

(大家好,今天我们将继续来学习CSS字体属性的相关知识,大家可以在评论区进行互动答疑哦~加油!💕) 三、CSS字体属性 CSS Fonts(字体)属性用于定义字体系列、大小,粗细,和文字样式(如斜…

list的常用接口底层实现与介绍

目录 概念: list的基本结构: list的迭代器⭐❤: 自定义类型的完善: const的迭代器: insert erase: size empty push_back 、push_front 、pop_back、pop_front swap 、operator 析构函数…

什么是CSGO游戏搬砖及游戏搬砖注意事项?

CSGO市场是指《反恐精英:全球攻势》游戏内的物品交易市场。玩家可以在这个市场上买卖各类虚拟物品,包括武器皮肤、刀具、手套等。CSGO市场的价格是由供需关系、稀有度、流行度等多个因素影响的。 一般来说,稀有度较高或者比较受欢迎的物品价格…

vscode安装通义灵码

作为vscode的插件,直接使用 通义灵码-灵动指间,快码加编,你的智能编码助手 通义灵码,是一款基于通义大模型的智能编码辅助工具,提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码注释生成、代码解释、研…

python--不死兔子问题

def rabbit(n):if n < 3:return 1return rabbit(n - 1) rabbit(n - 3)if __name__ __main__:print(rabbit(4))

1.Docker简介和安装

1 Docker 简介 1.1 Docker 是什么&#xff1f; docker是一个开源的应用容器引擎。 1.2 容器是什么&#xff1f; 容器是一种轻量级的虚拟化技术 &#xff0c;它是一个由应用运行环境、容器基础镜像组成的集合。 以 Web 服务 Nginx 为例&#xff0c;如下图所示&#xff1a;Ngin…

算法 - 符号表-下

&#x1f600;前言 推荐从上看到下 算法 - 符号表-上 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 算法 - 符号表查找树1. 插入操作2. 性质 红黑树1. 左旋转2. 右旋转3. 颜色转换4. 插入5. 分析 散列表1. 散列函数2. 拉链法3. 线性探测法3.1 查找3.2 插入3.3 删除3.5 …

外卖配送时间预测项目

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 项目背景 外卖服务的兴起: 随着互联网技术和移动应用的发展&#xff0c;外卖成为一种日益普及的餐饮服务方式。顾客通过餐厅、杂货店的网站或移…