call、apply、bind 为什么会有call和apply? call和apply两个方法的作用基本相同,它们都是为了改变某个函数执行时的上下文(context)而建立的, 他的真正强大之处就是能够扩充函数赖以运行的作用域。通俗一点讲,就是改变函数体内部this 的指向。
1 2 3 4 5 6 7 8 9 window .color = "red" ;var o = {color : "blue" };function sayColor ( ){ alert (this .color ); }sayColor (); sayColor.call (this ); sayColor.call (window ); sayColor.call (o);
call( thisValue , arg1, arg2, … )
注意:如果call方法没有参数,或者参数为null或undefined,则等同于指向全局对象。
1 2 3 4 5 6 7 8 9 10 11 window .color = "red" ;var o = {color : "blue" };function sayColor ( ){ alert (this .color ); } sayColor.call (this ); sayColor.call (window ); sayColor.call (); sayColor.call (null ); sayColor.call (undefined ); sayColor.call (o);
应用场景
判断对象类型1 2 3 var arr = [];Object .prototype .toString .call (arr);
手撕call 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var foo = { count : 1 }; function bar ( ) { console .log (this .count ); } bar.myCall (foo); -------------------------------------------------------------------- Function .prototype .myCall = function (context ) { var context = context || window ; context.fn = this ; var args = [...arguments ].slice (1 ); var result = context.fn (...args); delete context.fn ; return result; };
apply( thisValue , [arg1, arg2, …] ) 很明显,我们看标题的可以知道call和apply的一个区别了,它们两个唯一的区别就是传参列表的不同,apply是接收的参数是一个数组。
手撕apply 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var foo = { count : 1 }; function bar ( ) { console .log (this .count ); } bar.myApply (foo); -------------------------------------------------------------------- Function .prototype .myApply = function (context ) { var context = context || window ; context.fn = this ; var result; if (arguments [1 ]) { result = context.fn (...arguments [1 ]); } else { result = context.fn (); } delete context.fn ; return result; }
应用场景
1 2 3 4 5 var a = [10 , 2 , 4 , 15 , 9 ];Math .max .apply (Math , a); Math .min .apply (null , a); Math .max (...[10 , 2 , 4 , 15 , 9 ]);
可以将一个类似(伪)数组的对象(比如arguments对象)转为真正的数组。 前提: 被处理的对象必须有length属性,以及相对应的数字键。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var ArrayLike = { 0 : 'a' , 1 : 'b' , 2 : 'c' , length : 3 }Array .prototype .slice .apply ({0 : 1 , length : 1 }) Array .prototype .slice .apply ({0 : 1 }) Array .prototype .slice .apply ({0 : 1 , length : 2 }) Array .prototype .slice .apply ({length : 1 }) Array .from (ArrayLike ); ...ArrayLike ;
1 2 3 4 5 6 7 var arr1 = [1 ,2 ,3 ];var arr2 = [4 ,5 ,6 ]; [].push .apply (arr1, arr2);console .log (arr1); console .log (arr2); arr1.push (...arr2);
1 2 3 4 5 6 7 var arr1 = [1 , 2 , { id : 1 , id : 2 }, [1 , 2 ]];var arr2 = ['ds' , 1 , 9 , { name : 'jack' }];Array .prototype .push .apply (arr1,arr2)console .log (arr1); [...arr1,...arr2]
bind( thisArg[, arg1[, arg2[, …]]])
call和apply它们两个是改变this的指向之后立即调用该函数,而bind则不同,它是创建一个新函数,我们必须手动去调用它。
bind()是ES5新增的一个方法
传参和call或apply类似
不会执行对应的函数,call或apply会自动执行对应的函数
bind会返回对函数的引用
举个栗子 1 2 3 4 5 6 7 8 9 var a ={ name : "Cherry" , fn : function (a,b ) { console .log ( a + b) } }var b = a.fn ; b.call (a,1 ,2 ); b.bind (a,1 ,2 )();
应用场景 1 2 3 4 5 6 7 8 9 10 11 12 13 var obj = { name :'JuctTr' , age : 18 };document .addEventListener ('click' ,ExecuteFun .bind (obj),false ); function ExecuteFun (a,b ){ console .log (this .name , this .age ); }
手撕bind 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Function .prototype .myBind = function (context ) { if (typeof this !== 'function' ) { throw new TypeError ('Error' ) } var _this = this var args = [...arguments ].slice (1 ) return function F ( ) { if (this instanceof F) { return new _this (...args, ...arguments ) } return _this.apply (context, args.concat (...arguments )) } }