JavaScript之applye、bind和call方法详解

本文详细介绍了JavaScript中的apply(),bind(),和call()方法,包括它们的作用、一般用法、应用场景、参数特性以及手写实现逻辑。重点讲解了这三种方法在改变this指向、处理数组参数和继承方面的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 多平台发布

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值