为何须要这些?主要是由于this,来看看this干的好事。数组
box.onclick = function(){
function fn(){
alert(this);
}
fn();
};浏览器
咱们本来觉得这里面的this指向的是box,然而倒是Window。通常咱们这样解决:app
box.onclick = function(){
var _this = this;
function fn(){
alert(_this);
}
fn();
};函数
将this保存下来。this
还有一些状况,有时咱们想让伪数组也可以调用数组的一些方法,这时call、apply、bind就派上用场了。prototype
咱们先来解决第一个问题修复this指向。对象
box.onclick = function(){
function fn(){
alert(this);
}
fn();
};ip
改为以下:字符串
box.onclick = function(){
function fn(){
console.log(this);
}
fn.call(this);
};io
很神奇吧,call的做用就是改变this的指向的,第一个传的是一个对象,就是你要借用的那个对象。
fn.call(this);
这里的意思是让this去调用fn这个函数,这里的this是box,这个没有意见吧?若是这个你不清楚,极可能你是javscript的新朋友。box调用fn,这句话很是重要,咱们知道this它始终指向一个对象,恰好box就是一个对象。那么fn里面的this就是box。
box.onclick = function(){
function fn(){
console.log(this);
}
fn.call(this);
};
这句话在某些状况下是能够简写的,好比:
box.onclick = function(){
var fn = function(){
console.log(this); //box
}.call(this);
};
或者这样:
box.onclick = function(){
(function(){
console.log(this);
}.call(this)); //box
};
又或者这样:
var objName = {name:'JS2016'};
var obj = {
name:'0 _ 0',
sayHello:function(){
console.log(this.name);
}.bind(objName)
};
obj.sayHello();//JS2016
call和apply、bind可是用来改变this的指向的,但也有一些小小的差异。下面咱们来看看它们的差异在哪。
function fn(a,b,c,d){
console.log(a,b,c,d);
}//call
fn.call(null,1,2,3);//apply
fn.apply(null,[1,2,3]);//bind
var f = fn.bind(null,1,2,3);
f(4);
结果以下:
1 2 3 undefined
1 2 3 undefined
1 2 3 4
前面说过第一个参数传的是一个你要借用的对象,但这么咱们不须要,全部就传了一个null,固然你也能够传其余的,反正在这里没有用到,除了第一个参数后面的参数将做为实际参数传入到函数中。
call就是挨个传值,apply传一个数组,bind也是挨个传值,但和call和apply还有多少不一样,使用call和apply会直接执行这个函数,而bind并不会而是将绑定好的this从新返回一个新函数,何时调用由你本身决定。
var objName = {name:'JS2016'};
var obj = {
name:'0 _ 0',
sayHello:function(){
console.log(this.name);
}.bind(objName)
};
obj.sayHello();//JS2016
这里也就是为何我要用bind的缘由,若是用call的话就会报错了。本身想一想这个sayHello在obj都已经执行完了,就根本没有sayHello这个函数了。
这几个方法使用的好的话能够帮你解决很多问题好比:
正常状况下Math.max只能这样用
Math.max(10,6)
但若是你想传一个数组的话你能够用apply
var arr = [1,2,30,4,5];
console.log(Math.max.apply(null,arr));
又或者你想让伪数组调用数组的方法
function fn(){
[].push.call(arguments,3);
console.log(arguments); //[1, 2, 3]
}
fn(1,2);
再者:
var arr = ['aaabc'];
console.log(''.indexOf.call(arr,'b')); //3
牛逼不,简直偷梁换柱,固然还有不少能够利用的,本身尽情花辉去吧。
简单说一下这种偷梁换柱的原理吧,实际上浏览器内部根本就不在意你是谁,它只关心你传给个人是否是我可以运行的,以下:
正常状况
var str = 'aaabc';
console.log(str.indexOf('b'));
而这种状况其实作的事情和上面如出一辙,看我来拆解。
var arr = ['aaabc'];
''.indexOf.call(arr);
这句话就是说让arr调用字符串的indexOf方法,前面说过了浏览器内部不在意你是谁,因此谁均可以来调用,但不是100%成功,具体看以下。
''.indexOf.call(arr,'b')
这里的arr就是['aaabc'],内部极可能拆成了'aaabc',所以就成了下面的这段代码。
'aaabc'.indexOf('b');
这就是它们的秘密。
这里得说一下bind在某些浏览器下不兼容。咱们来模拟一个玩玩。
Function.prototype.$bind = function(obj){
//保存当前this
var _this = this;
//截取除了第一个之外的全部实际参数
var a = [].slice.call(arguments,1);
//返回一个新函数
return function(){
//让当前那个调用的函数的this指向obj,而且把实参传给它,这里用了concat是由于,咱们可能在绑定之后还传递参数,因此才把他们合并起来。如f(4)这个是在绑定之后传的参数,a这个argument是绑定时的。
_this.apply(obj,a.concat([].slice.call(arguments)));
};
};function fn(a,b,c,d){
console.log(a,b,c,d);
}var f = fn.$bind(null,1,2,3);
f(4);
这个方法和实际上的bind仍是差异很大的,如
var arr = ['JSS'];var index = ''.indexOf.$bind(arr,'S');
console.log(index())------------------
function fff(){
[].push.$bind(arguments,1);
console.log(arguments);
}fff();
这些都无法使用,由于技术有限就没办法带你们封装一个完美的bind了,若是有须要网上搜索一下吧。