译javascript的this关键词理解

10次阅读

共计 2929 个字符,预计需要花费 8 分钟才能阅读完成。

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

http://www.ruanyifeng.com/blo…

http://www.ruanyifeng.com/blo…

今天在留言区发现了一国外大神关于 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/…

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

正文完
 0