js中的this是面试官经常考的问题,相信每一个开发者都对this有或多或少的迷惑,那么什么是this?又如何找到this的指向?
this是什么?
我们先来看看js规范是如何解释它的
摘自《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) // {}
- 浏览器环境
全局环境下的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
- 浏览器环境 非严格模式下
- nodejs 严格模式下
function fn(){
'use strict'
return this
}
const result = fn()
console.log(result) // undefined
console.log(result === undefined) // true
- 浏览器环境下
非严格模式下,函数内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。
总结:
-
全局环境下的this,在nodejs中是{},实际指向的是module.exports这个模块作用域,,浏览器指向的是Window。
-
函数内的this
非严格模式下,函数内this的指向
nodejs:global
浏览器:window
严格模式下,函数内this的指向
nodejs:undefined
浏览器:undefined -
构造函数的this,new关键字来声明的对象this指向的是它的实例。
-
call,apply,bind 函数改变的this,指向的是其函数内的指定参数。
-
作为对象的方法,它们的this指向的是调用该函数的对象。
-
箭头函数的this,箭头函数的this指向的是外层代码块的this。
习题
通过几道题,看你是否真的掌握了this。
- 运行 test2.js,说出打印结果。
①创建test1.js
module.exports = this
②创建test2.js
const t1 = require('./test1')
console.log(this === t1)
- 运行test2.js,说出打印结果
①创建test1.js
module.exports = function(){
return this
}
①创建test2.js
const t1 = require('./test1')
function fn(){
return this
}
console.log(fn() === this)
- 运行以下代码,并说出打印结果
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)
- 运行一下代码,说出打印结果
function Person() {
this.name = 'ipenman'
return () => {
return () => {
console.log(this)
return () => {
console.log(this)
return () => {
console.log(this)
}
}
}
}
}
const p = new Person()
p()()()()
- 说出打印结果。
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,如有补充,欢迎评论,也可以把答案回复到评论里哟!