this指向一个动态的函数执行环境。
在一个普通的对象中,this的作用次域当然是对象本身
在前端最常用的浏览器中,alert(this)的结果是window。
var name='dangjingtao'
var obj={
name:'djtao',
getName:function(){
return this.name
}
}
console.log(obj.getName()) //djtao
var a=obj.getName; //抽离了使用环境
console.log(a()) //dangjingtao
现在来研究一个业务中常见的问题。
<a id="hit-me" href="javascript:;">hit me</a>
<script>
document.getElementById('hit-me').onclick=function(){
alert(this.id);//hit-me
var callback=function(){
alert(this.id)//undefined
}
callback();
}
</script>
处理方法是用一个变量存下 this
document.getElementById('hit-me').onclick=function(){
alert(this.id);//hit-me
var _this=this;
var callback=function(_){
alert(_this.id)//undefined
}
callback();
}
call用于绑定当前作用于
var name='dangjingtao';
var obj={
name:'djtao',
getName:function(){
return this.name
}
}
alert(obj.getName.call(this)); //dangjingtao
这里call(this)显然是把当前的作用域(window)绑定给了getName方法。
写一个简单的js选择器吧,比如 document.getElementById('div1')
,实在太长了。
var getId=document.getElementById
console.log(getId('div1'))
运行报错。因为用getId引用了之后,getElementById失去了this。
如何既能调用方法,又不丢失this?干货在于
document.getElementById=(function(func){
return function(){
return func.apply(document,arguments)
}
})(document.getElementById)
var getId=document.getElementById
console.log(getId('div1'))
这两个方法非常重要。
调用apply方法的时候,第一个参数是this的指向 , 第二个参数是一个数组或类数组集合 。apply把这个集合作为参数传递给被apply的函数。
var func=function(a,b,c){
alert([a,b,c]) //输出[1,2,3]
}
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方法?
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) //控制台打印{name:'djtao'}
func() //弹出djtao
简单地说,我们在不为这个对象注册方法的前提下,为了一个对象实现了对内部私有变量的访问。
bind的简单实现并不足以满足功能。我想扩展bind方法并允许传入多个参数,并且还可以追加这个方法的参数。
可以这么做。
Function.prototype.bind=function(){
var _this=this;//保存原函数
var ctx=[].shift.call(arguments)//拿到第一个参数(也就要是绑定的this)
var args=[].slice.call(arguments)//其他参数转化为数组
//下面返回一个新函数
return function(){
//执行这个新函数的时候,会把ctx作为this
//并且组合两次分别传入的参数,作为该函数的参数
return _this.apply(ctx,[].concat.call(args,[].slice.call(arguments)))
}
}
测试结果
var obj={
name:'djtao'
}
var func=function(a,b,c,d){
alert(this.name) //djtao
alert([a,b,c,d]) //输出1,2,3,4
}.bind(obj,1,2)
// 执行
func(3,4)
读书人的事,靠偷是不对的,应该是借。以下A是一个构造函数,拥有给某个人添加"是天才"后缀的处理过程。我要拿到A的属性。
var A=function(name){
this.name=name+'是天才'
}
可以构造一个B对象来借
var B=function(){
A.apply(this,arguments)
}
//然后写一个方法。
B.prototype.getName=function(){
return this.name
}
var b=new B('djtao');
console.log(b.getName)//结果是 djtao是天才
arguments对象不是数组。但是我想把它们作为数组来处理,比如push。我想借用Array对象的push方法,怎么做?
(function(){
Array.prototype.push.call(arguments,3);
console.log(arguments)
})(0,1,2)
// 执行结果打印出1,2,3
更骚的操作是给对象也加个push方法
var a={}
Array.prototype.push.call(a,'0')
Array.prototype.push.call(a,'第一个元素');
Array.prototype.push.call(a,'第二个元素')
console.log(a)
只要对象的属性可读写,length属性可读写,那就可以把push方法用到上面去。