关于 js 中 this 的理解

今天在写一个tab选项卡切换时遇到一个 this 的引用问题,做个记录加深一下理解。

//定时器
var timer = null;   
var lis = document.getElementsByTagName('li'); 
for (var i=0; i<lis.length; i++) {
    //给每个li元素添加自定义属性id,值为当前li元素在lis中的索引
    lis[i].id = i; 
    lis[i].onmouseover = function() {
        // 用that引用当前li元素
        var that = this;  
        if(timer){
            clearTimeout(timer);
        }
        timer = setTimeout(function() {
            for(var j=0; j<lis.length; j++){
                lis[j].className = '';
            }
            lis[that.id].className = 'select';  
        },500)
    }
}

当然,只为了说明 this 的引用,这只是其中的一部分代码。

对于不是很理解 this 的朋友来说,难道不是应该在定时器里直接用 this 表示当前触发的元素,用 this.id 获取开始自定义的属性值,然后给当前触发事件的元素添加 class 吗?为什么要在 setTimeout() 方法外定义一个 var that = this, 然后用 that 代替 this?

对,我当时就是想当然的这样做的,直接在 setTimeout() 方法里用 this.id 来作为 lis 的索引。但是结果却是浏览器报错,报错信息为‘className 为 undefined’。这是个什么情况,难道是没有给每个 li 元素添加上自定义的 id 属性?经过在 Elements 里查看,事实是确实每个 li 元素都添加上了 id 属性 并且属性值也都是正确的。那么就可以判定是 this 的问题,然后经过打断点查看,发现 this 的值为 window

为什么 this 的 值会是 window呢。经过一番搜素得知 setTimeout() 是 window 的一个方法,由于一般的书写习惯会将前面 window 省略,其实 setTimeout() 也可以写成 window.setTimeout(),类似的还有setInterval() 也是属于 window 下的方法。

那么问题就显而易见,this 的 值是 window,肯定引用不了当前触发事件的 li 元素。那么应该怎样在 setTimeout() 方法里引用当前 li 元素呢?
答案就是,在 setTimeout() 方法外部、lis[i].onmouseover = function(){} 函数内部申明个变量存贮触发事件时当前 li 元素,var that = this。因为在 lis[i].onmouseover = function(){} 函数内部 this 肯定指向触发事件的当前 li 元素。再在 setTimeout() 方法内部 用 that 代替 this,问题就可以解决了。

通过这个问题过后,我查了相关资料了解了一下 this 在各种情况下的引用问题,也在这儿做个总结:

  • call() 或者 apply()中 改变函数执行环境的情况下,this 的指向。
func.call(context, arg1, arg2)

它会立即执行函数,第一个参数是指定执行函数中 this 的上下文,后面是执行函数时需要传入的参数。
this 就是上面代码中的 context,this 就是 call 一个函数时传的 context。

func.apply(context, arguments)

它会立即执行函数,第一个参数是指定函数中 this 的上下文,第二个参数是一个数组,是传给执行函数的参数。(与 call() 的区别)
this 就是上面代码中的 context。

其实也可以这样理解:

func.call(this, arg1, arg2) == func.apply(this, arguments) == this.func(arg1, arg2)
  • this 总是指向函数的直接调用者:

①全局作用域中调用:

function func(){
    console.log(this);
}

func();  // window

因为 func() 函数是在全局作用域中调用的,所以 this 指向 window。

上面的代码也可以等价的变为 call() 形式,如下:

function func() {
    console.log(this);
}

func.call(undefined);   // 可以简写为
func();  // window

结合第一种 call() 情况下来说,func() 打印值应该为 undefined,但是事实却是 window。是因为浏览器里有一条规则:如果 call() 里传入的不是一个对象,那么 window 对象就是默认的 context,所以打印值为 window。

② 作为对象的方法调用:

var obj = {
    name: 'Bob',
    foo: function() {
        console.log(this.name);
    }
}
obj.foo();  // 'Bob'

这时候 this 指向当前的这个对象 (obj)。
如果把对象的方法赋给一个变量呢:

var obj = {
    name = 'Bob',
    foo: function() {
        console.log(this);
    }
}
var test = obj.foo;
test();   // window

可以看到,这时候 this 指行了全局,当我们把 var test = obj.foo,test 直接指向了一个函数的引用,这时候就和 obj 这个对象没有关系了,所以它被当做普通函数来调用。因为 test 是一个全局变量,所以 this就指向 window。

  • 在事件中,this指向触发这个事件的对象

本文就是其中的一个例子。

其他的待更。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值