JavaScript进阶讲解三—>this的指向一

111 阅读4分钟

上一节我们讲了闭包的原理及形成,希望大家可以多去理解一下。本节我们开始讲js中this的指向问题


一、结论与规则与优先级排序

结论

  1. 函数在调用时,JavaScript会默认给this绑定一个值。
  2. this的绑定和定义的位置(编写的位置)没有关系。
  3. this的绑定和调用方式以及调用的位置有关系。
  4. this是在运行时被绑定的。

规则

  1. 默认绑定。
  2. 隐式绑定。
  3. 显示绑定。
  4. new绑定。

优先级排序

  1. .隐式绑定高于默认绑定(默认绑定最低)
  2. 显示绑定优先级高于隐式绑定
  3. new绑定优先级高于隐式绑定
  4. new绑定优先级高于bind(new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高,new绑定可以和bind一起使用,new绑定优先级更高) 接下来我们开始对这4种规则一 一进行讲解

我们先来看一种特殊情况(在全局作用域下的this)

// js文件
console.log(this);

一般来说我们的this都是在函数中使用的,但是我们直接在全局打印呢? 两种情况:1. 浏览器中运行 this指向window 2. node环境下运行 this指向{}

一、默认绑定-》this指向window

什么情况下是默认绑定呢?独立函数调用时。 独立函数:函数没有被绑定到某个对象上进行调用。如下

function foo() {
    console.log(this); // window
}
foo()
function foo() {
    console.log(this); // window
    foo2()
}

function foo2() {
     console.log(this); // window
     foo3()
}

function foo3() {
     console.log(this); // window
}

foo()
function foo(fn) {
    fn()
}

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

foo(bar.bar)

上面的这三种都是默认绑定所以都是window。前两种大家应都能理解。 我们来看看第三种,可能大家会疑惑 为什么第三个是window而不是bar,如果你有这种疑惑,那么你在仔细的看看我们最前面的结论this的绑定和定义的位置(编写的位置)没有关系,和其调用方式以及调用的位置有关系 ,现在大家明白了吗 fn在调用时,可没有绑定到某个对象上哟,他是独立的在调用哟。 那他什么时候this指向bar呢?看下面代码

function foo(fn) {
    fn()
}

var bar = {
    bar: function() {
        console.log(this); // bar
    }
}

// foo(bar.bar)
bar.bar()

看到了吗?我们只改了最后一行代码,他的指向就变成了bar。这就是我接下来要说的隐式绑定

二、隐式绑定-》this指向调用的那个对象

通过某个对象进行调用的就是隐式绑定,换句话说就是他的调用是由一个对象发起的

var bar = {
    bar: function() {
        console.log(this); // bar
    }
}
bar.bar()
function foo(fn) {
    console.log(this); // bar
}

var bar = {
    name: 'bar',
    foo: foo
}
var bar2 = {
    name: 'bar2',
    bar: bar
}
bar2.bar.foo()

三、显示绑定(call、apply、bind)

明确的绑定了this指向的对象的就叫显示绑定

  1. bind返回一个函数,可以延迟调用
  2. apply、call直接调用(但他们两接收的参数不一样,apply只能接收两个参数,第一个参数是this,第二个参数是一个数组,在调用的时候需要把其他参数都放在这个数组里。call可以接收多个参数,第一个参数是this,在调用的时候需要把其他参数一个一个按顺序传入)
function foo() {
    console.log(this);
}

var obj = {
    name: 'obj'
}
foo() // window
foo.call(obj) // obj
foo.apply(obj) // obj

var newFoo = foo.bind(obj)
newFoo() // obj

这里对前两个(foo.call(obj)和foo.apply(obj))指向的是obj,大家应该都可以理解,但是对于newFoo()这个大家可能会有疑问: 大家可能会觉得 newFoo()这个是个独立函数调用啊?上面不是说独立函数调用指向window吗? 是的,这里确实是独立函数调用,但是他在调用之前显示的绑定了一个obj,也就是说默认调用与显示调用冲突了,这里打印的obj,所以我们可以得出结论显示绑定优先级高于默认绑定

四、new绑定 ->指向new的那个对象

function Person(name) {
    this.name = name
    console.log(this); // Person
}

var p = new Person('xt')
console.log(p.name); // xt

可以看到this指向的就是 Person。 接下来我们来看看new与隐式绑定谁的优先级高

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

obj.foo() // obj
new obj.foo() // foo

从上面的结果,我们可以得出结论 new绑定优先级高于隐式绑定