Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 27 题:关于 const 和 let 声明的变量不在 window 上

Open funlee opened this issue 5 years ago • 19 comments

在ES5中,顶层对象的属性和全局变量是等价的,var 命令和 function 命令声明的全局变量,自然也是顶层对象。

var a = 12;
function f(){};

console.log(window.a); // 12
console.log(window.f); // f(){}

但ES6规定,var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性,但 let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

let aa = 1;
const bb = 2;

console.log(window.aa); // undefined
console.log(window.bb); // undefined

在哪里?怎么获取?通过在设置断点,看看浏览器是怎么处理的:

letandconst

通过上图也可以看到,在全局作用域中,用 let 和 const 声明的全局变量并没有在全局对象中,只是一个块级作用域(Script)中

怎么获取?在定义变量的块级作用域中就能获取啊,既然不属于顶层对象,那就不加 window(global)呗。

let aa = 1;
const bb = 2;

console.log(aa); // 1
console.log(bb); // 2

funlee avatar Mar 06 '19 03:03 funlee

qq20190306-113547

gaomin avatar Mar 06 '19 03:03 gaomin

js的这种"绝对领域"(作用域)是通过什么创造的呢?是通过”{ }“这两个符号创造的,有代码为证:

  let a = 1;
  {
    let aa = 20;
    console.log(aa); // 20
  }

  console.log(a); // 
  console.log(aa); // Uncaught ReferenceError: aa is not defined
  • 注意:这里的”{ }“要区分对象的写法var obj = { },如果上面的代码在"{ }"前面放上一个等号就会是当作对象词法解析了。

henry-fun avatar Mar 06 '19 07:03 henry-fun

在ES5中,全局变量直接挂载到全局对象的属性上,所以能在window上看到var声明的变量 在ES6中,全局对象的属性和全局变量脱钩,但是为了保持兼容性,旧的不变,所以var、function声明的全局变量依然可以在window对象上看到,而let、const声明的全局变量在window对象上看不到

windluo avatar Mar 06 '19 07:03 windluo

js的这种"绝对领域"(作用域)是通过什么创造的呢?是通过”{ }“这两个符号创造的,有代码为证:

  let a = 1;
  {
    let aa = 20;
    console.log(aa); // 20
  }

  console.log(a); // 
  console.log(aa); // Uncaught ReferenceError: aa is not defined
  • 注意:这里的”{ }“要区分对象的写法var obj = { },如果上面的代码在"{ }"前面放上一个等号就会是当作对象词法解析了。

你这个相当于 是块作用域了,没有在全局作用域中

jjeejj avatar Mar 08 '19 01:03 jjeejj

与之相比var let const三种,前者因为var的变量会提升到window,但是let个const不会,let ,const会生成块作用域,同一作用域下let和const不能声明同名变量,而var可以

xxxin128 avatar Jul 10 '19 06:07 xxxin128

我同时还想补充一点的就是如果在声明之前调用let const 声明的变量的时候会报错(这里面有一个暂时性死区的问题)不能再声明之前调用 例如:

console.log(a); //Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 1;

YOMXXX avatar Jul 10 '19 06:07 YOMXXX

const和let会生成块级作用域,可以理解为

let a = 10;
const b = 20;
相当于:
(function(){
         var  a = 10;
         var b = 20;
})()

ES5没有块级作用域的概念,只有函数作用域,可以近似理解成这样。 所以外层window必然无法访问。

lijiayi01 avatar Jul 10 '19 07:07 lijiayi01

根据以上的讨论,我也补充下。 如下创造了2个Block,分别对应变量e和f1个Script(也可以理解为顶级Block),对应变量a和b,剩下的c和d作为全局变量const和let的作用域

CHristopherkeith avatar Jul 14 '19 10:07 CHristopherkeith

继续补充一下函数和try catch的,五种应该全了 2

l-x-f avatar Sep 12 '19 04:09 l-x-f

scope作用域链

SnailOwO avatar Jan 03 '20 06:01 SnailOwO

关于这道题题解,发现答案没有从根本上解释全局环境下声明的let和const 为什么没有挂在window对象 上面???

其实关于这道题我们需要了解全局环境与全局执行上下文的相关概念后,才能解释为什么是这样 关于相关概念我们需要参阅ECMA规范 https://www.ecma-international.org/ecma-262/6.0/#sec-global-environment-records

