call、apply、bind 以及 new 手写

目录

1. call

2. apply

3. bind

4. new

5. 测试


1. call

模拟实现代码如下:

// 函数用法简单示例 func.call(obj, param1, param2)
    Function.prototype.myCall = function(context){
        context = context || window; // context 是要调用函数的那个对象,即 obj
        context.fn = this; // this 指的是调用 call 方法的那个函数,即 func,这里相当于让 obj 的 fn 属性指向 func
        let args = [...arguments].slice(1); // 参数 param1 param2
        let re = context.fn(...args); // 在 obj 上调用 func 方法,re 为返回值
        delete context.fn; // 删除对象 obj 上的 fn 属性,相当于解除对象 obj.fn 对 func 的引用
        return re; // 函数返回
    }

2. apply

模拟实现代码如下:

// 函数用法简单示例 func.apply(obj, [param1, param2])
    Function.prototype.myApply = function(context){
        context = context || window;
        context.fn = this;
        let re;
        if(arguments[1]){
            re = context.fn(...arguments[1]);
        }else{
            re = context.fn();
        }
        delete context.fn;
        return re;
    }
前面两个方法实现总结:
它们相当于在某个对象里面添加了一个属性,该属性指向需要执行的函数,然后在调用了函数后从该对象上移除添加的属性。

3. bind

模拟实现代码如下:

// 函数用法简单示例 func1 = func.bind(obj, param1, param2)
    Function.prototype.myBind = function (context) {
        let _this = this; // 保留对当前 this 的引用,即函数 func,后续会用到
        let args = [...arguments].slice(1);
        return function F(){ // 会形成闭包
            if(this instanceof F){ // 作为构造函数时,this 会指向 F 创建的实例对象
                return new _this(...args,...arguments); // args 是开始绑定时传入的参数,arguments 接受新传入的参数
            }
            // 后续普通调用时
            return _this.apply(context, args.concat(...arguments));
        }
    }

4. new

模拟实现代码如下:

function myNew() {
        let Constructor = [].shift.call(arguments); // 这里修改了 arguments(严格模式下也有效),可用添加 'use strict' 进行测试
        if(typeof Constructor !== 'function'){
            throw new Error('The first arguments of new must be a function');
        }
        let obj = {};
        Object.setPrototypeOf(obj, Constructor.prototype); // 原型链做链接
        let res = Constructor.apply(obj, arguments); // 相当于构造函数 Constructor 调用,可能在 obj 上添加一些属性
        return res instanceof Object ? res : obj; // 如果返回值是隐式返回,则返回新创建的对象 res,否则返回显示返回的对象 obj(这和Constructor函数是否有返回值有关)
    }

5. 测试

上述模拟实现测试:

// 测试代码
    var name = 'A';
    var obj = {
        name:'B'
    }
    function outputName(param1, param2){
        console.log(param1, param2, this.name);
    }

    function Fn(name, age){
        this.name = name;
        this.age = age;
    }

    // 函数调用
    outputName.myCall(obj, 1, 2); // 1 2 'B'
    outputName.myApply(obj, [1, 2]); // 1 2 'B'
    outputName.myBind(obj, 1, 2)(); // 1 2 'B'
    console.log(myNew(Fn,'小明', 22)); // Fn {name: '小明', age: 22}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值