1、call&apply |
call, apply都属于Function.prototype的方法,由于属于Function.prototype,因此每一个Function对象实例,也就是每一个方法都有call, apply属性啦。html
若是不明白,请见“Javascript之一切皆为对象3”。chrome
并且它们的做用都是同样的,只是使用方式不一样而已。数组
做用:借用别人的方法来调用,就像本身有这个方法同样。浏览器
咦,那它们怎样才能达到这目的呢?app
对象。函数
对象?ui
是的,其实就是改变执行上下文对象的内部指针,由于在Javascript中,代码总有一个执行上下文对象,那么当我手动改变它时,就能够改变这个执行上下文啦,也就能够利用非本身的方法成为本身的方法哦。this
咱们一块儿来写个Demo。spa
假如,我有一个方法a,它的做用是输出对象的名字this.name;那么当我使用call或者apply改变它的执行上下文对象时,它的输出结果是不同的。prototype
什么意思?
详情请见下代码:
<!DOCTYPE html> <head> <title>call&apply</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> var name = 'windowName'; //方法a的做用,是输出对象的名字 function a(){ console.log(this.name); } function b(){ this.name = 'bName'; } //将a方法的执行上下文对象指向window a.call(window); //将a方法的执行上下文对象指向new b() a.call(new b()); </script> </body> </html>
执行上述代码,结果以下:
看见了么?因此说call,apply的做用就是借用别人的方法,改变别人方法的执行上下文对象为本身,成为本身的方法,为己所用。
注意: call或apply的第一个参数传的是什么,它们就会将其默认为执行上下文对象。假若咱们没有指明call或apply的执行上下文对象,即,call和apply的第一个参数是null、undefined或为空时,在非严格模式下,函数内的this指向window或global,浏览器就是window。严格模式下,null为null,undefined或空为undefined。
什么意思,请见下面的demo(仅以call举例且为非严格模式):
<!DOCTYPE html> <head> <title>call&apply</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> function print(){ console.log(this); }; //将第一个参数数字1,做为执行上下文对象 print.call(0,1,2); //将第一个参数字符串'123',做为执行上下文对象 print.call('123'); //将第一个参数true,做为第执行上下文对象 print.call(true); //将第一个参数对象,做为执行上下文对象 print.call(new Object()); //将null传入 print.call(null); //将undefined传入 print.call(undefined); //不传任何参数 print.call(); </script> </body> </html>
看见了么,我上面传入的依次是数字,字符串,true,对象,null,undefined和空,获得下面的结果:
那么,call与apply既然做用同样,那它们有什么区别呢?
它们的第一个参数,毋庸置疑,都是传入的执行上下文对象,区别是从第二个参数开始的。call方法的其它参数依次传递给借用的方法做参数,而apply就两个参数,第二个参数为一个数组传递。
简单点,就是:
fun.call(obj, arg1, arg2…) === fun.apply(obj, [arg1, arg2…]) === obj.fun(arg1, arg2…);
咦,call和apply的区别是,参数的传递不一样,有什么用呢?
根据它们传递参数的区别,当参数明确的时候,使用call;当传递的参数不明确时,用 apply咯,即传递arguments给apply做为第二个参数。
好了,光说不作没用,咱们写个demo看看。
<!DOCTYPE html> <head> <title>call&apply</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> function print(name, age, time){ console.log("name: "+ name +" age: "+ age +" time: "+ time ); }; function fn(a, b, c){ //使用call,参数明确 print.call(this,a); //使用apply,参数明确 print.apply(this,[a, b]); //使用apply,参数不明确 print.apply(this,arguments); } fn('monkey',24,'1992'); </script> </body> </html>
执行上述代码,结果以下:
call与apply,这下明白了么?
2、bind |
bind,最开始认识它的时候,理解就是改变执行上下文的对象。
好比,当咱们使用setTimeout时,默认匿名函数里的this指向的是window,但使用对象的方法时,我想将this指向对象呢,怎么办呢?其中的一个方法就是使用bind。
(关于setTimeout的理解,见“setTimeout那些事儿”)。
如:
<!DOCTYPE html> <head> <title>bind</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> var name = 'window'; var obj = { name:'monkey', print: function(){ //在这里使用bind,显示地将this指向obj,因此console.log会输出'monkey' setTimeout(function(){ console.log(this.name); }.bind(this),100); } }; obj.print(); </script> </body> </html>
执行上述代码结果为:
好了,既然谈到bind是改变执行上下文中的对象,我靠,那咱们怎么不使用call或apply呢?
call或apply不也是改变执行上下文的对象么?
是的,咱们将上面的demo修改下,将bind换成call,代码以下:
<!DOCTYPE html> <head> <title>bind</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> var name = 'window'; var obj = { name:'monkey', print: function(){ setTimeout(function(){ console.log(this.name); }.call(this)/*在这里将bind换成call*/,100); } }; obj.print(); </script> </body> </html>
打开chrome调试器,得下结果:
咦,我靠,这不是和bind同样么?
是的,但若是咱们将setTimeout的延迟时间,换成2秒,或者更长呢?打开chrome调试器,运行修改后的代码,你就会发现区别,call或apply是立马呈现’monkey’,而bind是在延迟相应时间后,呈现’monkey’。
Why?
由于call或apply是将执行上下文对象换了后,当即执行;而bind是将执行上下文对象换了后,建立一个新函数。
咱们再一块儿写个demo看看。
<!DOCTYPE html> <head> <title>bind</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> function fun(){ console.log(this.name); } function obj1(){ this.name = 'call||apply'; } function obj2(){ this.name = 'bind'; } var o1 = new obj1(); var o2 = new obj2(); fun.call(o1); fun.bind(o2); </script> </body> </html>
执行上述代码,结果为:
咦,怎么只打印了一个’call||apply’呢?
由于咱们在上面的代码中,bind我只是绑定了对象o2,可是它又不当即执行,而是返回一个新函数哦。
咱们修改以上代码,手动执行bind返回后的新函数看看。
<!DOCTYPE html> <head> <title>bind</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> function fun(){ console.log(this.name); } function obj1(){ this.name = 'call||apply'; } function obj2(){ this.name = 'bind'; } var o1 = new obj1(); var o2 = new obj2(); fun.call(o1); //手动调用bind建立的新函数 fun.bind(o2)(); </script> </body> </html>
运行代码:
嘿嘿,这下对了吧。
因此,必定要记住bind方法会建立一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以建立它时传入的第一个参数做为this,即执行上下文对象。
好了,晚安everyone~