JavaScript基础系列(6):`this`这六种使用方式,你都理解了吗?

简介: 执行发现两个this都指向了全局的window.也就是全局执行上下文中的this和函数执行上下文中的this都指向了全局window.所以默认的情况下,在全局中调用一个函数,函数中执行上下文中的this都是指向window对象的。

image.png


上个月写过一篇V8是如何运行JavaScript(let a = 1)代码的?,写完之后我就发现,我对平常使用的工具V8引擎,偏底层的知识了解的竟然是如此甚少。同时我真正从事前端的时间还算是比较短的,那么基础也算是非常的薄弱。结合以上,我打算有时间就去从底层的角度去学习了解,便于在使用过程中的理解和解决遇到的问题,理解JavaScript的本质,能够更好的学习JavaScript。如果你跟我有同样的困惑,那我们可以结伴同行,共同学习。


本系列我会从我的视角不断的去总结:








1、前言


通过本文可以学习到以下三点:


  • 了解严格模式和非严格模式的区别


  • 了解this在不同场景下的区别


  • 了解如何改变this的指向问题


2、普通函数


<script>
console.log(this, '全局')
function foo() {
  console.log(this, '函数')
}
foo()
</script>

image.png


执行发现两个this都指向了全局的window.也就是全局执行上下文中的this和函数执行上下文中的this都指向了全局window.所以默认的情况下,在全局中调用一个函数,函数中执行上下文中的this都是指向window对象的。


而我们平常工作中的项目,你可以去试试通常都是严格模式下的。


在ECMAscript 5的时候,JavaScript引擎添加了另外一种运行模式:严格模式,这种情况下代码会在更严格的条件下运行。而我们上面的代码其实就是运行在非严格模式下,我只是在一个html文件中添加如上代码,没有其他任何的设置配置。而我们平常工作中的项目,你可以去试试通常都是严格模式下的。


我们来看上面的代码如何在严格模式下运行。


<script>
"use strict"
console.log(this, '全局')
function foo() {
  console.log(this, '函数')
}
foo()
</script>


image.png


可以发现,函数中的this打印,变成了undefined。在全局调用一个函数执行,其函数执行上下文中的this值是undefined。


总结:全局上下文中的this,无论在严格模式还是非严格模式下,它的指向都是全局变量window。 普通函数中的this,在严格模式下的指向是undefined,在非严格模式下的指向为全局变量的window。


3、var和let声明的变量


<script>
console.log(this, '全局')
var name1 = 'name1'
let name2 = 'name2'
console.log(this.name1, '全局this.name1')
console.log(this.name2, '全局this.name2');
function foo() {
  console.log(this, '函数')
  console.log(this.name1, '函数this.name1')
  console.log(this.name2, '函数this.name2');
}
foo()
</script>

image.png


通过运行可以发现通过let声明的变量,无论是在全局执行上下文,还是函数执行上下文中打印的都是undefined。


总结:通过执行上下文的角度,我们去查看过var和let声明变量的区别,有兴趣的可以去查看一下( www.baidu.com )。var声明的变量被存放到执行上下文的变量环境,let声明的变量都被存放到执行上下的词法环境中,但是var声明的变量同时被存放到全局变量window下,而全局执行上下文中的this指向是window。而let不在其中,所以无论是全局执行上下文还是函数执行上下,this中是访问不到let声明的变量的。


4、对象中的函数


<script>
  'use strict';
let bar = {
  name: 'aehyok',
  fun: function () {
    console.log(this.name,'bar对象')
  }
}
bar.fun()
const bb = bar.fun
bb()
</script>


image.png


通过截图执行情况可以发现,在严格模式下,直接调用bar.fun()打印出来的this指向的是bar对象。如果将bar.fun赋值给一个变量,这个变量的引用其实存储的就是bar对象中的fun函数,这样相当于变成了一个普通的函数,在严格模式下打印出来就为undefined。


总结:使用对象来调用其内部的一个函数,该函数的this指向为对象本身。在全局上下文中调用一个函数,函数内部的this指向为(严格模式下指向undefined,非严格模式下指向为window)


那调用bb()的时候有没有办法将this指向原来的bar对象呢?


5、call、apply、bind


<script>
  'use strict';
let bar = {
  name: 'aehyok',
  fun: function () {
    console.log(this,'bar对象')
  }
}
bar.fun()
const bb = bar.fun
bb()
bar.fun.call(bar)
bar.fun.apply(bar)
bar.fun.bind(bar)()
</script>


image.png


通过截图的执行结果可以发现,最后三个的打印this指向全部是bar对象本身。再看最后三行代码,call、apply、bind,都可以改变函数的this指向。


6、箭头函数


<script>
  'use strict';
let bar = {
  name: 'aehyok',
  fun:function() {
    console.log(this,'bar函数')
    function inner () {
      console.log(this, 'inner函数')
    }
    inner()
  }
}
bar.fun()
</script>


