apply 调用函数的同时,改变函数的 this 指向。第一个参数为想要 this 指向的对象,第二个参数为数组,数组的 value 就是想传入函数的参数
call
call 调用函数的同时,改变函数的 this 指向。第一个参数为想要 this 指向的对象,后面的为一个或多个参数,是传入调用函数的参数
bind
返回一个新的函数,新函数的 this 为 bind 的第一个参数,其余参数为新函数的参数
call
1 2 3 4 5 6 7 8 9 10 11 12 13
//手写的call方法卸载Function的原型上,这样每个函数都能访问到 Function.prototype.myCall = function (obj) { obj = obj || window; //如果第一个参数为空,则让其指向window const newArg = []; for (let i = 1; i < arguments.length; i++) { //参数从1开始,因为第0个参数为this newArg.push(arguments[i]); } obj.p = this; //其中这里的this就是调用myCall的函数,给obj对象添加函数p,p = 调用myCall的函数 let result = obj.p(...newArg); //使用拓展运算符将1以后的参数传给函数p,并用result接收函数p的返回值 delete obj.p; //因为原本的obj中并没有p这个函数,所以运行完后要把它删除 return result; //返回函数执行的结果 };
apply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Function.prototype.myApply = function (obj, arg) { obj = obj || window; //如果obj为空就让它指向window const newArg = []; obj.p = this; //这里的this就是调用myApply的函数,给obj添加p这个方法,此时p = 调用myApply的函数 let result = null; //因为apply接收两个参数,第一个为this,第二个为数组参数,所以判断有无数组参数 if (!arg) { result = obj.p(); //没有直接调用,并用result接收p的返回值,也就是调用myApply的函数的返回值 } else { for (let i = 0; i < arg.length; i++) { newArg.push(arg[i]); } result = obj.p(...newArg); //有,则将参数传给p再调用,并用result接收p的返回值,也就是调用myApply的函数的返回值 } delete obj.p; //结束后删掉obj中的p return result; //返回返回值,如果没有则返回的是null };
bind
1 2 3 4 5 6 7 8
Function.prototype.myBind = function () { const fn = this || window; //这里的this就是指向调用myBind的函数 let arg = Array.prototype.slice.call(arguments); //使用Array.prototype.slice.call可以将arguments类数组转为数组。因为slice能返回一个新数组。这里也可以用Array.from将arguments转为数组。 let _this = arg.shift(); //使用数组的队列方法shift,它能取出数组的第一个值并且返回它。 returnfunction () { return fn.apply(_this, arg); //返回fn的返回值,如果没有则会返回undefined }; };
1 2 3 4 5 6 7 8 9 10 11
//Array.prototype.slice将类数组转为数组的关键 Array.prototype.slice = function (start, end) { var result = newArray(); start = start || 0; end = end || this.length; // this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键 for (var i = start; i < end; i++) { result.push(this[i]); }