这是网上的一道面试题,检测JavaScript基本功掌握的怎么样以及平时有没有去深入研究一些方法的实现,简而言之,就是有没有折腾精神。在模拟之前我们先要明白和了解原生call和apply、bind方法是什么。
实现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和apply、bind方法是什么。
参考:https://github.com/jawil/blog/issues/16