JS进阶this机制

this机制是js中很重要的知识点,它和作用域查询是两个容易混淆的知识点。本文将详细介绍this的4种绑定规则。

默认绑定

  1. 全局环境中,this指向window。
  2. 独立函数调用时,this指向window。严格模式下指向undefined。
  3. 作为对象的方法调用时,this指向原对象
// 示例1
console.log(window === this) // true

// 示例2
function foo() {
  console.log(window === this) // true
}
foo()

// 示例3
var obj = {
  foo: function() {
  console.log(this === obj) // true
 
  // 注意,这里是独立调用
   function foo2() {
    console.log(this === window) // true
   }
   foo2()
  }
}
obj.foo()

隐式绑定

被直接对象所包含的函数调用时,this隐式绑定到该直接对象。

function foo() {
  console.log(this.a)
}

var o1 = {
  a: 1,
  foo: foo,
  o2: {
   a: 2,
   foo: foo
  }
}

// foo()函数的直接对象是o1,this隐式绑定到o1
console.log(o1.foo()) // 1

// foo()函数的直接对象是o2,this隐式绑定到o2
console.log(o1.o2.foo()) // 2

隐式丢失

隐式丢失是指被隐式绑定的函数丢失绑定对象,进而默认绑定到window对象。

函数别名

var a = 0;
function foo() {
  console.log(this.a)
}
var obj = {
  a: 1,
  foo: foo
}

var fn = obj.foo;
fn() // 0

示例中造成隐式丢失的原因是在赋值时,只把foo()函数赋给了fn,而fn与obj对象之间没有任何联系。

// 等价于
var a = 0;
var fn = function foo(){
  console.log(this.a)
}
fn() // 0

参数传递

var a = 0;
function foo() {
  console.log(this.a)
}
var obj = {
  a: 1,
  foo: foo
}

function foo2(fn) {
  fn()
}
foo2(obj.foo) // 0

和上面的原因类似,把函数作为参数传递给foo2时,只是把foo函数赋给了fn,fn和obj之间没有联系。

// 等价于
var a = 0;
function foo2(fn) {
  fn()
}
foo2(function foo() {
  console.log(this.a)
})

内置函数

var a = 0;
function foo() {
  console.log(this.a)
}
var obj = {
  a: 1,
  foo: foo
}

setTimeout(obj.foo, 100) // 0

内置函数也会造成隐式丢失,原因同上。

// 相当于
var a = 0;
setTimeout(function foo() {
  console.log(this.a)
}, 100)

间接引用

var a = 0;
function foo() {
  console.log(this.a)
}
var o1 = {a: 1, foo:foo}
var o2 = {a: 2}
// 将o1.foo函数赋值给o2.foo函数后再调用,相当于立即调用foo函数
;(o2.foo = o1.foo)() // 0

var a = 0;
function foo() {
	console.log(this.a)
}
var o1 = {a: 1, foo:foo}
var o2 = {a: 2}
o2.foo = o1.foo
// 将o1.foo函数赋值给o2.foo函数后再调用,是作为o2对象的方法调用
o2.foo() // 2

在JavaScript引擎内部,对象o1和方法o1.foo存储在两个不同的内存地址,所以只有o1.foo()这样调用时,this指向o1。

显示绑定

通过bind()、apply()、bind()方法把this绑定到对象上,叫做显示绑定。对于被调用的函数来说叫做间接调用。

var a = 0;
function foo() {
  console.log(this.a)
}

var obj = {
  a:1
}

foo.call(obj) // 1

非严格模式下null或undefined值会被转换为全局对象,严格模式下this值是指定值。

// 非严格模式
var a = 0;
function foo() {
  console.log(this.a)
}

var obj = {
  a:1
}
foo.call(null) // 0

// 严格模式
'use strict'
var a = 0;
function foo() {
  console.log(this.a)
}

var obj = {
  a:1
}

foo.call(null) // Uncaught TypeError

硬绑定

硬绑定是显示绑定的一种,使this不能再修改。

var a = 0;
function foo() {
  console.log(this.a)
}
var obj = {
  a: 1
}

var foo2 = function() {
  foo.call(obj)
}

foo2() // 1
setTimeout(foo2, 100) // 1
foo2.call(window) // 1

new绑定

当函数作为构造函数调用时,函数中的this绑定被称作new绑定。

var obj = {
  a: 1
}

function foo() {
  this.a = 2
}

var test = new foo()
console.log(test) // {a: 2}

当使用构造函数方式调用对象的方法时,this不再指向原始对象。

var o = {
  m: function(){
    console.log(this === o) // false
    console.log(this === obj) // false
  }
}
var obj = new o.m();

优先级

this的四种绑定机制,如果同时存在两种以上的绑定规则,它们会遵循下面的绑定顺序:

  1. 是否是new绑定?如果是,this绑定的是新创建的对象
var fn = new foo();
  1. 是否是显式绑定?如果是,this绑定的是指定的对象
var fn = foo.call(obj2);
  1. 是否是隐式绑定?如果是,this绑定的是属于的对象
var fn = obj1.foo(); 
  1. 如果都不是,则使用默认绑定
var fn = foo();

结语

this的四种绑定规则:默认绑定、隐式绑定、显式绑定和new绑定,分别对应函数的四种调用方式:独立调用、方法调用、间接调用和构造函数调用。

日常开发中很多的错误都是由于this的隐式丢失造成的,平常多观察多思考可以减少错误的发生。

优秀文章首发于聚享小站,欢迎关注!
原文地址:https://www.cnblogs.com/yesyes/p/15352003.html