js手撕——bind的模拟实现

在前端面试中,可能会出现一些手撕模拟实现的题目,这里进行一些记录。

原生api

js中中提供了 call, apply, bind 用于手动指定this的指向

概念

调用 call 和 apply 的对象,必须是一个函数 Function

由于apply()方法(或者call()方法)不仅绑定函数执行时所在的对象,还会立即执行函数

第一个参数,是一个对象, 由该对象调用Function

s.Function.call(obj)
// 相等于 obj.Function

A.sayHello.call();//不传参数指向全局window对象,输出window.name也就是window
//如果call方法没有参数,或者参数为null或undefined,则等同于指向全局对象。

call 和 apply 的主要作用,是改变对象的执行上下文,并且是立即执行的。它们在参数上的写法略有区别。

bind 也能改变对象的执行上下文,它与 call 和 apply 不同的是,返回值是一个函数,并且需要稍后再调用一下,才会执行。

call(),apply(),bind()都是用来重定义this对象的

这三个函数的第一个参数都是 this 的指向对象;

第二个参数的差别:

call 的参数直接放进去,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,‘n1’, … ,‘n’ )

apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,[‘n1’, …, ‘m’ ])

bind 除了返回是函数以外,它的参数和 call 一样。

当然,三者的参数不限定是 string 类型,允许是各种类型,包括函数 、 object 等等!

var obj = {
    name:'tzb',
    age:18,
    myFunc:function(n, a){
        console.log(this.name + "年龄" + this.age);
        console.log(n + "年龄" + a);
    }
}

var db = {
    name :'zhi',
    age:22
}
// call apply bind 都是挂到Object上的函数
//1.call
obj.myFunc(); // tzb 年龄18
// obj.myFunc().call()是错误写法 ()表示立即执行函数 myFunc()执行完成之后并没有返回值
obj.myFunc.call(db,'bin',33) // zhi年龄22 /n bin年龄33

//2.apply
obj.myFunc.apply(db,['bin',33]) // zhi年龄22  bin年龄33

//3.bind
var f = obj.myFunc.bind(db,'bin',33);
// bind返回的是一个函数 加上()才会执行
f();  // zhi年龄22  bin年龄33
模拟实现

bind的模拟实现使用到了 apply ,这里先给出apply的模拟实现

apply
// args传入的需要是一个数组
function myApply(context, args) {
    if (typeof this !== 'function') {
        throw new Error('调用者必须是函数');
    }
    context = context || window;
    // 手动给调用者添加一个方法
    context.fn = this;
    let res;
    if (args) {
        res = context.fn(...args);
    }else {
        res = context.fn();
    }
    // 获得结果后删除该属性
    delete context.fn;
    return res;
}

Function.prototype.myApply = myApply;
bind模拟实现
// bind(thisObj, a1, a2, a3);

function myBind(...args) {
    if (typeof this !== 'function') {
        throw new Error('调用者必须是函数');
    }
    const fn = this;
    const context = args[0] || window;
    const myArgs = args.slice(1);

    return function(...newArgs) {
        fn.apply(context, myArgs.contact(newArgs));
    }

}

上述模拟实现没有考虑bind返回的函数作为构造函数的情况。
在构造函数中,this指向新生成的对象。那么下面这个就不合适了。

  return function(...newArgs) {
        fn.apply(context, myArgs.contact(newArgs));
    }

考虑构造函数的情况

// bind(thisObj, a1, a2, a3);

function myBind(...args) {
    if (typeof this !== 'function') {
        throw new Error('调用者必须是函数');
    }

    // this为调用bind的函数对象
    const fn = this;
    const context = args[0] || window;
    const myArgs = args.slice(1);

    let Temp = function(){};
    Temp.prototype = fn.prototype;
    // bind返回的函数 f 作为构造函数时 
    let f =  function(...newArgs) {
        return fn.apply(this instanceof f ? this : context, myArgs.contact(newArgs));
    }
    // 继承原型链上的属性
    f.prototype = new Temp();

    return f;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值