Hulk's Blog

文章
笔记

一文搞懂this指向问题

1/2/2021 • ☕️ 3 min read

var bar = {
    myName:"time.geekbang.com",
    printName: function () {
        console.log(myName)
    }    
}
function foo() {
    let myName = "极客时间"
    return bar.printName
}
let myName = "极客邦"
let _printName = foo()
_printName()
bar.printName()

在 printName 函数里面使用的变量 myName 是属于全局作用域下面的,所以最终打印出来的值都是“极客邦”。这是因为 JavaScript 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。

在对象内部的方法中使用对象内部的属性是一个非常普遍的需求。但是 JavaScript 的作用域机制并不支持这一点,基于这个需求,JavaScript 又搞出来另外一套 this 机制。

arrow function的this指向

箭头函数因为其内部 this 的值无法被改变,它与 创建 箭头函数时上下文的this指向相同

const fun = () => {
  console.log(this);
};
fun();

// Window {window: Window, self: Window, document: document, name: "", location: Location, …}

new 关键字

使用 new 关键字调用函数时,函数中的 this 指向为 JS 创建的新对象

function func(){
    console.log(this);
    this.name = 'news';
    console.log(this);
};
new func();

// func {}
// func {name: 'news'}

bind方法

使用 bind 方法可将函数绑定到其外部的 this

注意

  • 避免使用 bind 将函数绑定到其外部的 this。使用箭头函数替代,因为这样 this 可以在函数声明就能清楚地看出来,而非在后续代码中看到。
  • 不要使用 bind 设置 this 为与父对象无关的值;这通常是出乎意料的,这也是 this 获得如此糟糕名声的原因。考虑将值作为参数传递;它更加明确,并且可以使用箭头函数
function func() {
    console.log(this);
}

let o = {
    name: 'bind'
};

func.bind(o)();

// { name: 'bind' }

apply和call方法

使用 apply 和 call 方法可将this绑定到传入函数的第一个参数。两个方法区别在于通过 apply 调用时实参是放到数组中的,而通过 call 调用时实参是逗号分隔的。

function func() {
    console.log(this,a);
}

let o = {
    name: 'bind'
};

func.call(o,5, 6, 2, 3, 7);
func.apply(o,[5, 6, 2, 3, 7]);

// {name: "bind"} Arguments(5) [5, 6, 2, 3, 7, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// {name: "bind"} Arguments(5) [5, 6, 2, 3, 7, callee: ƒ, Symbol(Symbol.iterator): ƒ]

函数作为对象的成员变量被调用

函数作为对象的成员变量被调用时,this的指向为调用函数的对象

let obj = {
    name: 'obj',
    func: function (){
        console.log(this)
    }
};


obj.func();
// {name: "obj", func: ƒ}

当函数没有作为方法被调用,而是被赋值给另一个变量时,则根据那个变量来判断this指向

let obj = {
    name: 'obj',
    func: function (){
        console.log(this)
    }
};

let func = obj.func;
func();
// Window {window: Window, self: Window, document: document, name: "", location: Location, …}

浏览器和node环境下

在浏览器和node环境下全局this的指向会有所不同

  • 在浏览器里,this 指向 Window。
  • 在 Node.js 里,this指向 Global

this 的设计缺陷以及应对方案

  • 嵌套函数中的 this 不会从外层函数中继承
var myObj = {
  name : "极客时间", 
  showThis: function(){
    console.log(this)
    function bar(){console.log(this)}
    bar()
  }
}
myObj.showThis()
  • 普通函数中的 this 默认指向全局对象。

    • 不过这个设计也是一种缺陷,因为在实际工作中,我们并不希望函数执行上下文中的 this 默认指向全局对象,因为这样会打破数据的边界,造成一些误操作。如果要让函数执行上下文中的 this 指向某个对象,最好的方式是通过 call 方法来显示调用。

    • 这个问题可以通过设置 JavaScript 的“严格模式”来解决。在严格模式下,默认执行一个函数,其函数的执行上下文中的 this 值是 undefined,这就解决上面的问题了。