this 是 Javascript 语言 的一个保留关键字, 它代表函数运行时自动生成的一个内部对象, 只能在函数内部使用. 首先要记住的是, 函数中的 this 并不是在创建时确定下来的, 而是在函数执行的时候才确定的. 这个也是造成使用 this 时出现很多误解的原因.
本章内容:
谁调用, 谁负责
new 的过程
怪异的 return
被干掉的 this
丢失的 this
在开始今天的内容之前, 先来看看下面的代码:
- // 实例 1
- var name = 'a';
- var obj = {
- name: 'b',
- getName: function() { console.log(this.name);
- }
- };
- obj.getName();
- var getName = obj.getName;
- getName();
- var obj2 = (function() {
- return function() {
- console.log(this.name);
- }
- })();
- obj2();
- // 实例 2
- function Test() {
- console.log(this.name);
- }
- Test();
- var test = new Test();
- function Test2() {
- this.name = 'c';
- }
- var test2 = new Test2();
- console.log(test2.name);
- // 实例 3
- function Test3() {
- this.name = 'd';
- return {}; // 1 | 'd' | null | undefined
- }
- var test3 = new Test3();
- console.log(test3.name);
- function Test4() {
- this.name = 'd';
- return 1;
- }
- var test4 = new Test4();
- console.log(test4.name);
- // 实例 4
- function Test5() {
- "use strict";
- console.log(this.name);
- }
- console.log(Test5());
- function test6() {
- console.log(this.name);
- }
- // 实例 5
- var btn = document.querySelector('.btn');
- btn.name = 'button';
- btn.onclick = test6;
- var btn2 = document.querySelector('.btn2');
- btn2.name = 'button';
- btn2.onclick = function() {
- test6();
- }
保存着你的答案, 接下来我们一一分析.
1. 谁调用, 谁负责
来回顾一下 实例 1:
- var name = 'a';
- var obj = {
- name: 'b',
- getName: function() {
- console.log(this.name);
- }
- };
- obj.getName(); // b
- var getName = obj.getName;
- getName(); // a
- var obj2 = (function() {
- return function() {
- console.log(this.name);
- }
- })();
- obj2(); // a
首先说说 obj.getName(), 它打印的是 b, 为什么呢? 因为调用 getName() 的是 obj 这家伙, 自然, 它的 this 就指向了 obj, 所以 this.name 自然就是 b.
而接下来的 getName() 为毛打印的是 a 呢? 因为当前调用 getName() 的并不是 obj 了, 而是全局变量 window, 所以打印结果是 a.
在 obj2 中, 我们采用了立即执行函数, 它返回一个函数, 而当我们调用时, 调用它的也是全局变量 window, 所以其打印的结果也是 a.
注: 匿名函数内的 this 是指向 window 的, 更准确的说是指向调用者.
结论: 谁调用, 谁负责.
2. new 的过程
- function Test() {
- console.log(this.name);
- }
- Test(); // a
- var test = new Test(); // undefined
- function Test2() {
- this.name = 'c';
- }
- var test2 = new Test2();
- console.log(test2.name); // c
要搞清楚上面为什么打印 c, 就有必要了解 new 的过程.
new Test2() 其实执行了三步动作:
- var obj = {};
- obj.__proto__ = Test2.prototype;
- Test2.call(obj);
首先, 创建了一个空对象 obj 接着, 将这个空对象的
[[Prototype]](__proto__)
成员指向了 Test2 函数对象 prototype 成员对象最后, 将 Test2 函数对象的 this 指针替换成 obj.
当没有显式返回值或者返回为基本类型, null 时, 默认将 this 返回. 可参考: 3. 怪异的 return
搞清楚 new 的过程, 相信你对打印出 c 的原因已经明白了.
结论: 当使用 new 时, 函数内部的 this 指向已经改变了, 并不指向 window.
3. 怪异的 return
在上面的 "new 的过程" 中, 我们了解了 new 的函数内的 this 指向, 但是, 也有一些例外, 比如下面:
- function Test3() {
- this.name = 'd';
- return {};
- }
- var test3 = new Test3();
- console.log(test3.name); // undefined
- function Test4() {
- this.name = 'd';
- return 1;
- }
- var test4 = new Test4();
- console.log(test4.name); // d
可以再试试
return 'd' | null | undefined
中的一个或更多类型.
结论: 如果 return 的是一个对象 (null 除外), 那么 this 指向的这个对象, 否则 this 依旧指向实例对象.
4. 被干掉的 this
- function Test5() {
- "use strict";
- console.log(this.name);
- }
- console.log(Test5()); // Uncaught TypeError: Cannot read property 'name' of undefined
在严格模式下, 函数内的 this 都指向了 undefined.
除了严格模式下, 函数内的 this 被干掉了. 在 ES6 中箭头函数的 this 同样被干掉了, 但是它有点不一样, 它可以盗用它最接近一层作用域内的 this
5. 丢失的 this
- function test6() {
- console.log(this.name);
- }
- var btn = document.querySelector('.btn');
- btn.name = 'button';
- btn.onclick = test6; // button
- var btn2 = document.querySelector('.btn2');
- btn2.name = 'button';
- btn2.onclick = function() {
- test6(); // a
- }
在 JavaScript 中, 事件绑定的函数的 this 是指向绑定元素的, 比如上面的 onclick 绑定的 test6 函数内的 this 是指向 btn 元素的. 而 btn2 绑定事件为什么打印 a 呢? 回顾一下上面第一节中提到的 "谁调用, 谁负责".
此外, setTimeout 和 setInterval 等函数内 this 默认的指向是 Window.
来源: https://juejin.im/entry/5b534d12f265da0fa8673fd1