站长资讯网
最全最丰富的资讯网站

什么是this?深入解析JavaScript中的this

什么是this?下面本篇文章给大家介绍一下JavaScript中的this,并聊聊this在函数不同调用方式下的区别,希望对大家有所帮助!

什么是this?深入解析JavaScript中的this

JavaScript中的this格外的不一样,比如Java语言中的this是在代码的执行阶段是不可更改,而JavaScript的this是在调用阶段进行绑定。因为这一性质所以给了this很大的发挥空间。但其在严格模式和非严格模式下又有些不一样,在函数的不同调用方式也导致this有些区别。

什么是this?

首先对this的下个定义:this是在执行上下文创建时确定的一个在执行过程中不可更改的变量。

所谓执行上下文,就是JavaScript引擎在执行一段代码之前将代码内部会用到的一些变量函数this提前声明然后保存在变量对象中的过程。这个'代码片段'包括:全局代码(script标签内部的代码)、函数内部代码eval内部代码。而我们所熟知的作用域链也会在保存在这里,以一个类数组的形式存储在对应函数的[[Scopes]]属性中。

this只在函数调用阶段确定,也就是执行上下文创建的阶段进行赋值,保存在变量对象中。这个特性也导致了this的多变性:即当函数在不同的调用方式下都可能会导致this的值不同。

上面我们说过了在严格模式下和非严格模式下this表现不同:

var a = 1; function fun() {    'use strict';     var a = 2;       return this.a; } fun();//报错 Cannot read property 'a' of undefined
  • 严格模式下,this指向undefined;

var a = 1; function fun() {     var a = 2;       return this.a; } fun();//1
  • 非严格模式下this指向window;

上面同一段代码,在不同模式下之所以有不同表现,就是因为this在严格模式,非严格模式下的不同。

结论:当函数独立调用的时候,在严格模式下它的this指向undefined,在非严格模式下,当this指向undefined的时候,自动指向全局对象(浏览器中就是window)

多提一句,在全局环境下,this就是指向自己,再看一个例子:

this.a = 1; var b = 1; c = 1; console.log(this === window)//true //这三种都能得到想要的结果,全局上下文的变量对象中存在这三个变量

再多提一句,当this不在函数中用的时候会怎样?看一个例子:

var a = 1000; var obj = {     a: 1,       b: this.a + 1 } function fun() {     var obj = {           a: 1,         c: this.a + 2 //严格模式下这块报错 Cannot read property 'a' of undefined     }     return obj.c; } console.log(fun());//1002 console.log(obj.b);//1001

这种情况下this还是指向了window。那么我们可以单独下个结论:

当obj在全局声明的时候,obj内部属性中的this指向全局对象,当obj在一个函数中声明的时候,严格模式下this会指向undefined,非严格模式自动转为指向全局对象。

好了,刚刚小试牛刀下,知道了严格模式和非严格模式下this的区别,然而我们日常应用最多的还是在函数中用this,上面也说过了this在函数的不同调用方式还有区别,那么函数的调用方式都有哪些呢?四种:

  • 在全局环境或是普通函数中直接调用
  • 作为对象的方法
  • 使用apply和call
  • 作为构造函数

下面分别就四种情况展开:

直接调用

上面的例子,其实就是直接调用的,不过我决定再写一个例子:

var a = 1; var obj  =  {     a: 2,       b: function () {         function fun() {           return this.a         }        console.log(fun());     } }  obj.b();//1

fun函数虽然在obj.b方法中定义,但它还是一个普通函数,直接调用在非严格模式下指向undefined,又自动指向了全局对象,正如预料,严格模式会报错undefined.a不成立,a未定义。

重要的事情再说一遍:当函数独立调用的时候,在严格模式下它的this指向undefined,在非严格模式下,当this指向undefined的时候,自动指向全局对象(浏览器中就是window)。

作为对象的方法

var a = 1; var obj = {   a: 2,   b: function() {     return this.a;   } } console.log(obj.b())//2

b所引用的匿名函数作为obj的一个方法调用,这时候this指向调用它的对象。这里也就是obj。那么如果b方法不作为对象方法调用呢?啥意思呢,就是这样:

var a = 1; var obj = {   a: 2,   b: function() {     return this.a;   } } var t = obj.b; console.log(t());//1

如上,t函数执行结果竟然是全局变量1,为啥呢?这就涉及Javascript的内存空间了,就是说,obj对象的b属性存储的是对该匿名函数的一个引用,可以理解为一个指针。当赋值给t的时候,并没有单独开辟内存空间存储新的函数,而是让t存储了一个指针,该指针指向这个函数。相当于执行了这么一段伪代码:

var a = 1; function fun() {//此函数存储在堆中     return this.a; } var obj = {   a: 2,   b: fun //b指向fun函数 } var t = fun;//变量t指向fun函数 console.log(t());//1

此时的t就是一个指向fun函数的指针,调用t,相当于直接调用fun,套用以上规则,打印出来1自然很好理解了。

使用apply,call

关于apply和call是干什么的怎么用本文不涉及,请移驾:apply,call

这是个万能公式,实际上上面直接调用的代码,我们可以看成这样的:

function fun() {       return this.a; } fun();//1 //严格模式 fun.call(undefined) //非严格模式 fun.call(window)

这时候我们就可以解释下,为啥说在非严格模式下,当函数this指向undefined的时候,会自动指向全局对象,如上,在非严格模式下,当调用fun.call(undefined)的时候打印出来的依旧是1,就是最好的证据。

为啥说是万能公式呢?再看函数作为对象的方法调用:

var a = 1; var obj = {   a: 2,   b: function() {     return this.a;   } } obj.b() obj.b.call(obj)

如上,是不是很强大,可以理解为其它两种都是这个方法的语法糖罢了,那么apply和call是不是真的万能的呢?并不是,ES6的箭头函数就是特例,因为箭头函数的this不是在调用时候确定的,这也就是为啥说箭头函数好用的原因之一,因为它的this固定不会变来变去的了。关于箭头函数的this我们稍后再说。

作为构造函数

何为构造函数?所谓构造函数就是用来new对象的函数,像FunctionObjectArrayDate等都是全局定义的构造函数。其实每一个函数都可以new对象,那些批量生产我们需要的对象的函数就叫它构造函数罢了。注意,构造函数首字母记得大写。

function Fun() {   this.name = 'Damonre';   this.age = 21;   this.sex = 'man';   this.run = function () {     return this.name + '正在跑步';   } } Fun.prototype = {   contructor: Fun,   say: function () {     return this.name + '正在说话';   } } var f = new Fun(); f.run();//Damonare正在跑步 f.say();//Damonare正在说话

如上,如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。为啥呢?new做了啥呢?

伪代码如下:

function Fun() {   //new做的事情   var obj = {};   obj.__proto__ = Fun.prototype;//Base为构造函数   obj.name = 'Damonare';   ...//一系列赋值以及

赞(0)
分享到: 更多 (0)