哈Ha!
在
上一篇文章中,我们研究了应用于EcmaScript的OOP的一般理论,以及有关JS中的OOP与古典语言之间的差异的新手开发人员的普遍谬误。
今天,我们将讨论另外两个同样重要的EcmaScript概念,即,实体与执行上下文的关系(此连接)和实体与生成上下文的关系(
ScopeChain )。
因此,让我们开始吧!
这个
在采访中回答以下问题:“告诉我们更多有关
此问题 。”。 通常,新手开发人员会给出非常模糊的答案:“
这是对象”,位于“用于调用方法的位置”之前,“这是调用函数的上下文”,等等。
实际上,这种概念(对于EcmaScript至关重要)的情况要复杂得多。 让我们按顺序弄清楚。
假设我们有一个JavaScript程序,该程序具有全局声明的变量; 全局功能; 局部函数(在其他函数内部声明),从函数返回的函数。
const a = 10; const b = 20; const x = { a: 15, b: 25, } function foo(){ return this.a + this.b; } function bar () { const a = 30; return a + b; } function fooBaz(){ function test () { return this.a + this.b; } return test(); } function fooBar() { const a = 40; const b = 50; return function () { return a + b; } } fooBar()();
将控制权转移到可执行代码时,将在执行上下文中创建一个条目。 可执行代码-这是我们在给定时间执行的任何代码,可以是全局代码或任何函数的代码。
执行上下文是代表和定界代码的抽象。 从这种抽象的角度来看,代码分为全局代码(任何连接的脚本,内联脚本)和函数代码(嵌套函数的代码不属于父函数的上下文)。
第三种类型-EvalCode。 在本文中,我们忽略了它。
从逻辑上讲,执行上下文集是一个按照后进先出(lifo)原理工作的
堆栈 。 堆栈的底部始终是全局上下文,顶部是当前可执行文件。 每次调用函数时,都会在其上下文中创建一个条目。 函数完成后,其上下文结束。 用完的上下文按顺序和相反的顺序从堆栈中删除。
看一下上面的代码。 我们在全局代码中调用了
fooBar函数。 在
fooBar函数中
,我们返回
一个立即调用
的匿名函数 。 堆栈发生以下变化:
全局上下文进入该堆栈-调用
fooBar时
,其上下文进入堆栈
-fooBar上下文结束,返回
一个匿名函数,并从堆栈中删除-调用一个
匿名函数 ,其上下文进入堆栈-
匿名函数实现,返回值并将其上下文从堆栈中删除-在脚本末尾,将从堆栈中删除
全局上下文 。
执行上下文可以有条件地表示为对象。 该对象的属性之一将是词法环境(LO)。
词汇环境包含:
- 所有上下文变量声明
- 所有函数声明
- 函数的所有形式参数(如果我们正在谈论函数的上下文)
输入执行上下文时,解释器将扫描上下文。 所有变量声明和函数声明都将出现在上下文的开头。 创建的变量等于未定义的变量,并且函数完全可以使用。
这也是执行上下文的属性,而不是上下文本身,正如一些新手访调员回答的那样!
这是在进入上下文时定义的,并且在上下文生存期结束之前一直保持不变(直到从堆栈中删除上下文为止)。
在全局执行上下文中,
这由
严格模式决定:当严格模式关闭时,它包含一个全局对象(在浏览器中,它代理到窗口对象的顶层),其中“使用严格”的定义是不确定的。
在函数的上下文中-这个问题要有趣得多!
此函数的功能由调用者确定,并取决于调用的语法。 例如,据我们所知,有一些方法可以让您在调用(
call ,
apply )时很难固定该方法,也有一些方法可以让您使用“已修复此问题”(
bind )创建包装器。 在这些情况下,我们明确声明了这一点,毫无疑问它的定义。
使用正常的函数调用,情况就复杂得多!
EcmaScript内置类型之一
ReferenceType将帮助我们理解函数中如何附加此类型。 这是在实施级别可用的内部类型之一。 从逻辑上讲,它是一个具有两个
基本属性的对象(对某个基本对象的引用,为其返回ReferenceType),
propertyName (该对象标识符的字符串表示形式,其返回了ReferenceType)。
对于所有变量声明,函数声明和属性引用,
都会返回
ReferenceType (从理解这一点的角度来看,这是我们感兴趣的情况)。
为以通常方式调用的函数定义
此规则:
如果ReferenceType位于功能激活括号的左侧,则此ReferenceType的底部将放在this
函数中。 如果方括号左侧是其他类型,则this
可能是全局对象,也可能是undefined
对象(实际上为null
,但是由于从ecmascript的角度来看null没有特定的值,因此将其强制转换为全局对象,对该对象的引用可能是等于undefined
取决于严格模式)。让我们看一个例子:
const x = 0; const obj = { x: 10, foo: function() { return this.x; } } obj.foo();
我认为定义方法很清楚。 现在考虑一些不太明显的情况。
功能表达
让我们回到参考类型。 此类型具有内置的
GetValue方法,该方法返回通过ReferenceType接收到的对象的真实类型。 在表达式区域中,GetValue始终会触发。
一个例子:
(function (){ return this;
这是因为GetValue始终在表达式区域中触发。 GetValue返回一个函数类型,并且在激活括号的左侧不是ReferenceType。 回想一下我们确定
该规则的规则:
如果方括号的左边有任何其他类型,则将全局对象放入this
对象或undefined
(实际上为null
,但是由于从ecmascript的角度来看null没有特定值,因此它将转换为全局对象,取决于严格模式,该链接可以等于undefined) 。
表达式区域是:赋值(=),运算符|| 或其他逻辑运算符,三元运算符,数组初始化程序,逗号分隔列表。
const x = 0; const obj = { x: 10, foo: function() { return this.x; } } obj.foo();
命名函数表达式中的相同情况。 即使递归调用此全局对象或
undefined
此嵌套函数在父级中调用
也是重要的情况!
const x = 0; function foo() { function bar(){ return this.x; } return bar(); } const obj = {x:10}; obj.test = foo; obj.test();
这是因为对
bar()
的调用等效于对
LE_foo.bar
的调用,并且词法环境的对象为此未定义。
构造函数
正如我上面所写:
此函数的功能由调用者确定,并取决于调用的语法。
我们使用new关键字调用构造函数。 此函数激活方法的特殊之处在于,将调用内部函数方法
[[construct]] ,该方法执行某些操作(有关设计人员创建实体的机制,将在OOP的第二篇或第三篇文章中进行讨论!)并调用内部
[[call]]方法,该方法放下在
此创建的构造函数实例中。
范围链
范围链也是这样的执行上下文的属性。 它是当前上下文和所有生成上下文的词法环境的对象列表。 在此链中,解析标识符名称时将进行变量搜索。
注意:这将函数与执行上下文关联,并将ScopeChain与子上下文关联。
规范指出ScopeChain是一个数组:
SC = [LO, LO1, LO2,..., LOglobal];
但是,在某些实现中,例如JS,范围链是通过
链接列表实现的。
为了更好地理解ScopeChain,我们将讨论功能的生命周期。 它分为创建阶段和执行阶段。
创建函数时,将为其分配内部
[[SCOPE]]属性。
在
[[SCOPE]]中 ,记录了较高(生成)上下文的词汇环境的对象的层次链。 该属性将保持不变,直到该函数被垃圾收集器销毁为止。
注意!
[[SCOPE]]与ScopeChain不同,它是函数本身的属性,而不是其上下文。
调用函数时,将初始化并填充其执行上下文。 上下文附加有ScopeChain = LO(函数本身)+ [[SCOPE]](影响上下文的LO层次链)。
标识符名称的
解析-ScopeChain链
中从左到右顺序轮询
LO对象。 输出是ReferenceType,其基本属性指向在其中找到标识符的LO对象,PropertyName将是标识符名称的字符串表示形式。
这就是盖子在引擎盖下的排列方式! 闭包本质上是在ScopeChain中搜索其标识符在函数中存在的所有变量的结果。
const x = 10; function foo () { return x; } (function (){ const x = 20; foo();
以下示例说明了生命周期
[[SCOPE]] 。
function foo () { const x = 10; const y = 20; return function () { return [x,y]; } } const x = 30; const bar = foo();
一个重要的例外是
构造函数 。 对于此类功能,[[SCOPE]]始终指向全局对象。
另外,不要忘记,如果ScopeChain链中的链接之一具有原型,那么搜索也将在原型中进行。
结论
我们将通过论文提出关键思想:
- 这是实体与执行上下文的关系
- ScopeChain是实体与所有生成上下文的关系
- this和ScopeChain是执行上下文属性
- 此功能由调用方确定,并取决于调用的语法
- ScopeChain是当前上下文的词法环境+ [[Scope]]
- [[Scope]]-这是函数本身的属性,包含生成上下文的词汇环境的层次链
希望本文对您有所帮助。 直到以后的文章,朋友们!