js中this的作用和使用方法

this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。

例如:

function test(){
  this.x = 1;
}

随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。进一步说,this和它声明环境无关,而完全取决于他的执行环境。

以下有四种情况可以概括this的用法:

第一种:纯粹的函数调用

这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global。

下面来看代码理解:

function test(){
  this.x = 1;
  alert(this.x);
}
test();

执行函数test()之后会弹出什么呢?很明显,弹出1.

好,那么将代码改变一下,如下:

var x = 1;
function test(){
  alert(this.x);
}
test();

执行函数test()之后会弹出什么呢?还是一样的,依旧弹出1

好,继续改,如下:

var x = 1;
function test(){
   this.x = 0;
}
test();
alert(x);

执行函数test()之后接下来会弹出什么呢?这题很明显的是弹出0,因为函数test执行后里面的this.x=0这个值将全局变量x原先的值1给覆盖了(作用域链),所以最终输出的是0,但是this的指向一直没变,依旧指向window对象。

第二种:作为对象方法的调用

函数还可以作为某个对象的方法调用,这时this就指这个上级对象。下面来看代码进一步理解:

function test(){
  alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m();

执行完o.m()之后弹出什么呢?很明显弹出1,首先先声明了一个函数名为test的函数,然后声明了一个空的对象o,然后接下来给对象o添加一个属性x且值为1;随后又给对象o添加了一个方法o.m=test;现在test里面的this指向的就是o这个对象,因为test函数作为对象o的方法调用,然后调用对象o的方法m,于是弹出1.

第三种:作为构造函数调用

所谓构造函数(创建类[对象]的一种方式,若是不懂,可以查阅链接:js原型、原型链与继承的理解),就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。下面看代码理解:

function test(){
  this.x = 1;
}
var o = new test();
alert(o.x);

弹出1,但是得注意这里的this并不是全局对象!再看一段代码理解:

var x = 2;
function test(){
  this.x = 1;
}
var o = new test();
alert(x); 

输出为2,可证明this并不是全局对象,因为全局对象x的值没有发生变化。

第四种:apply调用

apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。下面看代码理解:

var x = 0;
function test(){
  alert(this.x);
}
var o={};
o.x = 1;
o.m = test;
o.m.apply(); 

apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。

如果把最后一句改为:o.m.apply(o),输出应该为多少?

输出应该为1,说明这时this代表的是对象o。

讲完了this的4个使用方法,下面就该用试题考考大家到底有没有完全理解:

var name = '罗恩';
var aaa = {
    name: '哈利',
    say: function () {
        console.log(this.name);
    }
}
var bbb = {
    name: '赫敏',
    say: aaa.say
}
var ccc = aaa.say;
aaa.say(); 
bbb.say();  
ccc();

后面三句代码得到什么?

第一个aaa.say();输出“哈利”,这个没啥好讲的;

第二个bbb.say(),bbb对象声明自身的say方法只是把aaa对象的say方法引用过来,但注意,这里引用的是一个方法而非一个对象,而aaa.say存储的是一个匿名函数。

如果这样不好理解,那么我们可以改写下bbb对象的写法,如下:

var bbb = {
    name: '赫敏',
    say: function () {
        console.log(this.name);
    }
}

这种写法与原来的代码没有什么区别,但是可以让人看的更明白些,输出“赫敏”;

第三个ccc()是在最外层执行,也就是在全局对象window下。所以ccc()执行的时候this指代的就是window对象。而在window对象下声明了name属性,就相当于window.name = '罗恩',输出的当然就是罗恩。

当然,也有特殊情况,那就是 setTimeout 和 setInterval 。

如果开头的aaa对象的声明改成:

var aaa = {
    name: '哈利',
    say: function () {
        setTimeout(function(){
            console.log(this.name);
        },100)
    }
}

再看原来的三行输出什么?即aaa.say();bbb.say(); ccc(); 输出什么?

会发现输出全是‘罗恩’,也就是说,三次this,指代的都是window对象。

但是为什么这样呢?查阅的链接里说,关于为什么会这样,因为涉及到JS异步回调(关于js异步回调的知识,如果只是仅仅想快速熟悉this的用法,那么只要记住这个特殊情况即可。这个知识点曾经是阿里还是小米的面试题。

关于js异步回调的知识,博主会下次更新。

下面再来看看几道笔试题,进一步加深印象:

var foo = {
    bar:function (){
        return this.baz;
    },
    baz:1
};
(function (){
    return typeof arguments[0]();
})(foo.bar);

这题应该输出什么呢?是object还是“object”,“number”?还是“function”呢?亦或是“undefined”?

下面我们就来好好看看,上面我阐述了this的4个使用方法:

1.纯粹的函数调用

2.作为对象方法调用

3.作为构造函数调用

4.apply()调用

首先我们来看,下面的匿名函数外部有个(),代表立即执行,而括号当中是foo.bar,大家要明白,匿名函数后面括号中的值或者对象是以参数的形式传给匿名函数的,而每个函数都有这么一个默认属性arguments,而刚刚好foo.bar对应的是arguments[0],所以这里相当于是吧foo.bar这个方法赋值给了arguments[0],而很明显的是在匿名函数中arguments[0]是在全局环境下的,也就是说foo.bar中的this指向的是arguments[0],所以在执行baz的时候自然this就是window了,而window上面没有baz属性,于是返回undefined,而typeof undefined输出的是“undefined”.(注意:这里typeof输出的字符串!)

下面再做一道笔试题:

var foo = {
    bar:function (){
        return this.baz;
    },
    baz:1
};
typeof (f = foo.bar)();

这题输出多少呢?我们知道foo.bar的确是函数方法的调用,但是在赋值给f的时候,this的指向就改变了

如果不好理解,那么把函数拆分一下:

var f = foo.bar;
typeof f();

这样在调用形式上又是普通函数调用,this指向的是window,所以答案是“undefined”。


好了,关于this的用法就到这里了,希望大家好好消化理解。

猜你喜欢

转载自blog.csdn.net/charles_tian/article/details/79778872