2.作用域
2021-08-13 10:04:42 7 举报
AI智能生成
js作用域,ao、vo,this指向
作者其他创作
大纲/内容
动态和静态作用域
静态作用域
js是静态
词法作用域
函数作用域在定义的位置确定
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
foo定义在全局作用域,value局部找不到就往作用域上级全局找,是1
动态作用域
函数作用域在调用时确定
执行上下文栈
可执行代码
全局代码
globalContext
初始化入上下文栈
应用程序结束出栈
函数代码
执行函数创建函数上下文加入执行栈
执行完毕出栈
eval代码
eval和with
eval
function foo (str, a) {
eval(str)
console.log(a, b)
}
b = 2
foo('var b = 3;', 1)
正常来说,console.log(a, b)的时候,foo作用域中,是没有b的。会查找到全局作用域
但是eval('var b = 3')被执行。foo在运行的时候,eval对作用域的环境进行了修改
输出:1, 3
with
var obj = {
a: 1,
b: 2,
c: 3
}
with(obj){
a: 2,
b: 3,
c: 4
}
console.log(obj)
通过with可以做到不需要重复的引用对象本身
输出{a"2, b:3, c:4}
with 可以将一个没有或者有多个属性的对象处理称为一个完全隔离的词法作用域,这个对象的属性会被定义称为在这个作用域中的词法标识符
with语法是根据你传递的对象,创建另一个全新的词法作用域
欺骗词法
执行顺序
进入上下文,再执行代码
变量对象(Variable object), VO
全局上下文
函数上下文
活动对象(activation object, AO)
进入函数时创建
通过函数的arguments初始化
进入执行上下文时,代码未执行
AO = {}
arguments初始化
AO.arguments = Arguments对象
形参
有实参,AO[形参key] = 形参value
无实参,属性值为undefined
函数声明
AO[函数名] = 该函数体
变量对象已经存在相同名称的属性,则完全替换这个属性
所以函数声明优于变量声明
变量声明
必须以var声明
AO已有同名属性,忽略
AO无同名属性,AO[变量名] = undefined
所以函数表达式不会提升
示例
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: reference to function c(){},
d: undefined
}
代码执行阶段
作用域链(Scope chain)
找寻变量
通过上下文的VO找变量
当前VO,找不到遍历父级VO,找不到报错
函数有一个内部属性 [[scope]]
function foo() {
function bar() {
...
}
}
foo.[[scope]] = [
globalContext.VO
];
bar.[[scope]] = [
fooContext.AO,
globalContext.VO
];
this
指向的是那个调用它的对象
new
无return
指向new出来的实例
return
对象
指向该对象
非对象
指向new出来的实例
LHS和RHS
RHS
RHS查询,找到变量,但是进行不合理的应用的时候,会抛出异常TypeError
RHS查询,如果找不到变量,会抛出异常ReferenceError
LHS
LHS查询,如果查询到全局作用域,都找不到变量,会很热心的声明一个变量,并返还给引擎
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
执行上下文
globalContext入栈
checkscope functionContext入栈
checkscope functionContext出栈
f functionContext入栈
f functionContext出栈
globalContext出栈
执行上下文
globalContext入栈
checkscope functionContext入栈
f functionContext入栈
f functionContext出栈
checkscope functionContext出栈
以下面的例子为例,结合着之前讲的变量对象和执行上下文栈,我们来总结一下函数执行上下文中作用域链和变量对象的创建过程:
var scope = "global scope";
function checkscope(){
var scope2 = 'local scope';
return scope2;
}
checkscope();
执行过程如下:
1.checkscope 函数被创建,保存作用域链到 内部属性[[scope]]
checkscope.[[scope]] = [
globalContext.VO
];
2.执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 函数执行上下文被压入执行上下文栈
ECStack = [
checkscopeContext,
globalContext
];
3.checkscope 函数并不立刻执行,开始做准备工作,第一步:复制函数[[scope]]属性创建作用域链
checkscopeContext = {
Scope: checkscope.[[scope]],
}
4.第二步:用 arguments 创建活动对象,随后初始化活动对象,加入形参、函数声明、变量声明
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
},
Scope: checkscope.[[scope]],
}
5.第三步:将活动对象压入 checkscope 作用域链顶端
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
},
Scope: [AO, [[Scope]]]
}
6.准备工作做完,开始执行函数,随着函数的执行,修改 AO 的属性值
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: 'local scope'
},
Scope: [AO, [[Scope]]]
}
7.查找到 scope2 的值,返回后函数执行完毕,函数上下文从执行上下文栈中弹出
ECStack = [
globalContext
];
0 条评论
下一页