谈谈js中的this

1,070 阅读4分钟

js中的this是面试官经常考的问题,相信每一个开发者都对this有或多或少的迷惑,那么什么是this?又如何找到this的指向?

this是什么?

我们先来看看js规范是如何解释它的

image.png

image.png

image.png
摘自《ECMAScrip (ECMA-262)

第一处 10.1.7 对this的解释(译):存在与每个活动执行上下文关联的this值。 this值取决于调用方和正在执行的代码类型,并由控件进入执行上下文时确定。与执行上下文关联的this值是不可变的。
第二处 11.1.1 译:this关键字计算为执行上下文的这个值。
第三处 12.2.1 描述this 也是正在运行时的上下文环境。

this的指向

ok,以上三处,都围绕一个词:**上下文环境。**也就是说要看this的指向就是通过上下文来决定的。
下面通过几个场景来观察this。

全局环境下的this

  • nodejs环境
console.log(this) // {}
  • 浏览器环境

image.png

全局环境下的this,在nodejs中是{},实际指向的是module.exports这个模块作用域,,浏览器指向的是Window。

函数内(function)中的this

  • nodejs 非严格模式下
function fn(){
  return this
}
const result = fn() 
console.log(result)  // Object [global] {...}
console.log(result === global) // true
  • 浏览器环境 非严格模式下

image.png

  • nodejs 严格模式下
function fn(){
  'use strict'
  return this
}
const result = fn() 
console.log(result) // undefined
console.log(result === undefined) // true
  • 浏览器环境下

image.png

非严格模式下,函数内this的指向
nodejs:global
浏览器:window
严格模式下,函数内this的指向
nodejs:undefined
浏览器:undefined

构造函数内的this

这个也属于函数内的this,因为内容较大,这里单独拿出来讲:

function Person(name){
	this.name = name
}
const p = new Person('zhangsan')
console.log(p.name) // zhangsan

例子可以看出,当经过new关键字来声明一个对象时,这里的this实际指向的是他的实例。

改变this的指向(call,apply,bind)


  • call

语法:function.call(thisArgs,arg1,arg2, ...)
第一个参数代表function函数内this值,可选。
arg1,arg2, ... 指定参数列表

function Person() {
  this.name = 'ipenman'
  this.sayHi = function() {
    console.log(`My name is ${this.name}`)
  }
}
function Man() {
  this.name = 'zhangsan'
}
const man = new Man()
const p = new Person()
p.sayHi.call(man) // zhangsan
  • apply

语法:function.apply(thisArgs,[arg1,arg2, ...])
第一个参数代表function函数内的this值,可选。
[arg1,arg2, ...] 指定参数列表

function Person() {
  this.name = 'ipenman'
  this.sayHi = function() {
    console.log(`My name is ${this.name}`)
  }
}
function Man() {
  this.name = 'zhangsan'
}
const man = new Man()
const p = new Person()
p.sayHi.apply(man) // zhangsan
  • bind

语法:与apply 语法相同
区别:bind函数会创建一个新的函数,不会立即执行

function Person() {
	this.name = 'ipenman'
  this.sayHi = function(){
  	console.log(`My name is ${this.name}`)
  }
}
function Man() {
  this.name = 'zhangsan'
}
const man = new Man()
const p = new Person()
p.sayHi.bind(man)() // zhangsan

call、apply、bind均以改变this的指向,p的this指向了man。

作为对象的方法

const Person = {
  name: 'ipenman',
  age: 24,
  show: function() {
    console.log(this)
  }
}
Person.show() // { name: 'ipenman', age: 24, sayHi: [Function: sayHi] }

当函数作为对象里的方法被调用时,它们的this指向的是调用该函数的对象。

箭头函数

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象 this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this --《es6标准入门 阮一峰》

function 函数:

const fn = function (){
  return this
}
console.log(fn()) // Object [global] {...}

箭头函数:

const fn = ()=>{
	return this
}
console.log(this) // {}

箭头函数没有自己的this,它的this就是上层代码块的this。

总结:

  1. 全局环境下的this,在nodejs中是{},实际指向的是module.exports这个模块作用域,,浏览器指向的是Window。

  2. 函数内的this

    非严格模式下,函数内this的指向
    nodejs:global
    浏览器:window
    严格模式下,函数内this的指向
    nodejs:undefined
    浏览器:undefined

  3. 构造函数的this,new关键字来声明的对象this指向的是它的实例。

  4. call,apply,bind 函数改变的this,指向的是其函数内的指定参数。

  5. 作为对象的方法,它们的this指向的是调用该函数的对象。

  6. 箭头函数的this,箭头函数的this指向的是外层代码块的this。

习题

通过几道题,看你是否真的掌握了this。

  1. 运行 test2.js,说出打印结果。

①创建test1.js

module.exports = this

②创建test2.js

const t1 = require('./test1')
console.log(this === t1)
  1. 运行test2.js,说出打印结果


①创建test1.js

module.exports = function(){
	return this
}

①创建test2.js

const t1 = require('./test1')
function fn(){
	return this
}
console.log(fn() === this)
  1. 运行以下代码,并说出打印结果
const Person = {
	name: 'ipenman',
  show: function(){
  	return this
  },
  show1:()=>{
  	return this
  }
}
console.log(Person.show())
console.log(Person.show1())
console.log(Person.show() === Person.show1())
console.log(Person.show1() === this)
console.log(Person.show() === this)

  1. 运行一下代码,说出打印结果
function Person() {
  this.name = 'ipenman'
  return () => {
    return () => {
      console.log(this)
      return () => {
        console.log(this)
        return () => {
          console.log(this)
        }
      }
    }
  }
}
const p = new Person()
p()()()()
  1. 说出打印结果。
function Person(){
	this.name = name
  this.age = 23
}
function Man(name){
	Person.call(this,name)
}
const man = new Man('ipenman')
console.log(man)

本文以6种上下文环境来描述this,如有补充,欢迎评论,也可以把答案回复到评论里哟!