this/call和apply this this指向一个动态的函数执行环境。
在一个普通的对象中,this的作用次域当然是对象本身
在前端最常用的浏览器中,alert(this)的结果是window。
1 2 3 4 5 6 7 8 9 10 11 12 var name='dangjingtao' var obj={ name:'djtao' , getName:function ( ) { return this .name } } console .log(obj.getName()) var a=obj.getName; console .log(a())
现在来研究一个业务中常见的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 <a id ="hit-me" href ="javascript:;" > hit me</a > <script > document .getElementById('hit-me' ).onclick=function ( ) { alert(this .id); var callback=function ( ) { alert(this .id) } callback(); } </script >
处理方法是用一个变量存下this
1 2 3 4 5 6 7 8 9 10 11 document .getElementById('hit-me' ).onclick=function ( ) { alert(this .id); var _this=this ; var callback=function (_ ) { alert(_this.id) } callback(); }
call用于绑定当前作用于
1 2 3 4 5 6 7 8 9 10 var name='dangjingtao' ; var obj={ name:'djtao' , getName:function ( ) { return this .name } } alert(obj.getName.call(this ));
这里call(this)显然是把当前的作用域(window)绑定给了getName方法。
遗失的this 写一个简单的js选择器吧,比如document.getElementById('div1')
,实在太长了。
1 2 var getId=document .getElementById console .log(getId('div1' ))
运行报错。因为用getId引用了之后,getElementById失去了this。
如何既能调用方法,又不丢失this?干货在于
1 2 3 4 5 6 7 8 document .getElementById=(function (func ) { return function ( ) { return func.apply(document ,arguments ) } })(document .getElementById) var getId=document .getElementById console .log(getId('div1' ))
call和apply 这两个方法非常重要。
调用apply方法的时候,第一个参数是this的指向 , 第二个参数是一个数组或类数组集合 。apply把这个集合作为参数传递给被apply的函数。
1 2 3 4 5 var func=function (a,b,c ) { alert([a,b,c]) } func.apply(null ,[1 ,2 ,3 ])
call是apply的高级实现。当你知道参数数量时,也可以用call来写。比如func.call(obj,'a','b','c')
。
非严格模式下,如果第一个参数为null,this还是指向默认的宿主。比如在浏览器中就是window。严格模式下则为null。
应用 如果认为,call和apply作用只局限于改变this的指向,那就错了。你还可以获取别的属性方法,绑定
来看看其它用法。
访问一类对象的内部数据 大部分高级浏览器都实现了Function.protoype.bind
方法,用以绑定this
。学习上述知识,能否手写模拟一个bind方法?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Function .prototype.bind=function (ctx ) { var _this=this ; return function ( ) { return _this.apply(ctx,arguments ) } } var name='dangjingtao' var obj={ name:'djtao' } var func=function ( ) { return this .name }.bind(obj) console .log(obj) func()
简单地说,我们在不为这个对象注册方法的前提下,为了一个对象实现了对内部私有变量的访问。
再丰富一点点 bind的简单实现并不足以满足功能。我想扩展bind方法并允许传入多个参数,并且还可以追加这个方法的参数。
可以这么做。
1 2 3 4 5 6 7 8 9 10 11 Function .prototype.bind=function ( ) { var _this=this ; var ctx=[].shift.call(arguments ) var args=[].slice.call(arguments ) return function ( ) { return _this.apply(ctx,[].concat.call(args,[].slice.call(arguments ))) } }
测试结果
1 2 3 4 5 6 7 8 9 10 11 var obj={ name:'djtao' } var func=function (a,b,c,d ) { alert(this .name) alert([a,b,c,d]) }.bind(obj,1 ,2 ) func(3 ,4 )
借用其他构造对象的方法 读书人的事,靠偷是不对的,应该是借。以下A是一个构造函数,拥有给某个人添加”是天才”后缀的处理过程。我要拿到A的属性。
1 2 3 var A=function (name ) { this .name=name+'是天才' }
可以构造一个B对象来借
1 2 3 4 5 6 7 8 9 10 11 var B=function ( ) { A.apply(this ,arguments ) } B.prototype.getName=function ( ) { return this .name } var b=new B('djtao' );console .log(b.getName)
再比如 arguments对象不是数组。但是我想把它们作为数组来处理,比如push。我想借用Array对象的push方法,怎么做?
1 2 3 4 5 (function ( ) { Array .prototype.push.call(arguments ,3 ); console .log(arguments ) })(0 ,1 ,2 )
更骚的操作是给对象也加个push方法
1 2 3 4 5 6 7 8 var a={}Array .prototype.push.call(a,'0' ) Array .prototype.push.call(a,'第一个元素' ); Array .prototype.push.call(a,'第二个元素' ) console .log(a)
只要对象的属性可读写,length属性可读写,那就可以把push方法用到上面去。