image.png


通过之前的解析我们可以非常的清楚,如截图所示的执行结果。在fun函数中的this指向为bar对象,而在inner函数中调用的this指向为undefined。这里也就说明了嵌套函数里的this并不会传递,并不会将我们能否将this指向改为bar对象呢?当然上一小节,我们已经可以通过call、apply、bind改变this的指向。


第一种方式:通过定义变量进行传递this


<script>
  'use strict';
let bar = {
  name: 'aehyok',
  fun:function() {
    console.log(this,'bar函数')
    let self = this
    function inner () {
      console.log(self, 'inner函数')
    }
    inner()
  }
}
bar.fun()
</script>


image.png


定义一个self变量,如果嵌套函数inner中需要,在inner函数中直接使用self,而不是this了。


这里我们来看一下另外一种方式:箭头函数的妙用


<script>
  'use strict';
let bar = {
  name: 'aehyok',
  fun:function() {
    console.log(this,'bar函数')
    let inner = () => {
      console.log(this, 'inner函数')
    }
    inner()
  }
}
bar.fun()
</script>


image.png


通过运行的截图可以发现,inner箭头函数中this的指向是bar对象。这是因为ES6中的箭头函数并不会创建自身的执行上下文,所以箭头函数中的this取决于它的外部函数。那么很自然的就指向了bar对象了。


7、构造函数


还是通过vue创建实例来看结果


<script>
  // "use strict"
function Vue (name) {
  let vm = this
  vm.name = name
  console.log(vm)
}
const _vue = Vue('普通函数')
const vue = new Vue('构造函数')
</script>


执行结果如下


image.png


总结:

_vue通过普通函数赋值,其中的this指向指向了window(这里是非严格模式)。

如果是严格模式this指向会是undefined。

而通过构造函数new出来,其中的this指向了构造函数创建出来的实例对象。这也是构造函数比较特殊的地方。


8、总结


  • 1、全局执行上下文中的this


  • 在严格模式下和非严格模式下,this的指向都为全局变量window。


  • 2、函数执行上下文中的this(在全局执行上下文中调用函数)


  • 在非严格模式下,this的指向为全局变量window。


  • 在严格模式下,this的指向为undefined。


  • 3、通常我们在项目中都是在严格模式下调用的,当然也有配置可配。(你可以在公司的vue项目中进行测试)


  • 4、对象中的函数


  • 通过对象.函数的方式,this的指向便是对象本身


  • 但是要注意不能将对象.函数赋值给别的变量,然后再调用额


  • 5、构造函数


  • 通过构造函数 new 出来的,其中的this指向了new出来的实例对象


  • 6、改变 this 指向的方式


  • 通过箭头函数


  • 通过声明 let self = this,然后在需要的嵌套函数中使用self 即可


  • 就是使用 call apply bind


  • fun.apply(thisArgs, [arg1, arg2]) 参数通过数组的方式传递
  • fun.call(thisArgs, arg1, arg2) 参数通过多个参数传递
  • fun.bind(thisArgs, arg1, arg2)() bind 相当于创建一个新的函数,我们还需要手动调用
目录
相关文章
|
1月前
|
JavaScript 前端开发
javascript中的this
javascript中的this
|
3月前
|
JavaScript 前端开发
错综复杂的this:理清你的JavaScript代码中的指向问题
错综复杂的this:理清你的JavaScript代码中的指向问题
|
1月前
|
JavaScript
JS中改变this指向的六种方法
JS中改变this指向的六种方法
|
6月前
|
JavaScript 前端开发
详解js中的this指向
详解js中的this指向
44 0
|
6月前
|
前端开发 JavaScript
前端基础 - JavaScript之this关键字
前端基础 - JavaScript之this关键字
26 0
|
4天前
|
JavaScript 前端开发
js开发:请解释this关键字在JavaScript中的用法。
【4月更文挑战第23天】JavaScript的this关键字根据执行环境指向不同对象:全局中指向全局对象(如window),普通函数中默认指向全局对象,作为方法调用时指向调用对象;构造函数中指向新实例,箭头函数继承所在上下文的this。可通过call、apply、bind方法显式改变this指向。
7 1
|
1月前
|
JavaScript
JS中call()、apply()、bind()改变this指向的原理
JS中call()、apply()、bind()改变this指向的原理
|
2月前
|
JavaScript 前端开发
JavaScript中this的指向问题
JavaScript中this的指向问题
|
3月前
|
前端开发 JavaScript
揭开`this`的神秘面纱:探索 JavaScript 中的上下文密钥(下)
揭开`this`的神秘面纱:探索 JavaScript 中的上下文密钥(下)
|
3月前
|
前端开发 JavaScript
揭开`this`的神秘面纱:探索 JavaScript 中的上下文密钥(上)
揭开`this`的神秘面纱:探索 JavaScript 中的上下文密钥(上)