【译】javascript的this关键词理解

作者: MJ 分类: javascript 发布时间: 2019-04-23 17:27

一直以来,javascript里边的this都是一个很难理解的东西,之前看的最多的就是阮一峰老师关于this的理解:

http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html

今天在留言区发现了一国外大神关于this的理解,借助翻译工具读了一下原文,相对来说是最好的关于理解this的文章,就翻译了一下,也算是记录一下。

JavaScript的一个常用特性是“this”关键字,但它也常常是该语言中最令人困惑和误解的特性之一。“this”到底是什么意思?它是如何决定的?

本文试图澄清这种困惑,并以一种清晰的方式解释这个问题的答案。

“this”关键字对于那些用其他语言编程的人来说并不新鲜,而且它通常引用在通过类的构造函数实例化类时创建的新对象。例如,如果我有一个类Boat(),它有一个方法moveBoat(),当在moveBoat()方法中引用“this”时,我们实际上是在访问新创建的Boat()对象。

在JavaScript中,当使用“new”关键字调用函数构造函数时,函数构造函数中也有这个概念,但是它不是惟一的规则,而且“this”常常可以引用来自不同执行上下文的不同对象。如果您不熟悉JavaScript的执行上下文,我建议您阅读我关于这个主题的另一篇文章(本人注:文章找不到了)。谈得够多了,让我们来看一些JavaScript例子:

// 全局作用域

foo = 'abc';
alert(foo); // abc

this.foo = 'def';
alert(foo); // def

无论何时在全局上下文中使用关键字“this”(而不是在函数中),它总是指向全局对象。现在让我们看看函数中“this”的值:

var boat = {
    size: 'normal',
    boatInfo: function() {
        alert(this === boat);
        alert(this.size);
    }
};

boat.boatInfo(); // true, 'normal'

var bigBoat = {
    size: 'big'
};

bigBoat.boatInfo = boat.boatInfo;
bigBoat.boatInfo(); // false, 'big'

那么上面的“this”是如何确定的呢?我们可以看到一个对象boat,它有一个属性size和一个方法boatInfo()。在boatInfo()中,如果该对象的值是实际的boat对象,它将发出警报,并警告该对象的size属性。因此,我们使用boat.boatInfo()调用函数,可以看到这是boat对象,并且boat的size属性是正常的。

然后我们创建另一个对象bigBoat,它的size属性为big。然而,bigBoat对象没有一个boatInfo()方法,因此我们使用bigBoat从boat复制该方法。boatInfo = boat.boatInfo。现在,当我们调用bigBoat.boatInfo()并输入函数时,我们看到它不等于boat, size属性现在是big。为什么会这样?这个值在boatInfo()中是如何变化的?

您必须意识到的第一件事是,任何函数中的这个值都不是静态的,它总是在每次调用函数时确定的,但是在函数实际执行之前,它是代码。函数内部的值实际上是由父作用域提供的,在父作用域中调用函数,更重要的是,函数语法是如何编写的。

每当调用一个函数时,我们必须查看方括号/圆括号“()”的左边。如果在括号的左边我们可以看到一个引用,那么传递给函数调用的“this”的值就是该对象所属的值,否则它就是全局对象。让我们来看一些例子:

function bar() {
    alert(this);
}
bar(); // global - 因为方法bar()在调用时属于全局对象

var foo = {
    baz: function() {
        alert(this);
    }
}
foo.baz(); // foo - 因为方法baz()在调用时属于对象foo

如果到目前为止一切都很清楚,那么上面的代码显然是有意义的。通过用两种不同的方式编写call / invoke语法,我们可以在同一个函数中更改“this”的值,从而使事情变得更加复杂:

var foo = {
    baz: function() {
        alert(this);
    }
}
foo.baz(); // foo - 因为baz在调用时属于foo对象

var anotherBaz = foo.baz;
anotherBaz(); // global - 因为方法anotherBaz()在调用时属于全局对象,而不是foo

在这里,我们看到baz()中的“this”值每次都是不同的,因为它的语法调用有两种不同的方式。现在,让我们看看“this”在深度嵌套对象中的值:

var anum = 0;

var foo = {
    anum: 10,
    baz: {
        anum: 20,
        bar: function() {
            console.log(this.anum);
        }
    }
}
foo.baz.bar(); // 20 - 因为()的左边是bar,它在调用时属于baz对象

var hello = foo.baz.bar;
hello(); // 0 - 因为()的左边是hello,它在调用时属于全局对象

另一个经常被问到的问题是如何在事件处理程序中确定关键字“this”?答案是,事件处理程序中的“this”总是指向它所触发的元素。我们来看一个例子:

<div id="test">I am an element with id #test</div>

function doAlert() { 
    alert(this.innerHTML); 
} 

doAlert(); // undefined 

var myElem = document.getElementById('test'); 
myElem.onclick = doAlert; 

alert(myElem.onclick === doAlert); // true 
myElem.onclick(); // I am an element

这里我们可以看到,当第一次调用doAlert()时,它会发出未定义的警报,因为doAlert()属于全局对象。然后我们写myElem。onclick = doAlert,它将函数doAlert()复制到myElem的onclick()事件。这基本上意味着无论何时触发onclick(),它都是myElem的一个方法,这意味着“This”的值就是myElem对象。

关于这个主题,我想指出的最后一点是,“this”的值也可以使用call()和apply()手动设置,覆盖了我们今天讨论的内容。同样有趣的是,当在函数构造函数中调用“this”时,“this”引用构造函数中所有实例中新创建的对象。原因是函数构造函数是用“new”关键字调用的,它创建了一个新对象,其中构造函数中的“this”总是引用刚刚创建的新对象。

总结

希望今天的博客文章已经澄清了对“this”这个关键字的任何误解,你可以一直知道“this”的正确值。我们现在知道,“this”的值从来不是静态的,它的值取决于函数是如何调用的。

原文:http://davidshariff.com/blog/javascript-this-keyword/#first-article

欢迎关注小程序,感谢您的支持!

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

邮箱地址不会被公开。 必填项已用*标注