es6中 作用域是 全局 函数 块 ,对象里的函数 作用域的上层作用域是什么 ?

6 阅读3分钟

这是一个非常经典且容易混淆的问题。核心结论是:对象不构成单独的作用域

因此,对象里的函数,它的“上层作用域”是定义该函数时所在的那个环境(全局、函数或块),而不是它所属的对象。

详细解释

  1. JavaScript 只有三种作用域(ES6起):

    • 全局作用域
    • 函数作用域
    • 块级作用域{} 配合 let/const
  2. 对象的大括号 {} 不是作用域
    对象字面量的 {} 只是表示一个数据结构,它不会创建新的作用域。这与函数的 {} 或 if/for 后的 {} 完全不同。

  3. 函数的“上层作用域”由定义位置决定(词法作用域)
    一个函数在哪里被定义,它的上层作用域就是那个位置的当前环境。这与它被挂在哪个对象上、甚至被调用时是通过哪个对象调用的,毫无关系

具体例子说明

例子1:对象定义在全局作用域

// 全局作用域
let globalVar = 'global';

const myObject = {
  method: function() {
    console.log(globalVar); // 能访问到全局变量
    // 这里能访问 globalVar,是因为上层作用域是全局作用域
  }
};

myObject.method(); // 输出 'global'

解析method 函数定义在全局代码中,所以它的上层作用域就是全局作用域。它完全不关心自己是 myObject 的一个属性。

例子2:对象定义在函数作用域内

function outerFunction() {
  let outerVar = 'I am from outer function';

  const myObject = {
    method: function() {
      console.log(outerVar); // 能访问到 outerFunction 内的变量
      // 这里的上层作用域是 outerFunction 的函数作用域
    }
  };

  myObject.method();
}

outerFunction(); // 输出 'I am from outer function'

解析method 函数定义在 outerFunction 内部,所以它的上层作用域是 outerFunction 的函数作用域。它可以看到 outerVar

例子3:对象定义在块级作用域内(ES6)

{
  let blockVar = 'I am from block scope';

  const myObject = {
    method: function() {
      console.log(blockVar); // 能访问到块级变量
      // 这里的上层作用域就是这个 {} 块级作用域
    }
  };

  myObject.method(); // 输出 'I am from block scope'
}

解析method 函数定义在块级作用域 {} 内,所以它的上层作用域是这个块级作用域

特别提醒:不要和 this 混淆

很多人会问这个问题,是因为把“作用域链”和“原型链”(区别)或 this 的指向搞混了。

  • 作用域链:决定变量(如 varletconst)的查找规则。静态的,由代码编写位置决定。对象不参与。
  • this:决定调用者是谁。动态的,由调用方式决定。对象会参与(通过 . 调用时,this 指向该对象)。
var name = 'Global';

const obj = {
  name: 'Object',
  method: function() {
    console.log(name);     // 查找变量 name,走作用域链。输出 'Global'
    console.log(this.name); // 查找属性 this.name,看调用方式。输出 'Object'
  }
};

obj.method();
  • name:沿着作用域链找到了全局的 'Global'
  • this.name:因为是通过 obj.method() 调用,this 指向 obj,所以找到了 obj 里的 'Object'

总结

问题答案
对象里的函数,上层作用域是什么?定义该函数时所在的那个作用域(全局/函数/块)。
对象本身是作用域吗?不是。对象的大括号不创建作用域。
那为什么函数可以访问对象里的其他属性?不能直接访问。必须通过 this.属性名 或 对象名.属性名。不能直接写 属性名 就去访问对象里的属性,因为那不是作用域链的工作。

所以,忘记“对象作用域”这个概念,在JavaScript中它不存在。记住:只有函数和块(配合let/const)才能创建新的作用域,对象只是数据的容器。