当控制进入执行时,执行以下步骤 函数对象F中包含的函数代码的上下文,一个调用者 提供thisArg,并且调用者提供了argumentsList:
- 如果功能代码是严格代码,请将ThisBinding设置为thisArg。
- 否则如果thisArg为null或未定义,则将ThisBinding设置为全局对象。
- 否则如果Type(thisArg)不是Object,则将ThisBinding设置为ToObject(thisArg)。
- 否则将ThisBinding设置为thisArg。
- 让localEnv成为调用NewDeclarativeEnvironment的结果,传递F的[[Scope]]内部属性的值作为 参数。
- 将LexicalEnvironment设置为localEnv。
- 将VariableEnvironment设置为localEnv。
- 让代码为F的[[Code]]内部属性的值。
- 使用功能代码code和argumentsList执行声明绑定实例化,如10.5。
中所述 醇>
请考虑以下代码段:
function foo(){
var a={p:'p'};
o={c:'c'};
}
因此我们有以下内容:
thisArg
为空,因此ThisBinding
设置为全局对象environment record
内部属性所代表的[[Scope]]
。LexicalEnvironment
设置为在步骤5中执行的环境。VariableEnvironment
设置为在步骤5中执行的环境。在步骤8中,在VariableEnvironment中创建绑定,但不在LexicalEnvironment中创建。但在sec 10.3中说
创建执行上下文时,它的LexicalEnvironment和 VariableEnvironment组件最初具有相同的值。
问题:
为什么在创建执行上下文后,LexicalEnvironment和VariableEnvironment在我的情况下仍然相同?
答案 0 :(得分:1)
我不确定我理解你的问题,但这是我理解的第10.4.3节:
步骤1到4 正在处理此值。基本上,在严格模式下,this
将保留为null
或undefined
值,而不是默认为全局对象(如果是浏览器,则为window
)。这包括未通过常规对象或事件处理程序机制调用函数的情况。
步骤5到7 表示每次输入功能时都会创建一个新的命名环境。 它描述了此环境的创建,该环境链接到前一个环境以形成当前名称范围。
对于每个新功能,两个环境共存。
解析名称后,首先搜索词法环境,然后搜索Variable环境。如果两个搜索都失败,则在环境链的上层重复该过程,直到遇到“全部捕获”全局范围。在此范围内,所有标识符都作为全局(window
)对象的属性处理。您可以将其描绘为整个代码包含在with (window)
块中。
词汇环境可以看作是变量范围的临时扩充。
词法和变量环境 功能 相同,直到您使用两个特定语句更改词汇环境:with
或catch
。这并不意味着它们被实现为相同的数据结构。
在实现方面,您可以将词汇环境想象为空列表,将变量环境想象为包含所有局部变量和参数名称的列表。
遇到catch
或with
语句时,将使用新名称填充词法列表,该名称将优先于存储在变量列表中的名称。
catch
只会使其参数可用于名称解析(即允许您引用异常参数)。没什么大不了的,因为新名称和函数参数一样明确。
with
是一个相当危险的野兽。它将创建一个新环境,其中包含作为参数传递的对象的所有属性的名称。范围将包含变量环境链以及这个新的词汇环境。
这里可用于解析的新名称在对象内“隐藏”。
例如:
var a = 'a', b = 'surprise!', o = {a:'a'};
with (o) { a = b; }
console.log (a+" "+b+" "+o.a);
将产生
a surprise! surprise!
a
已解析为o.a
,因为o
包含名为a
的媒体资源。
在 lexical 环境中找不到b
,因此尝试了当前的变量环境并找到变量'b'
。
这是一种非常危险的机制,因为如果您认为对象包含给定属性而实际上没有,则您将引用当前范围之外的变量。
例如,一个简单的拼写错误:
with (element.style) {leftt = '10px';}
会将window.leftt
属性设置为'10px'
,除非您碰巧在当前范围内的某处声明了名为leftt
的变量。
现在,如果你给你的对象属性提供像'i'或'j'这样的愚蠢名字,你可能会在相信你正在设置对象属性的同时在一个范围链上的某个地方乱写一个随机循环索引。
步骤8 描述了在建立功能范围后绑定的参数。基本上,参数与值绑定,并且它们的名称将添加到变量环境中。
保持两个独立环境的重点是提升机制始终使用 变量 环境链作为范围。
这个想法是变量或函数的行为就好像它已经在当前作用域块的顶部声明一样,所以例如在with块中声明的函数不应该解析它的名字,而对象属性是由声明。
坦率地说,这是一个相当有争议的问题,因为ECMA规范不允许在块内部进行函数声明,尽管大多数实现都是如此,但结果会有变化。
现在举个例子:
function foo(){
var a={p:'p'};
o={c:'c'};
}
您的函数不包含任何with
或catch
语句,因此'foo()'中的范围链只是两个变量环境的列表:
global (a bunch of DOM objects all seen as properties of 'window')
function foo (var a)
一旦你打电话给foo(),
a
将作为foo的局部变量解析,将被创建为具有值p
的属性'p'
的对象(并在离开foo时立即收集垃圾( ),除非你设法从持久变量中引用它。)
o
,因此它将被'catch-all'全局范围捕获,因此被解析为window
的(新)属性宾语。它将创建一个window.o.c
属性,其值为'c'
。
这回答了你的问题吗?