let/const <-> 作用域类型
- 函数作用域内,提前使用了let定义的变量,报:Uncaught ReferenceError: Cannot access ‘a’ before initialization。因为变量有提升,但是没初始化填充
- 全局作用域内,提前使用let定义的变量,报:Uncaught ReferenceError: ttt is not defined。没有变量提升。
d8打印一下作用域信息:


可以看到函数作用域内,对于let/const的变量说:尚未分配,洞初始化操作被忽略/跳过
而全局作用域下直接先在 context[index] 中占位,但是此时。。i dont know。总之没有提升变量声明。
在类作用域下同函数作用域下报相同错误,其实很正常,因为js本质上没有类,class只是函数的一种语法糖
惰性编译与预扫描

虽然惰性编译函数作用域,但在编译全局作用域之前,会先对顶级函数作用域进行 预扫描。
eval
chromium源码中对eval作用域做了大量的处理,我看到其中一点是:
eval的作用域就是 最近的非eval的父级作用域下,但是非严格模式下,如果在eval中直接出现的变量(没用var/let/const声明),没有在其上层作用域中出现过, 则会透传到window下,造成内存泄漏。
关于各种eval嵌套(配合block/function/eval/catch scope),做了大量处理,看的我💔,后来都不知道读了啥
忽略/报错重复的声明
function a(e) {console.log(e===arguments[0], e, a);var e = 3;//这个声明合法,但是会被v8忽略再次声明,因为你在function scope里跟参数e重复了//let e = 2;//直接报错,v8不让let,const重复声明; 而且const必须有初始化值var a ;console.log(e, arguments[0]);}a({});
catch scope
这个比较特殊,它的作用域就是 catch后面 圆括号里的范围,难道你没发现:
function test(){var t = 'ok'try {var t = '33';throw 1;} catch (e=t) {//这么写运行时直接报错,Uncaught SyntaxError: Unexpected token '='console.log(e);}}test();
Find variable with (variable->mode() <= |mode_limit|) that was declared in|scope|. This is used to catch patterns like `try{}catch(e){let e;}` andfunction([e]) { let e }, which are errors even though the two 'e's are eachtime declared in different scopes. Returns the first duplicate variablename if there is one, nullptr otherwise.
查找在 |scope| 中声明的具有 (variable->mode() <= |mode_limit|) 的变量。这用于捕获诸如 `try{}catch(e){let e;}` 和 `function([e]) { let e }` 之类的模式,即使这两个 e 每次都在不同的scope内声明,这些模式也是错误的。如果有,则返回第一个重复变量名,否则返回 nullptr。
来个冗杂的示例
不能调试,硬读chromium真的太难了,。。细节上还是没太懂,怎么查找变量插入声明覆盖值的
只能依据实际情况配合那些源码里的名字,望文生义了,淦,以后一定要想办法调试起来。!!
//with内部如果使用了未声明的变量,就会从上层作用域里找,最多找到native scope// 总之在所能找到的最上层scope添加这个变量声明;(前提:所过之处,没有同名词法变量)//如果with的对象内没有该字面量函数,并且该变量没有使用声明类型var/let/const ://则该变量在with-block scope内先插入一个let g声明;//然后再一直向外层scope找g,一直找到最近的终止层scope;期间若遇到var声明同名变量,//则沿用这个var初始声明,并插入var g undefined在终止层scope。//最后,执行到fn g时,就把这个fn重新赋值给 f 里的g//但如果终止层scope内(后代scope也算)有g同名的词法声明(let/const), 则不做任何操作// 即这个函数字面量只能在with内生效let obj = {g: 'ccgg'};function f() {let g = 'what_g'; // prevent sloppy block function hoisting.function m(){let t = 3;console.log(t, g);//3 undefined}m();//undefined, fn m.. 说明with中的g在作用域分析时就已经被insert了var声明console.log(g, m);// var g = 3;let aa = 'old aa';{console.log(g, aa);//undefined 77//到这里aa一定是77//注意:with和eval的上下文、scope可以是动态的查找// var g = 3;let f = 'f99';//这么理解:obj中的变量默认在一个with作用域内,obj内变量假设初始是var类型with(obj) {console.log('with内', g);//ccggaa = 'new aa';//下述前提:f函数内没有g的同名词法声明(即无 let/const g)//如果obj中有同名变量,此处又用了var g,则依然会将g的var声明插入到上面的f内//并给予undefined初始值。然后执行到下面这行后,就把这个函数g给了obj.g//但是不会赋值给f里的g为函数!它仍为undefinedfunction g() {try { throw 0; }catch { eval(`console.log('f-block-with-g-eval', f, g)`); }console.log('with-g内的aa', aa);return 'g();返回g';}console.log('执行: ', g());}console.log('with外面1 aa', aa, g);//从这层scope往上找g}console.log('with外面2 aa', aa, g);}f();console.log(obj.g);//ccgg// console.log(aa, g);//这俩都会报错的,未定义,因为f内部我有对应的词法声明
