模拟实现apply、call、bind方法

2019-06-202036次阅读javascript

这是网上的一道面试题,检测JavaScript基本功掌握的怎么样以及平时有没有去深入研究一些方法的实现,简而言之,就是有没有折腾精神。在模拟之前我们先要明白和了解原生call和applybind方法是什么。

实现apply方法

//context的fn属性唯一性
function symbolProto(obj){

    var proto = "00" + Math.random();

    if( obj.hasOwnProperty(proto) ){

        arguments.callee( obj );

    }else{

        return proto;
    }

}
//原生JavaScript封装apply方法
Function.prototype.simulationApply = function(context){

    var context = context || window,//当context为null的时候,视为指向window

        args = arguments[1],//获取传入的数组参数

        fn = symbolProto( context );//context的fn属性唯一性

    context[fn] = this;// 获取调用的函数,用this可以获取

    if(typeof args == 'undefined') { //没有传入参数直接执行,call,apply的第二个参数是可选的

        return context[fn]();
    }


  //var returnValue = eval('context[fn]('+ ( args.length && args.join(',') ) +')'); 类数组没有数组方法当然包括join

  var par = 'context[fn](',
    i = 0,
    len = args.length;

  for(; i < len; i++) {
      //得到"context.fn(arg1,arg2,arg3...)"这个字符串在,最后用eval执行
      par += i == args.length - 1 ? args[i] : args[i] + ',';
  }

  par += ')';

    var returnValue = eval(par); //eval()函数会将传入的字符串当做JavaScript代码进行执行

    delete context[fn]; //执行完毕之后删除这个属性

    return returnValue;

}

var obj = {
    name: 'china'
}

function sayHello(age) {
    return {
        name: this.name,
        age: age
    }
}

console.log( sayHello.simulationApply(obj,[26]) );//{name: "china", age: 26}

console.log( Math.max.simulationApply(null,[3,8,2,10]) );//10

实现Call方法

//原生JavaScript封装call方法
Function.prototype.simulationCall = function(context) {

    return this.simulationApply( [].shift.simulationApply(arguments),arguments );
    //巧妙地运用上面已经实现的simulationApply函数
}
console.log( sayHello.simulationCall(obj,29) );//{name: "china", age: 29}
console.log( Math.max.simulationCall(null,3,8,2,20) );//20
console.log( [].shift.simulationCall([4,5]) );//4 强调一下call,apply的第二个参数是可选的

实现bind方法

Function.prototype.simulationBind = Function.prototype.bind || function (context) {//“嗅探”,进行兼容处理
    var me = this;//获取调用的函数,用this可以获取
    var args = Array.prototype.slice.simulationCall(arguments, 1);//bind拥有预设的初始参数
    var F = function () {};//bind返回的函数如果作为构造函数,搭配new关键字出现的话,我们的绑定this就需要“被忽略”
    F.prototype = this.prototype;
    var bound = function () {
        var innerArgs = Array.prototype.slice.simulationCall(arguments);
        var finalArgs = args.concat(innerArgs);//预设的初始参数+实参
        return me.simulationApply(this instanceof F ? this : context || this, finalArgs);
    }
    bound.prototype = new F();
    return bound;
}
console.log(sayHello.bind(obj,24)());//{name: "china", age: 24}

如果有点吃力,就慢下来,小步向前,重新回顾原生call和applybind方法是什么。

参考:https://github.com/jawil/blog/issues/16

上一篇: JS压缩后IE低版本不兼容,试试把uglifyjs-webpack-plugin升级到2+  下一篇: 为什么 ['1', '7', '11'].map(parseInt) 返回 [1, NaN, 3]  

模拟实现apply、call、bind方法相关文章