Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
funlee opened this issue Mar 6, 2019 · 19 comments
Open

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

funlee opened this issue Mar 6, 2019 · 19 comments
Labels

Comments

@funlee
Copy link

funlee commented Mar 6, 2019

在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
@gaomin
Copy link

gaomin commented Mar 6, 2019

qq20190306-113547

@henry-fun
Copy link

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 = { },如果上面的代码在"{ }"前面放上一个等号就会是当作对象词法解析了。

@windluo
Copy link

windluo commented Mar 6, 2019

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

@jjeejj
Copy link
Contributor

jjeejj commented Mar 8, 2019

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 = { },如果上面的代码在"{ }"前面放上一个等号就会是当作对象词法解析了。

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

@yygmind yygmind changed the title 第27题:关于 const 和 let 声明的变量不在 window 上 第 27 题:关于 const 和 let 声明的变量不在 window 上 Apr 26, 2019
@xxxin128
Copy link

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

@YOMXXX
Copy link

YOMXXX commented Jul 10, 2019

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

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

@lijiayi01
Copy link

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

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

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

@CHristopherkeith
Copy link

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

@l-x-f
Copy link

l-x-f commented Sep 12, 2019

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

@SnailOwO
Copy link

SnailOwO commented Jan 3, 2020

scope作用域链

@XiaoDHuang
Copy link

关于这道题题解,发现答案没有从根本上解释全局环境下声明的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指向问题

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

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

@BruceYuj
Copy link

同意 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 获取。

@fariellany
Copy link

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

@soraly
Copy link

soraly commented Jun 17, 2020

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

@JoMartinezZhu
Copy link

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

@YueHua46
Copy link

YueHua46 commented Dec 11, 2021

我开始以为这个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()

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

@Yangfan2016
Copy link

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

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

@codeMonsterWWL
Copy link

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

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

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

@yinzuowen
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests