apply和call都是为了改变某个函数运行时的上下文而存在的(就是为了改变函数内部this的指向),Function对象的方法,每一个函数都能调用;面试
使用apply或call方法,其运行的上下文指向第一个参数,apply的第二个参数是一个参数数组,call的第二个及其之后的参数都是数组里面的元素。数组
数组之间的追加;浏览器
例如:多维数字转一维app
let arr=[1,[7,8],[5,6]]; res=[].concat.apply([],arr)
扩充做用域拥有Math的min和max方法,获取数组中的最大值和最小值;函数
let numbers = [5, 458 , 120 , -215 ];
let maxInNumbers = Math.max.apply(Math, numbers), //458工具
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
验证是不是数组;测试
function isArray(obj){this
return Object.prototype.toString.call(obj) === '[object Array]' ;
}es5
好比: arguments对象,获取到的文档节点等,并无数组的那些方法:prototype
Array.prototype.slice.apply(argument);
//理论上来讲这个比较快,直接在原型上查找slice方法
//但实际上比较慢
或者
[].slice.apply(arguments);
//理论上来讲这个比较慢,由于要Array作一个实例化再查找slice方法
//实际上比较快,由于如今的各类自动化工具会把上一种方法转换为这种,而第二种代码比较简洁,因此会比较快;
也是改变函数体内this的指向,bind()是es5中的方法,bind会建立一个新函数,称为绑定函数,当调用这个函数的时候,绑定函数会以建立它时传入bind()方法的第一个参数做为this,传入bind()方法的第二个及之后的参数加上绑定函数运行时自己的参数按照顺序做为原函数的参数来调用原函数;
例如:(后面的代码皆取自张鑫旭大神的博客)
var button = document.getElementById("button"), text = document.getElementById("text"); button.onclick = function() { alert(this.id); // 弹出text }.bind(text);
但因为ie6~ie8不支持该方法,因此若想在这几个浏览器中使用,咱们就要模拟该方法,这也是面试常考的问题,模拟的代码以下:
if (!function() {}.bind) { Function.prototype.bind = function(context) { var self = this; var args = Array.prototype.slice.call(arguments); return function() { return self.apply(context, args.slice(1)); } }; }
上面的代码中this的指向是个容易理解错的地方。
首先,咱们判断是否存在bind方法,而后,若不存在,向Function对象的原型中添加自定义的bind方法。
这里面var self = this这段代码让我很困扰,按理说,prototype是一个对象,对象的this应该指向对象自己,也就是prototype,但真的是这样吗。看看下面的代码:
function a(){}; a.prototype.testThis = function(){console.log(a.prototype == this);}; var b = new a(); b.testThis();//false
显然,this不指向prototype,而通过测试,它也不指向a,而指向b。因此原型中的this值就明朗了。指向调用它的对象。
Array.prototype.slice.call(arguments);
上面这段代码,它的做用是将一个类数组转化为真正的数组,arguments是传给call的那个上下文(因为arguments本身没有slice方法,这里属于借用Array原型的slice方法)。并且通过测试,若果你不给slice传参数,那就等于传了个0给它,结果就是返回一个和原来数组如出一辙的副本。
这以后的代码就很好理解,返回一个函数,该函数把传给bind的第一个参数当作执行上下文,因为args已是一个数组,排除第一项,将以后的部分做为第二部分参数传给apply,前面讲过apply的用法。
如此,咱们本身的这个bind函数的行为就同es5中的bind同样了。
总之三个的使用区别: