面试高频手写题005-实现call/apply/bind

这篇文章详细解释了JavaScript中call、apply和bind方法的区别,并提供了它们的手动实现。call和apply都用于改变函数内部的this指向并立即执行,区别在于传参方式,而bind则返回一个新的函数,其this指向被绑定的对象,但不会立即执行。文章还通过测试用例展示了如何使用这三个方法。
摘要由CSDN通过智能技术生成

一、call/apply/bind的区别

// call
obj.fn.call(newObj, 1, 2, 3)

// apply
obj.fn.apply(newObj, [1, 2, 3])

// bind
var newfn = obj.fn.bind(newObj, 1, 2, 3)
newfn()
  • 三者都可以用来改变this指针的方向

  • call和apply绑定的函数都是立即执行,而bind会返回一个函数不会立即执行

  • call和bind的传参形式相同,而apply的第二个参数需要为数组形式

二、手写实现call

分析:

  • 需要定义在Function.prototype上

  • 两个参数:第一个参数为this指向的目标对象,后面的参数是传入fn的参数,个数不定

  • 要改变fn里的this指向obj,同时会立即执行该函数fn,返回函数执行的结果

// 手写实现call
Function.prototype.myCall = function (obj, ...args) {
    obj = obj == null ? Window : new Object(obj)

    let fnkey = Symbol()  // 唯一key,不会出现属性名称的覆盖
    obj[fnkey] = this  // this就是当前的函数fn,此时函数内部的this就会指向obj

    let result = obj[fnkey](...args)  // 相当于执行fn,此时fn里的this是指向obj的

    delete obj[fnkey]  // 清理掉obj[fnkey],防止污染

    return result  // 返回结果
}


// 测试用例
function fn(a, b) {
    console.log(a + b)
    console.log(this.name)
}
let obj = {
    name: 1
}
fn.myCall(obj, 1, 2)

三、手写实现apply

分析:与call的实现相比,就是传参形式不同而已,其他都一样

// 手写实现apply
Function.prototype.myApply = function (obj, args) { // 注意这里传参与call不同
    obj = obj == null ? Window : new Object(obj)

    let fnkey = Symbol()  // 唯一key,不会出现属性名称的覆盖
    obj[fnkey] = this  // this就是当前的函数fn,此时函数内部的this就会指向obj

    let result = obj[fnkey](...args)  // 相当于执行fn,此时fn里的this是指向obj的

    delete obj[fnkey]  // 清理掉obj[fnkey],防止污染

    return result  // 返回结果
}


// 测试用例
function fn(a, b) {
    console.log(a + b)
    console.log(this.name)
}
let obj = {
    name: 1
}
fn.myCall(obj, [1, 2])

四、手写实现bind

分析:

  • 需要定义在Function.prototype上

  • 两个参数: 第一个参数为this指向的目标对象,后面的参数是传入fn的参数,个数不定

  • 返回一个函数,函数执行的时候里面的this指向目标对象obj

  • 可能这样使用:fn.bind(obj, 1)(2),所以要进行参数拼接

  • 返回一个函数,要考虑到返回的函数作为普通函数和构造函数的两种使用场景

  • 普通函数:改变this指向,函数执行的时候里面的this指向目标对象obj

  • 构造函数:不能改变this指向,还要保留原函数原型上的属性

// 手写实现bind
Function.prototype.myBind = function (obj, ...args) {
    let self = this  // 这里的this就是调用bind的函数fn

    let newfn = function (...innerArgs) {
        // 作为构造函数时,this指向实例,不能改变this指向;作为普通函数时,该项this指向,指向obj
        // 注意参数拼接
        return self.apply(this instanceof newfn ? this : obj, args.concat(innerArgs))  
    }

    // 如果是构造函数,需要继承原函数原型上的属性和方法
    newfn.prototype = new Object(this.prototype)
    return newfn
}


// 测试用例

function Person(name, age) {
    console.log('Person name:', name)
    console.log('Person age:', age)
    console.log('Person this:', this) // 构造函数this指向实例对象
}
  
// 构造函数原型的方法
Person.prototype.say = function() {
    console.log('person say')
}
  
// 普通函数
function normalFun(name, age) {
    console.log('普通函数 name:', name)
    console.log('普通函数 age:', age)
    console.log('普通函数 this:', this)  // 普通函数this指向绑定bind的第一个参数 也就是例子中的obj
}


var obj = {
    name: 'poetries',
    age: 18
}

// 先测试作为构造函数调用
var bindFun = Person.myBind(obj, 'poetry1') // undefined
var a = new bindFun(10) // Person name: poetry1、Person age: 10、Person this: fBound {}
a.say() // person say

// 再测试作为普通函数调用
var bindNormalFun = normalFun.myBind(obj, 'poetry2') // undefined
bindNormalFun(12) 
// 普通函数name: poetry2 
// 普通函数 age: 12 
// 普通函数 this: {name: 'poetries', age: 18}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值