亲宝软件园·资讯

展开

JavaScript中this的绑定你知道几种?

mick 人气:0

执行上下文

我们知道执行上下文分为两种:全局上下文和函数上下文(我的这篇文章对于执行上下文有讲解还对执行上下文和作用域迷糊吗?)。全局上下文只有一个,函数执行上下文是在函数调用的时候创建的。

每个执行上下文都有三个属性:

this到底是什么呢

this是在运行时绑定的,并不是在编写时绑定的,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何的关系,只取决于函数的调用方式。

调用位置

要理解this的绑定过程,首先要理解调用位置。调用位置就是函数的调用的位置(不是声明的位置)。所以我们要先来分析调用栈(也就是执行上下文栈)。我们先来看一段代码。

function baz() {
  console.log("baz")
  bar()
}
function bar() {
  console.log("bar")
  foo()
}
function foo() {
  console.log("foo")
}
baz()

当代码执行到foo(),进入foo的函数体,此时当前的调用栈为:

ECStack = [
    fooContext, // foo
    barContext, // bar
    bazContext, // baz
    globalContext, // 全局
]

通过调用栈我们就可以很清晰的找到函数的调用位置。baz在全局调用,bar在baz里调用,foo在bar里调用。

那函数在执行的时候是如何决定this的绑定对象的呢?

绑定规则

通过绑定规则决定this的绑定对象。

默认绑定

最常用的调用类型:独立函数调用。

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

var a = 2;
foo()

函数调用的时候,使用了this的默认绑定,因此this指向全局对象。

那么我们怎么知道这里应用了默认绑定呢?可以通过分析调用位置来看看 foo() 是如何调用的。在代码中,foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。

所以,在全局环境中调用一个函数,函数内部的this指向的是全局变量window。

隐式绑定

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

var obj = {
    a: 2,
    foo: foo
};

obj.foo(); // 2

这段代码我们看到foo的声明位置是在全局的,但是它被当做引用属性添加到了obj中。调用位置使用obj上下文引用函数。当foo被调用时候,它是被obj对象所包含的,落脚点指向obj对象。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。

通过一个对象调用其内部的一个方法,该方法的执行上下文中的this指向对象本身

我们看个特殊的例子

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

var obj = {
  a: 2,
  foo
}

var bar = obj.foo
var a = "mick"
bar() // mick

bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

我们再看另一种情况

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

function doFoo(fn) {
  fn()
}

var obj = {
  a: 2,
  foo
}

var a = "mick"

doFoo(obj.foo) // mick

嵌套函数中的this 不会从外层函数中继承。this永远指向最后调用它的那个对象

显示绑定

可以使用call、apply或bind方法。如果对这三个方法的实现原理感兴趣可以看看这篇手写call、apply、bind

function foo() {
  console.log(this.a)
}
var obj = {
  a: 2
}

foo.call(obj) // 2

通过call方法,可以在调用foo时候,强制把它的this绑定到obj上。

new绑定

这里我们先说一下new来调用函数会发生哪些事情

function foo(a){
    this.a = a
}
var bar = new foo(2)
console.log(bar.a)

使用new来调用foo时,我们会构造一个新对象并把它绑定到foo调用中的this上。

特例

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

var a = 2
var o = { a: 3, foo: foo }
var p = { a: 4 }

o.foo() // 3
;(p.foo = o.foo)() // 2

赋值表达式p.foo = o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者o.foo()。所以这里是默认绑定。

面试题

下面我们看个面试题吧

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person2 = { name: 'person2' }

person1.foo1()
person1.foo1.call(person2)

person1.foo2()
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

我们一个个来解析一下。

简单的谈了谈this的绑定,欢迎留言你的问题,大家一起学习一起进步!!!

加载全部内容

相关教程
猜你喜欢
用户评论