GlobalEnv是一个复合环境,包括一个由global构成的对象环境(objEnv)和一个一般声明的环境(declsEnv)组合而成,它是双环境组成的,统一交付一个环境存取的界面(objEnv/declsEnv 对应 Global/Script)

let/const 声明会放在declsEnv里面,而var的变量会通过ObjEnv来声明, 所以显而易见说明,let,const 声明的变量不在window对象

其实这里还有很多内容可以展开讨论

  • 关于执行上下文的lexicalEnvironment 和 VariableEnviroment指向
  • 关于环境与执行上下文的关系
  • 关于eval('var x = 0;let y = 0') 执行上下文的lexicalEnvironment和VariableEnviroment指向问题

关于环境概念也可参考的的笔记

关于答案更权威解释和更多可以展开内容的讨论 参考极客时间

XiaoDHuang avatar Jan 06 '20 14:01 XiaoDHuang

同意 XiaoDHuang 的解答。 要想知道为什么 const 和 let 声明的变量不在 window 上,理解 ECMAScript 文档中的 environment record概念即可。细节可以看我个人整理的知识点 《一起读ECMAScript-ECMAScript 中是如何讲解 environment record 和 executing context 的》

另外,最高赞答案说“全局环境下通过 let 和 const 声明变量处于script块级作用域中“,这句话概念上肯定是不正确的。没有理解 TC39 成员是如何在向后兼容 ES5 的情况下引入块级作用域的。 要想理解 ES6 引入的块级作用域只需要看看 ECMAScript 当中是如何解释 for 循环的。《ECMAScript 是如何通过 declaration 识别 block scope的》

那么如何在 global 环境中获取? 直接通过 identifier 获取就可以,只是没有被绑定在 global object 上。原理是 resolve binding 仍然可以通过 global execution context 指向的 lexical environment record 中的 declarative environment record 获取。

BruceYuj avatar Mar 11 '20 08:03 BruceYuj

在定义变量的块级作用域中就能获取

fariellany avatar May 26 '20 06:05 fariellany

class、let定义的即便是全局对象,但不是顶层对象的属性,在window自然获取不到,只能去定义变量的块级作用域里获取

soraly avatar Jun 17 '20 09:06 soraly

var的创建和初始化被提升,赋值不会被提升; let的创建被提升,初始化和赋值不会被提升; function的创建、初始化和赋值均会被提升;

JoMartinezZhu avatar Apr 28 '21 08:04 JoMartinezZhu

我开始以为这个let和const以及var不难,当我真正去尝试深入的时候发现涉及到一个全新的概念:script作用域。我理解他是我认为他和window是同级的作用域,他们共存的,但是一直有一个东西困扰我就是,这个script作用域是怎么来的?是在script脚本开始执行时就和window一样被创建了?可当我没有写任何代码就debugger的时候,我发现他并不存在,只有全局window的存在。当我尝试使用const和let时,他们便被创建出来了!同时我又去验证了一件事情,我在一个块作用域下去创建一个变量,如果我使用var,那么这个变量依然在全局,但是当我使用const或let时,他会创建一个全新的块作用域,并将这些变量放到这个块作用域下。所以我总结了一下:

  1. script作用域是块作用域的全局,就像全局作用域和函数作用域之间的关系。他们非常相似,只是一个是全局,一个是局部。
  2. 同时script作用域的特性和块作用域一样,如果内部没有let和const定义,作用域便不会被创建出来。
    debugger
    var a = 'window a'

    debugger
    const b = 'script b'
    debugger
    {
      const c = 'Block c'
      debugger
    }
    function Fun() {
      const a = 'Fun a'
      var b = 'Fun b'
      debugger
    }
    Fun()

感兴趣的可以自行去浏览器控制台的源代码那块点作用域去测试。

YueHua46 avatar Dec 11 '21 06:12 YueHua46

let const class 不会挂到全局作用域,而是在 script 作用域 let const 不可以重复声明

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/let

Yangfan2016 avatar Aug 04 '22 09:08 Yangfan2016

我同时还想补充一点的就是如果在声明之前调用let const 声明的变量的时候会报错(这里面有一个暂时性死区的问题)不能再声明之前调用 例如:

console.log(a); //Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 1;

因为let和const没有变量提升呀

codeMonsterWWL avatar Aug 09 '22 04:08 codeMonsterWWL

与之相比var let const三种,前者因为var的变量会提升到window,但是let个const不会,let ,const会生成块作用域,同一作用域下let和const不能声明同名变量,而var可以 跟提升没有关系。

yinzuowen avatar May 05 '23 08:05 yinzuowen