一直以来在看这些手写自定义方法,其实目的不是看实现结果,而是看过程的思路,从中会学习到一些逻辑的思维,虽然都是一些简单的小方法,但是还是值得我们一看的。
手写call
第一个我们就来看看这个call,可能大家都见惯不惯了,但还是以call 为例总结一下,apply 方法思路相同。首先我们来看看自带call方法的用途吧...
var value = 'this is window';
var foo = {
value : 'this is foo'
}
function bar(name,age){
console.log(this.value);
}
bar.call(foo); //this is foo
运行结果是'this is foo' , call函数在这里修改了bar运行时this的指向,相当于执行了foo.value,所以输出的是foo对象下的value。那我们自己封装的思路就是这样了:
Function.prototype.call2 = function(obj){
var obj = obj || window; //如果obj为null,那么我们让它指向window
obj.fn = this;
obj.fn();
delete obj.fn; //执行完后接的删除这个临时方法
}
var value = 'this is window';
var foo = {
value : 'this is foo'
}
function bar(name,age){
console.log(this.value);
}
bar(); //this is window
bar.call1(foo); //this is foo
在Function的原型上新建方法call2,传递第一个参数,也就是需要指向的那个对象,然后this指向当前调用call2 方法的对象,然后执行。 call方法是可以接收参数的,所以我们继续来完善这个方法。
Function.prototype.call2 = function(obj){
var obj = obj || window;
var arr = []; //临时创建一个空的数组,来存放传进来的参数
obj.fn = this;
for(var i = 1; i < arguments.length; i++){ //参数第一个是要指向的对象,所以我们从第二个参数开始循环添加,i=1
arr.push('arguments['+i+']');
}
var result = eval('obj.fn('+arr.join()+')');
delete obj.fn;
return result;
}
arr 数组存后结果是 ["arguments[1]", "arguments[2]"...],arr.join()把它转成字符串,所以我们用到eval 方法,可以把string字符串里的内容运行,把结果赋值给result,返回出去。 完整的代码是这样的:
Function.prototype.call2 = function(obj){
var obj = obj || window;
var arr = [];
obj.fn = this;
for(var i = 1; i < arguments.length; i++){
arr.push('arguments['+i+']');
}
var result = eval('obj.fn('+arr.join()+')');
delete obj.fn;
return result;
}
var value = 'this is window';
var foo = {
value : 'this is foo'
}
function bar(name,age){
console.log(this.value);
console.log(name,age);
}
bar.call2(foo,'fly',30); //this is foo fly 30
手写bind方法
这个也和call,apply 方法类似,bind方法返回的是一个绑定this后的函数,并且该函数并没有执行,需要手动去调用,而call,apply是立即执行。有了上面的自定义方法call2 ,这里bing我就简短来说了,直接上代码。
Function.prototype.bind2 = function(obj){
if(typeof this!== 'function'){
return this;
}
var arg = Array.prototype.slice.call(arguments,1); //从第二个参数开始截取
var that = this; //这里指向当前调用者,函数
//创建返回的函数
function bindFn(){
var _this = this instanceof bindFn ? this : obj; // 返回的函数可能涉及到new 操作符调用,所以这里需要判断一下,当前的对象 是否是 bindFn对象的实例,否则返回传进来的对象obj
return that.apply(_this,arg.concat(Array.prototype.slice.call(arguments)));
}
var fn = function(){}; // 创建空的函数,让这个函数的原型接收当前函数的属性和方法,然后吧实例赋值给bindFn的原型
fn.prototype = this.prototype;
bindFn.prototype = new fn();
return bindFn; //最后返回bindFn 函数
}