apply 和 call 都是为了改变某个函数运行时的上下文而存在的(就是为了改变函数内部 this 的指向),Function 对象的方法,每个函数都能调用;
使用 apply 或 call 方法,其运行的上下文指向第一个参数,apply 的第二个参数是一个参数数组,call 的第二个及其以后的参数都是数组里面的元素。
apply 和 call 的常用用法:
数组之间的追加;
例如:多维数字转一维
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){
return Object.prototype.toString.call(obj) === ‘[object Array]’ ;
}
让类数组拥有方法;比如: arguments 对象,获取到的文档节点等,并没有数组的那些方法:
Array.prototype.slice.apply(argument); // 理论上来说这个比较快,直接在原型上查找 slice 方法 // 但实际上比较慢 或者 [].slice.apply(arguments); // 理论上来说这个比较慢,因为要 Array 做一个实例化再查找 slice 方法 // 实际上比较快,因为现在的各种自动化工具会把上一种方法转换为这种,而第二种代码比较简洁,所以会比较快;
binde 方法的使用
也是改变函数体内 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 一样了。
总之三个的使用区别:
都是用来改变函数的 this 对象的指向的;
第一个参数都是 this 要指向的对象;
都可以利用后续参数传参;
bind 是返回对应函数,便于稍后调用,apply、call 是立即调用;