代码输出 this 指向

看代码写输出,

关于 this 指向、闭包、箭头函数、IIFE 的题:

var name = "global";

var obj = {
  name: "obj",
  getName: function () {
    return function () {
      return this.name;
    };
  },
  getNameArrow: function () {
    return () => this.name;
  },
};

// 1
console.log(obj.getName()());

// 2
console.log(obj.getNameArrow()());

// 3
var count = (function () {
  var n = 0;
  return function () {
    n++;
    return n;
  };
})();
console.log(count(), count(), count());

// 4
var funcs = [];
for (var i = 0; i < 3; i++) {
  funcs.push(function () {
    return i;
  });
}
console.log(funcs[0](), funcs[1](), funcs[2]());

// 5
var a = 10;
function foo() {
  console.log(this.a);
}
var obj2 = { a: 20, foo: foo };
var bar = obj2.foo;
obj2.foo();
bar();

参考答案

点击展开
  1. obj.getName()()'global'。getName() 返回了一个普通函数,这个函数是被单独调用的(()()),调用时没有挂在任何对象上,所以 this 指向全局(非严格模式下是 window),this.name 就是 var name = ‘global’。这是经典的”this 丢失”。
  2. obj.getNameArrow()()'obj'。箭头函数没有自己的 this,它捕获的是定义时外层的 this。getNameArrow 是被 obj.getNameArrow() 调用的,此时外层 this 是 obj,箭头函数就锁定了 obj,所以拿到 ‘obj’。这正是①和②的对比考点。
  3. count(), count(), count()1 2 3。IIFE 立即执行,内部 n 通过闭包被返回的函数持续引用,不会被销毁。每次调用累加,所以是 1 2 3(注意从左到右求值)。
  4. funcs[0](<>), funcs[1](<>), funcs[2](<>)3 3 3。var 没有块级作用域,三个函数共享同一个 i。循环结束后 i 已经变成 3,三个闭包读的都是这同一个 i,所以全是 3。
(如果把 var 改成 let,每次迭代是独立作用域,结果就是 0 1 2——常见追问。)
  5. obj2.foo()20bar()10。this 取决于调用方式:obj2.foo() 是方法调用,this 是 obj2,输出 20;bar() 把函数取出来单独调用,this 指向全局,输出 var a = 10。

总结:this 看谁在调用(箭头函数除外,它看定义位置);闭包让内部变量存活下来;var 无块级作用域会让循环闭包翻车。

评论

加载中...