主题
自实现bind、call方法
JavaScript
//原生JavaScript封装apply方法,第四版
Function.prototype.applyFour = function(context) {
var context = context || window
var args = arguments[1] //获取传入的数组参数
var fn = Symbol()
context[fn] = this //假想context对象预先不存在名为fn的属性
if (args == void 0) { //没有传入参数直接执行
return context[fn]()
}
var fnStr = 'context[fn]('
for (var i = 0; i < args.length; i++) {
//得到"context.fn(arg1,arg2,arg3...)"这个字符串在,最后用eval执行
fnStr += i == args.length - 1 ? args[i] : args[i] + ','
}
fnStr += ')'
var returnValue = eval(fnStr) //还是eval强大
delete context[fn] //执行完毕之后删除这个属性
return returnValue
}
JavaScript
//简单模拟Symbol属性
function jawilSymbol(obj) {
var unique_proper = "00" + Math.random();
if (obj.hasOwnProperty(unique_proper)) {
arguments.callee(obj)//如果obj已经有了这个属性,递归调用,直到没有这个属性
} else {
return unique_proper;
}
}
//原生JavaScript封装apply方法,第五版
Function.prototype.applyFive = function(context) {
var context = context || window
var args = arguments[1] //获取传入的数组参数
var fn = jawilSymbol(context);
context[fn] = this //假想context对象预先不存在名为fn的属性
if (args == void 0) { //没有传入参数直接执行
return context[fn]()
}
var fnStr = 'context[fn]('
for (var i = 0; i < args.length; i++) {
//得到"context.fn(arg1,arg2,arg3...)"这个字符串在,最后用eval执行
fnStr += i == args.length - 1 ? args[i] : args[i] + ','
}
fnStr += ')'
var returnValue = eval(fnStr) //还是eval强大
delete context[fn] //执行完毕之后删除这个属性
return returnValue
}
JavaScript
//原生JavaScript封装call方法
Function.prototype.callOne = function(context) {
return this.applyFive(([].shift.applyFive(arguments), arguments)
//巧妙地运用上面已经实现的applyFive函数
}
es5-shim实现bind
JavaScript
var $Array = Array;
var ArrayPrototype = $Array.prototype;
var $Object = Object;
var array_push = ArrayPrototype.push;
var array_slice = ArrayPrototype.slice;
var array_join = ArrayPrototype.join;
var array_concat = ArrayPrototype.concat;
var $Function = Function;
var FunctionPrototype = $Function.prototype;
var apply = FunctionPrototype.apply;
var max = Math.max;
// 简版 源码更复杂些。
var isCallable = function isCallable(value){
if(typeof value !== 'function'){
return false;
}
return true;
};
var Empty = function Empty() {};
// 源码是 defineProperties
// 源码是bind笔者改成bindFn便于测试
FunctionPrototype.bindFn = function bind(that) {
var target = this;
if (!isCallable(target)) {
throw new TypeError('Function.prototype.bind called on incompatible ' + target);
}
var args = array_slice.call(arguments, 1);
var bound;
var binder = function () {
if (this instanceof bound) {
var result = apply.call(
target,
this,
array_concat.call(args, array_slice.call(arguments))
);
if ($Object(result) === result) {
return result;
}
return this;
} else {
return apply.call(
target,
that,
array_concat.call(args, array_slice.call(arguments))
);
}
};
var boundLength = max(0, target.length - args.length);
var boundArgs = [];
for (var i = 0; i < boundLength; i++) {
array_push.call(boundArgs, '$' + i);
}
// 这里是Function构造方式生成形参length $1, $2, $3...
bound = $Function('binder', 'return function (' + array_join.call(boundArgs, ',') + '){ return binder.apply(this, arguments); }')(binder);
if (target.prototype) {
Empty.prototype = target.prototype;
bound.prototype = new Empty();
Empty.prototype = null;
}
return bound;
};