JavaScript基础-表达式与运算符-this

93 阅读5分钟

JavaScript中的this是当前执行上下文(globalfunctioneval)的一个属性。在非严格模式下,总是指向一个对象,在严格模式下可以是任何值

全局上下文

无论是否在严格模式下,在全局执行环境this都指向全局对象

console.log(this === window) // true

函数上下文

在多数情况下,this指向的对象通常由函数的调用方式所决定。this不能在执行期间被赋值,并且在每次函数调用时this的值也可能不同。

非严格模式下,且没有被设置,this指向全局对象(浏览器中为window对象,node环境下为globalThis对象)

function bar() {
	console.log(this === window)
}
bar();

严格模式下,如果在进入执行环境时没有设置this值,thisundefined

function bar() {
	'use strict'
	console.log(this === window)
}
bar();
更改函数this指向

如果想要把this的值从一个环境传到另一个,就需要使用callapply方法。

** call\apply 方法 **

在非严格模式下使用 call 和 apply 时,如果用作 this 的值不是对象,则会被尝试转换为对象。null 和 undefined 被转换为全局对象。原始值如 7 或 'foo' 会使用相应构造函数转换为对象。因此 7 会被转换为 new Number(7) 生成的对象,字符串 'foo' 会转换为 new String('foo') 生成的对象。

function add(c, d) {
	return this.a + this.b + c + d;
}
const o = {a: 1, b: 3}

// 第一个参数是用作“this”的对象
// 其余参数作为函数参数
add.call(o, 5, 7)

// 第一个参数作为“this”的对象
// 第二个参数是一个数组,数组中的两个成员用作函数参数
add.apply(o, [10, 20])

** bind 方法 **

es5中引入Function.prototype.bind()。调用func.bind({})会创建一个与func具有相同函数体和作用域的函数,但是在新的函数中this将永久绑定到第一个参数上。

function func() {
	console.log(this);
}
const newFunc = func.bind({name: 'alex', age: 22})
newFunc()

** 箭头函数 **

在箭头函数中,this与封闭词法环境的this保持一致。

在全局代码中,它将被设置为全局对象:

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

如果将this传递给call、bind、或者apply来调用箭头函数,它将被忽略。不过你仍然可以为调用添加参数,不过第一个参数(thisArg)应该设置为null。

// 接着上面的代码
// 作为对象的一个方法调用
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// 尝试使用call来设定this
console.log(foo.call(obj) === globalObject); // true

// 尝试使用bind来设定this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

在其他函数内创建的箭头函数的this被设置为封闭的词法环境的。

// 创建一个含有bar方法的obj对象,
// bar返回一个函数,
// 这个函数返回this,
// 这个返回的函数是以箭头函数创建的,
// 所以它的this被永久绑定到了它外层函数的this。
// bar的值可以在调用中设置,这反过来又设置了返回函数的值。
var obj = {
  bar: function() {
    var x = (() => this);
    return x;
  }
};

// 作为obj对象的一个方法来调用bar,把它的this绑定到obj。
// 将返回的函数的引用赋值给fn。
var fn = obj.bar();

// 直接调用fn而不设置this,
// 通常(即不使用箭头函数的情况)默认为全局对象
// 若在严格模式则为undefined
console.log(fn() === obj); // true

特殊情况

// 但是注意,如果你只是引用obj的方法,
// 而没有调用它
var fn2 = obj.bar;
// 那么调用箭头函数后,this指向window,因为它从 bar 继承了this。
console.log(fn2()() == window); // true

类上下文

的构造函数中,this是一个常规对象。类中所有非静态属性或方法都会配添加到this的原型中:

class Foo {
	constructor() {
    	const proto = Object.getPrototypeOf(this)
        console.log(Object.getOwnPropertypeNames(proto)
        this.foo = this.foo.bind(this)
    }
    
    bar() {
    	console.log(this)
    }
    
    foo() {
    	console.log(this)
    }
}

new Foo() // ['constructor', 'bar']

** 对象方法中的this ** 当函数作为对象里的方法被调用时,this 被设置为调用该函数的对象。这样的行为完全不会受函数定义方式或位置的影响。

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // 37

var o = {prop: 37};
function independent() {
  return this.prop;
}
o.f = independent;

console.log(o.f()); // 37

// this 的绑定只受最接近的成员引用的影响
o.b = {g: independent, prop: 42};
console.log(o.b.g()); // 42

原型链中的this

对于在对象原型链上某处定义的方法,同样的概念也适用。如果该方法存在于一个对象的原型链上,那么 this 指向的是调用这个方法的对象,就像该方法就在这个对象上一样。

class Obj {
  constructor() {
    this.a = 10
    this.b = 20
    this.c = 30
  }
}
Obj.prototype.func = function() {
  return this.a + this.b + this.c
}
const o = new Obj()
console.log(o.func()) // 60

getter 与 setter 中的 this

function sum() {
  return this.a + this.b + this.c
}
var o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return this.sum / 3
  }
};

Object.defineProperty(o, 'sum', {
    get: sum, 
    enumerable: true, 
    configurable: true
})

console.log(o.average, o.sum) // 2, 6

派生类

派生类的构造函数没有初始的this绑定。在构造函数中调用super()方法会生成一个this绑定,并相当于执行以下代码。在调用super()方法之前引用this会抛出错误。

this = new Foo()

派生类中的构造函数必须调用super()方法或者返回一个对象,否则会报错。

class Foo {}
// 正确
class Bar extends Foo {}
// 正确
class Bar extends Foo {
	constructor() {
    	super()
    }
}
// 正确
class Bar extends Foo {
	constructor() {
    	return {}
    }
}
// 错误
class Bar extends Foo {
	constructor() {}
}

其他

在DOM事件处理函数中的this指向

当函数被用作事件处理函数时,它的 this 指向触发事件的元素

document.querySelector('#app').addEventListener('click', function(e) {
	console(this == e.currentTarget)
})
内联事件处理函数中的this指向

当代码被内联 on-event 处理函数 调用时,它的this指向监听器所在的DOM元素

<button onclick="console.log(this)">click me</button>