this
是在函数运行时,在函数体内部自动生成的一个对象,并只能在函数体内部使用。
总的来说,this
就是函数运行时所在的环境对象。
函数在浏览器全局环境中被直接调用
this
指向 window
this
指向 undefined
function f1() {
console.log(this) // window
}
function f2() {
'use strict'
console.log(this) // undefined
}
setTimeout(function () {
console.log(this) // window
})
setTimeout(function () {
'use strict'
console.log(this) // 注意是 window
})
作为对象的方法调用
在执行函数时,如果函数的 this
是被上一级的对象调用,那么this
指向上一级对象。
举例 1:
const person = {
name: '下雪天的夏风',
brother: {
name: '路飞',
fn: function () {
return this.name
}
}
}
console.log(person.brother.fn()) // 路飞
举例 2:
const obj1 = {
name: '1',
fn: function () {
return this.name
}
}
const obj2 = {
name: '2',
fn: function () {
return obj1.fn()
}
}
const obj3 = {
name: '3',
fn: obj1.fn
}
const obj4 = {
name: '4',
fn: function () {
const fn = f1.fn
return fn()
}
}
var name = 'global'
console.log(obj1.fn()) // 1
console.log(obj2.fn()) // 1
console.log(obj3.fn()) // 3
console.log(obj4.fn()) // global
this
指向上一级对象obj1
this
指向上一级对象obj1
obj1.fn
是函数的引用,obj3.fn()
中this
指向上一级对象obj3
const fn = f1.fn
赋值后,fn()
变成独立调用,this
指向window
隐式绑定的函数丢失了绑定的对象,实质上都是变成了独立调用,this
指向了window
。
const obj = {
name: '下雪天的夏风',
foo: function () {
console.log(this.name)
}
}
下面这4种,都可以理解为将 obj.foo
赋值给一个新的变量来调用。
const bar = obj.foo
bar()
function doFoo(foo) {
foo()
}
doFoo(obj.foo)
setTimeout(obj.foo)
(false || obj.foo)()
call,apply,bind 的调用
三者异同点:
const targetObj = {}
function fn() {}
fn.call(targetObj, 'arg1', 'arg2')
fn.apply(targetObj, ['arg1', 'arg2'])
fn.bind(targetObj, 'arg1', 'arg2')() // 返回一个新函数来调用。
如果第 1 个参数是
null
|undefined
,此时this
绑定给window
作为构造函数调用
构造函数:可以通过这个函数生成有一个新的对象,此时 this
指向这个新对象。
function test() {
this.x = 1
}
const obj = new test()
obj.x // 1
对于构造函数Foo
来说,执行 new Foo()
会进行如下操作
const context = {}
context.__proto__ = Foo.prototype
前 2 步可以合并为
const context = Object.create(Foo.prototype)
Foo
的 this
指向新对象context
,并执行Foo
const result = Foo.apply(context, arguments)
context
return typeof result === 'object' && result !== null ? result : context
new 命令始终返回一个对象。
return
语句,并且后面跟着对象,则会返回这个对象。否则不管这个return
语句,返回this
对象。return
语句,则返回this
对象。箭头函数没有自己的this
对象,内部的this
就是定义时上层作用域中的this
。
也就是说,箭头函数内部的this
指向是固定的。
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 }); // id: 42
new > 显示绑定 > 隐式
显示绑定 > 隐示,比较容易理解。
我们来举个 new > 显示绑定 的例子:
function foo (a) {
this.a = a
}
const obj1 = {}
var bar = foo.bind(obj1)
bar(2)
console.log(obj1.a) // 2
相比于bar()
直接调用,通过 new 调用时,返回的实例已经与 obj1
解绑。
var baz = new bar(3)
console.log(baz.a) // 3
我们在一开始说:this
就是函数运行时所在的环境对象。
以下面的代码为例(隐示丢失):
const obj = {
name: '下雪天的夏风',
foo: function () {
console.log(this.name)
}
}
var name = '2'
obj.foo() // '下雪天的夏风'
const foo = obj.foo
foo() // 2
所以,
obj.foo
来说,foo
运行在obj
环境,所以this
指向obj
。foo
来说,foo
运行在全局环境,所以this
指向全局环境。那为什么会这样?
const obj = { name: '下雪天的夏风' }
将一个对象赋值给变量 obj
时:js 引擎会先在内存中生成对象{ name: '下雪天的夏风' }
,再将对象的内存地址赋值给变量obj
。
之后在读取obj.name
时,js引擎先在obj
拿到内存地址——>从内存地址获取原始对象——>从对象获取name
属性。
而属性的值可能是一个函数:
const obj = { foo: function() {} }
当对象的属性是函数时:js引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo
属性。
obj: {
foo: 函数的地址
}
此时函数是一个单独的值,所以它可以在不同的环境(上下文)执行。
由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。
所以,this
就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
const foo = function () {};
const obj = { foo };
// 单独执行
foo ()
// obj 环境执行
obj.foo ()
以上。