searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

js中的变量对象/活动对象与作用域链

2023-06-16 05:41:33
21
0

 

什么是AO和VO

  • AO:Activive Object,即函数的活动对象。
  • VO:Variable Object,即变量对象。

 

AO和VO的关系

AO可以理解为VO的一个实例,也就是VO是一个构造函数,然后VO(Context) === AO,所以VO提供的是一个函数中所有变量数据的模板。

对于同一个函数分多次执行,那么里面的变量、形参和定义的函数肯定是不同的函数,所以每次执行都会产生一个AO对象,即VO是AO的一个实例,但是这个实例并不是new 出来的,而是在同一段执行代码执行的时候放进来的。

VO是不能访问的(除了全局上下文的VO可以间接访问),但是可以访问AO的成员(属性)。
VO和AO其实是一个东西,只是处于不同的执行上下文生命周期。AO存在于执行上下文位于执行上下文堆栈顶部(就是上边说的’当控制进入函数代码的执行上下文时’)的时期。再粗暴点,就是函数调用时,VO被激活成了AO。
AO通过函数的arguments属性初始化,其值是一个ArgO,包括 callee、length、arg属性。其中arg属性就相当于下标,比如第一个参数对应arg = 0。

 

执行上下文

当执行js代码时,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。

执行上下文的代码会分成两个阶段进行处理:创建和执行

  • 创建阶段(当函数被调用,但是开始执行函数内部代码之前,也就是所谓的 预编译/词法分析阶段)
    • 创建Scope chain
    • 创建VO/AO(variables, functions and arguments)
    • 设置this
  • 激活/代码执行阶段
    • 设置变量的值、函数的引用,然后解释/执行代码

注意,在执行代码阶段,如果存在对于 VO/AO 内不存在的变量进行赋值,则该变量会被创建到global,并且这种创建变量方式给global一个属性,而不会在VO中,比如

 

(function(){
	testProp = '123'  // 这个函数执行之后,可以从window.testProp访问到
})()


// 执行上下文可以理解为一个对象,型如:

executionContext:{
    //这就是变量对象VO VO = 变量 + 函数声明 + AO(其实就是函数执行时候的实参)
    variable object:vars,functions,arguments, 
    //作用域链 = VO + 所有父级的作用域
    scope chain: variableObject + all parents scopes
    // 上下文对象,就是this指针,这个也是在进入上下文的时候进行一次性的确定
    thisValue: contextObject
}

this指向

这里要注意的是,this永远代表方法所属的对象,this是在进入上下文的时候进行确定的,之后不会改变,如果函数在执行过程中没有被关联到任何对象,那么 this 指向全局对象(这里和将变量创建到全局有些类似),简而言之,所有的匿名函数,this 都指向全局。

this的值 contextObject 是直接从执行上下文中获取的,不会进行任何任何作用域链查找,而是在进入上下文的时候一次性确定。

var context = "global";

var obj = {  
    context: "object",
    method: function () {  
        console.log(this + ":" +this.context);
        
        function f() {
            var context = "function";
            console.log(this + ":" +this.context); 
        };
        f(); 
        
        (function(){
            var context = "function";
            console.log(this + ":" +this.context); 
        })();
    }
};

obj.method();
// [object Object]:object
// [object Window]:global
// [object Window]:global

 

变量对象(Variable Object,VO)

变量对象是与执行上下文相关的数据作用域。它是一个与上下文相关的特殊对象,一般VO中会包含以下信息:

  • 变量声明 (var, Variable Declaration);
  • 函数声明 (Function Declaration, FD);
  • 函数的形参

注意,这里的 Declaration 是声明,也就是javascript中有两种声明方式

变量对象在函数创建阶段大致如下

  • 创建 arguments 对象,检查当前环境的参数,初始化属性和属性值。
  • 检查函数声明,当前环境中每发现一个函数就在 VO 中用函数名创建一个属性,以此来引用函数。如果函数名已经存在,就覆盖这个属性。
  • 检查变量声明,当前环境中每发现一个变量就在 VO 中用变量名创建一个属性,并初始化其值为 undefined。如果变量名存在,则跳过(注意这是在创建阶段,执行阶段会被赋值,也就是覆盖),继续检查。

看下面的代码执行过程的对比

console.log(hello); // [Function: hello]
function hello() { console.log('how are u') }
var hello = 10;
  • 首先进入全局环境创建阶段
  • 检查函数声明,将函数 hello 放入变量对象(全局环境为 window 对象)。
  • 检查变量声明,发现变量 hello 已经存在,所以跳过。(注意了这里是变量 var hello,如果是函数function hello(){} 则会进行覆盖)
  • 进入执行阶段,执行代码 console.log(hello) 时,会在全局环境的变量对象中寻找 hello,找到了函数 hello
  • 执行到赋值语句,hello 被赋值为字符串变量
console.log(hello); // 打印undefined
var hello = 10;
hello = 'i am hello'
  • 这里之所以打印的是undefined而不是 'i am hello',就是因为在创建过程中,hello的变量名已经存在,而 'I am hello' 又是个字符串而不是函数,所以是跳过而不是覆盖VO中的hello

 

活动对象(Activation Object,AO)

只有全局上下文的变量对象允许通过VO的属性名称间接访问;在函数执行上下文中,VO是不能直接访问的,此时由激活对象(Activation Object,缩写为AO)扮演VO的角色。AO 是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。

对于VOAO的关系可以理解为,VO 在不同的 Execution Context 中会有不同的表现:当在Global Execution Context中,可以直接使用VO;但是,在函数Execution Context中,AO就会被创建

作用域链

var glb = 'global'

var parentFunc = function(parentNum) {
	var b = 5

	function childFunc(childNum) {
		var a = 2
		return a + b + childNum
	}

	let c = childFunc(4)

	return b + c + parentNum
}

parentFunc(1)
 
  1. 执行流开始,代码进入 globalExecutionContext,此时创建Global VO

    //这里代表执行上下文栈(Execution context stack)
    
    // 全局上下文
    globalExecutionContext = {
    	scopeChain: {...},
    	variableObject: {
    		glb: 'global',
    		parentFunc: undefined,
    		...
    	}
    }
  2. 当代码执行到 parentFunc(1) 时,代码进入 parentFuncExecutionContextparentFunc 维护一个私有属性[[scope]] ,用来保存 scopeChain。在 parentFunc 被定义的时候,只有一个元素,在这里只有一个就是VO(global)。当 parentFunc 被执行的时候,parentFunc 函数的 AO 通过 arguments 进行初始化,然后形参也被加入到AO中,同时AO(parentFunc) 被添加到scopeChain 的顶部。

    //这里代表执行上下文栈(Execution context stack)
    
    // 全局上下文
    globalExecutionContext = {
    	scopeChain: {...},
    	variableObject: {
    		glb: 'global',
    		parentFunc: undefined,
    		childFunc: pointer to function childFunc,
    		...
    	}
    }
    
    // parentFunc的执行上下文
    parentFunc.[[scope]] = parentFuncExecutionContext.scopeChain
    // 执行后
    parentFuncExecutionContext = {
    	scopeChain: [AO(parentFunc),VO(global)],
    	variableObject: {
    		arguments:{
    			0: 1,
    			length: 1,
    			callee: pointer to function parentFunc
    		},
    		parentNum: 1
    		b: '5',
    		c: undefined  // 虽然实际无法打印出undefined,但是在VO中这个变量是存在的 
    	}
    }
  3. parentFunc 被执行,当执行到 childFunc 时,代码进入 childFuncExecutionContextchildFunc 维护一个私有属性[[scope]],用来保存 scopeChain。在 childFunc 被定义的时候(这里注意 parentFunc 被执行的时候 childFunc 才被定义),有两个元素,在这就是VO(global)AO(parentFunc)。当 childFunc 被执行的时候,childFunc 函数的 AO 通过 arguments 进行初始化,然后形参也被加入到AO中,同时AO(childFunc) 被添加到scopeChain 的顶部。

    //这里代表执行上下文栈(Execution context stack)
    
    // 全局上下文
    globalExecutionContext = {
    	scopeChain: {...},
    	variableObject: {
    		glb: 'global',
    		parentFunc: undefined,
    		childFunc: pointer to function childFunc,
    		...
    	}
    }
    
    // parentFunc的执行上下文
    parentFunc.[[scope]] = parentFuncExecutionContext.scopeChain
    // 执行后
    parentFuncExecutionContext = {
    	scopeChain: [AO(parentFunc),VO(global)],
    	variableObject: {
    		arguments:{
    			0: 1,
    			length: 1,
    			callee: pointer to function parentFunc
    		},
    		parentNum: 1
    		b: '5',
    		c: undefined,
    		childFunc: pointer to function childFunc
    	}
    }
    
    // childFunc的执行上下文
    childFunc.[[scope]] = childFuncExecutionContext.scopeChain
    // 执行后
    childFuncExecutionContext = {
    	scopeChain: [AO(childFunc), AO(parentFunc), VO(global)],
    	variableObject: {
    		arguments:{
    			0: 4,
    			length: 1,
    			callee: pointer to function childFunc
    		},
    		childNum: 4
    		a: 2
    	}
    }
  4. childFunc 执行完毕,返回值后退出执行上下文栈

  5. parentFunc 执行完毕,返回值后退出执行上下文栈

创建作用域

对比下面的两个输出结果,其实就是通过函数执行阶段的AO,通过函数包裹,在f(i) 执行时,函数的执行上下文的创建阶段(词法分析阶段),i 就已经确定了,也没有在后续的作用域内发生改变,这里的使用方法类似于闭包

for (var i = 0; i < 10; i++) {
  setTimeout(function () {
    console.log(i)
  }, 100 * i)
}
// 输出10, 10, 10, 10...

for (var i = 0; i < 10; i++) {
  f(i)
}

function f (i) {
  setTimeout(function () {
    console.log(i)
  }, 100)
}
// 输出1, 2, 3, 4...

关于块级作用域

JS有:全局作用域、函数作用域,对于一个var变量,只能存在全局作用域或函数作用域中的一个。而ES6中新增了块级作用域letconst

这里的块指的是代码块,块级作用域由 {} 包裹,if 语句和 for 语句里面的 {} 也属于块作用域。

for(var i=0;i<10;i++){
    setTimeout(function(){
       console.log(i)
    }, 100)
}
// 10,10,10,10...

由于使用 var 变量的情况下不存在块级作用域,所以对应的变量i是创建到全局的,所以循环结束后去全局变量i=10

for(let i=0;i<10;i++){
    setTimeout(function(){
       console.log(i)
    }, 100)
}
// 1,2,3,4,5...

使用let后实际是创建了10个{} 代码块和10个i值,所以最终函数执行时获取的是对应的{}内部的i

作用域链和原型链的关系

作用域链(scop chain)的主要作用是用来进行变量查找,他和原型链(prototype chain)的关系相当于一个二维查找,当代码需要查找一个属性(property)或者描述符(identifier)的时候,首先会通过作用域链(scope chain)来查找相关的对象;一旦对象被找到,就会根据对象的原型链(prototype chain)来查找属性(property)

延长作用域链

在 js 中,某些语句可以在作用域链前端临时添加一个变量对象,该变量对象会在代码执行完毕后移除。具体来说当执行流进入到下列两种语句时,作用域就会得到加长:

  • try-catch 语句的 catch

    在 catch 块中,错误对象 e 被添加到了其作用域链前端,这使得在 catch 块内部能够访问到错误对象。执行完后,catch 块内部的变量对象被销毁,因此在 catch 块外部就不能访问到错误对象 e 了

    var test = () => {
      try {
        throw Error("出错误了");
      } catch(e) {
        console.log(e);  //Error: 出错误了
      }
      console.log(e);  //Uncaught ReferenceError: e is not defined
    }
    test();
  • with(obj)语句

    将 obj 对象加入到作用域链前端。语句 with(persion) 将对象 persion 作为 VO 添加到了函数 getName 作用域链的前端,语句var myName = name在查找变量 name 时 会首先在其作用域链前端,即 person 对象中查找,查找到 name 属性为 snow。又因为 with 语句内的 VO 是只读的,在本层定义的变量,不能存储到本层,而是存储到它的上一层作用域。这样在函数 getName 的作用域内就能访问到变量 myName

    var persion = { name: 'snow' };
    var name = 'summer';
    var getName = () => {
      with(persion) {
        var myName = name;
      }
      return myName;
    }
    console.log(getName())  // 打印 => snow

总结

  • 全局环境没有 arguments 对象。
  • 我们编写代码时并不能访问函数的变量对象,但解释器在处理数据使其成为活动对象时就可以使用它。
  • 作用域链的搜索始终是从作用域链的前端开始,然后逐级的向后回溯,直到全局环境,不能反向搜索
0条评论
作者已关闭评论
陈****东
3文章数
0粉丝数
陈****东
3 文章 | 0 粉丝
陈****东
3文章数
0粉丝数
陈****东
3 文章 | 0 粉丝
原创

js中的变量对象/活动对象与作用域链

2023-06-16 05:41:33
21
0

 

什么是AO和VO

  • AO:Activive Object,即函数的活动对象。
  • VO:Variable Object,即变量对象。

 

AO和VO的关系

AO可以理解为VO的一个实例,也就是VO是一个构造函数,然后VO(Context) === AO,所以VO提供的是一个函数中所有变量数据的模板。

对于同一个函数分多次执行,那么里面的变量、形参和定义的函数肯定是不同的函数,所以每次执行都会产生一个AO对象,即VO是AO的一个实例,但是这个实例并不是new 出来的,而是在同一段执行代码执行的时候放进来的。

VO是不能访问的(除了全局上下文的VO可以间接访问),但是可以访问AO的成员(属性)。
VO和AO其实是一个东西,只是处于不同的执行上下文生命周期。AO存在于执行上下文位于执行上下文堆栈顶部(就是上边说的’当控制进入函数代码的执行上下文时’)的时期。再粗暴点,就是函数调用时,VO被激活成了AO。
AO通过函数的arguments属性初始化,其值是一个ArgO,包括 callee、length、arg属性。其中arg属性就相当于下标,比如第一个参数对应arg = 0。

 

执行上下文

当执行js代码时,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。

执行上下文的代码会分成两个阶段进行处理:创建和执行

  • 创建阶段(当函数被调用,但是开始执行函数内部代码之前,也就是所谓的 预编译/词法分析阶段)
    • 创建Scope chain
    • 创建VO/AO(variables, functions and arguments)
    • 设置this
  • 激活/代码执行阶段
    • 设置变量的值、函数的引用,然后解释/执行代码

注意,在执行代码阶段,如果存在对于 VO/AO 内不存在的变量进行赋值,则该变量会被创建到global,并且这种创建变量方式给global一个属性,而不会在VO中,比如

 

(function(){
	testProp = '123'  // 这个函数执行之后,可以从window.testProp访问到
})()


// 执行上下文可以理解为一个对象,型如:

executionContext:{
    //这就是变量对象VO VO = 变量 + 函数声明 + AO(其实就是函数执行时候的实参)
    variable object:vars,functions,arguments, 
    //作用域链 = VO + 所有父级的作用域
    scope chain: variableObject + all parents scopes
    // 上下文对象,就是this指针,这个也是在进入上下文的时候进行一次性的确定
    thisValue: contextObject
}

this指向

这里要注意的是,this永远代表方法所属的对象,this是在进入上下文的时候进行确定的,之后不会改变,如果函数在执行过程中没有被关联到任何对象,那么 this 指向全局对象(这里和将变量创建到全局有些类似),简而言之,所有的匿名函数,this 都指向全局。

this的值 contextObject 是直接从执行上下文中获取的,不会进行任何任何作用域链查找,而是在进入上下文的时候一次性确定。

var context = "global";

var obj = {  
    context: "object",
    method: function () {  
        console.log(this + ":" +this.context);
        
        function f() {
            var context = "function";
            console.log(this + ":" +this.context); 
        };
        f(); 
        
        (function(){
            var context = "function";
            console.log(this + ":" +this.context); 
        })();
    }
};

obj.method();
// [object Object]:object
// [object Window]:global
// [object Window]:global

 

变量对象(Variable Object,VO)

变量对象是与执行上下文相关的数据作用域。它是一个与上下文相关的特殊对象,一般VO中会包含以下信息:

  • 变量声明 (var, Variable Declaration);
  • 函数声明 (Function Declaration, FD);
  • 函数的形参

注意,这里的 Declaration 是声明,也就是javascript中有两种声明方式

变量对象在函数创建阶段大致如下

  • 创建 arguments 对象,检查当前环境的参数,初始化属性和属性值。
  • 检查函数声明,当前环境中每发现一个函数就在 VO 中用函数名创建一个属性,以此来引用函数。如果函数名已经存在,就覆盖这个属性。
  • 检查变量声明,当前环境中每发现一个变量就在 VO 中用变量名创建一个属性,并初始化其值为 undefined。如果变量名存在,则跳过(注意这是在创建阶段,执行阶段会被赋值,也就是覆盖),继续检查。

看下面的代码执行过程的对比

console.log(hello); // [Function: hello]
function hello() { console.log('how are u') }
var hello = 10;
  • 首先进入全局环境创建阶段
  • 检查函数声明,将函数 hello 放入变量对象(全局环境为 window 对象)。
  • 检查变量声明,发现变量 hello 已经存在,所以跳过。(注意了这里是变量 var hello,如果是函数function hello(){} 则会进行覆盖)
  • 进入执行阶段,执行代码 console.log(hello) 时,会在全局环境的变量对象中寻找 hello,找到了函数 hello
  • 执行到赋值语句,hello 被赋值为字符串变量
console.log(hello); // 打印undefined
var hello = 10;
hello = 'i am hello'
  • 这里之所以打印的是undefined而不是 'i am hello',就是因为在创建过程中,hello的变量名已经存在,而 'I am hello' 又是个字符串而不是函数,所以是跳过而不是覆盖VO中的hello

 

活动对象(Activation Object,AO)

只有全局上下文的变量对象允许通过VO的属性名称间接访问;在函数执行上下文中,VO是不能直接访问的,此时由激活对象(Activation Object,缩写为AO)扮演VO的角色。AO 是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。

对于VOAO的关系可以理解为,VO 在不同的 Execution Context 中会有不同的表现:当在Global Execution Context中,可以直接使用VO;但是,在函数Execution Context中,AO就会被创建

作用域链

var glb = 'global'

var parentFunc = function(parentNum) {
	var b = 5

	function childFunc(childNum) {
		var a = 2
		return a + b + childNum
	}

	let c = childFunc(4)

	return b + c + parentNum
}

parentFunc(1)
 
  1. 执行流开始,代码进入 globalExecutionContext,此时创建Global VO

    //这里代表执行上下文栈(Execution context stack)
    
    // 全局上下文
    globalExecutionContext = {
    	scopeChain: {...},
    	variableObject: {
    		glb: 'global',
    		parentFunc: undefined,
    		...
    	}
    }
  2. 当代码执行到 parentFunc(1) 时,代码进入 parentFuncExecutionContextparentFunc 维护一个私有属性[[scope]] ,用来保存 scopeChain。在 parentFunc 被定义的时候,只有一个元素,在这里只有一个就是VO(global)。当 parentFunc 被执行的时候,parentFunc 函数的 AO 通过 arguments 进行初始化,然后形参也被加入到AO中,同时AO(parentFunc) 被添加到scopeChain 的顶部。

    //这里代表执行上下文栈(Execution context stack)
    
    // 全局上下文
    globalExecutionContext = {
    	scopeChain: {...},
    	variableObject: {
    		glb: 'global',
    		parentFunc: undefined,
    		childFunc: pointer to function childFunc,
    		...
    	}
    }
    
    // parentFunc的执行上下文
    parentFunc.[[scope]] = parentFuncExecutionContext.scopeChain
    // 执行后
    parentFuncExecutionContext = {
    	scopeChain: [AO(parentFunc),VO(global)],
    	variableObject: {
    		arguments:{
    			0: 1,
    			length: 1,
    			callee: pointer to function parentFunc
    		},
    		parentNum: 1
    		b: '5',
    		c: undefined  // 虽然实际无法打印出undefined,但是在VO中这个变量是存在的 
    	}
    }
  3. parentFunc 被执行,当执行到 childFunc 时,代码进入 childFuncExecutionContextchildFunc 维护一个私有属性[[scope]],用来保存 scopeChain。在 childFunc 被定义的时候(这里注意 parentFunc 被执行的时候 childFunc 才被定义),有两个元素,在这就是VO(global)AO(parentFunc)。当 childFunc 被执行的时候,childFunc 函数的 AO 通过 arguments 进行初始化,然后形参也被加入到AO中,同时AO(childFunc) 被添加到scopeChain 的顶部。

    //这里代表执行上下文栈(Execution context stack)
    
    // 全局上下文
    globalExecutionContext = {
    	scopeChain: {...},
    	variableObject: {
    		glb: 'global',
    		parentFunc: undefined,
    		childFunc: pointer to function childFunc,
    		...
    	}
    }
    
    // parentFunc的执行上下文
    parentFunc.[[scope]] = parentFuncExecutionContext.scopeChain
    // 执行后
    parentFuncExecutionContext = {
    	scopeChain: [AO(parentFunc),VO(global)],
    	variableObject: {
    		arguments:{
    			0: 1,
    			length: 1,
    			callee: pointer to function parentFunc
    		},
    		parentNum: 1
    		b: '5',
    		c: undefined,
    		childFunc: pointer to function childFunc
    	}
    }
    
    // childFunc的执行上下文
    childFunc.[[scope]] = childFuncExecutionContext.scopeChain
    // 执行后
    childFuncExecutionContext = {
    	scopeChain: [AO(childFunc), AO(parentFunc), VO(global)],
    	variableObject: {
    		arguments:{
    			0: 4,
    			length: 1,
    			callee: pointer to function childFunc
    		},
    		childNum: 4
    		a: 2
    	}
    }
  4. childFunc 执行完毕,返回值后退出执行上下文栈

  5. parentFunc 执行完毕,返回值后退出执行上下文栈

创建作用域

对比下面的两个输出结果,其实就是通过函数执行阶段的AO,通过函数包裹,在f(i) 执行时,函数的执行上下文的创建阶段(词法分析阶段),i 就已经确定了,也没有在后续的作用域内发生改变,这里的使用方法类似于闭包

for (var i = 0; i < 10; i++) {
  setTimeout(function () {
    console.log(i)
  }, 100 * i)
}
// 输出10, 10, 10, 10...

for (var i = 0; i < 10; i++) {
  f(i)
}

function f (i) {
  setTimeout(function () {
    console.log(i)
  }, 100)
}
// 输出1, 2, 3, 4...

关于块级作用域

JS有:全局作用域、函数作用域,对于一个var变量,只能存在全局作用域或函数作用域中的一个。而ES6中新增了块级作用域letconst

这里的块指的是代码块,块级作用域由 {} 包裹,if 语句和 for 语句里面的 {} 也属于块作用域。

for(var i=0;i<10;i++){
    setTimeout(function(){
       console.log(i)
    }, 100)
}
// 10,10,10,10...

由于使用 var 变量的情况下不存在块级作用域,所以对应的变量i是创建到全局的,所以循环结束后去全局变量i=10

for(let i=0;i<10;i++){
    setTimeout(function(){
       console.log(i)
    }, 100)
}
// 1,2,3,4,5...

使用let后实际是创建了10个{} 代码块和10个i值,所以最终函数执行时获取的是对应的{}内部的i

作用域链和原型链的关系

作用域链(scop chain)的主要作用是用来进行变量查找,他和原型链(prototype chain)的关系相当于一个二维查找,当代码需要查找一个属性(property)或者描述符(identifier)的时候,首先会通过作用域链(scope chain)来查找相关的对象;一旦对象被找到,就会根据对象的原型链(prototype chain)来查找属性(property)

延长作用域链

在 js 中,某些语句可以在作用域链前端临时添加一个变量对象,该变量对象会在代码执行完毕后移除。具体来说当执行流进入到下列两种语句时,作用域就会得到加长:

  • try-catch 语句的 catch

    在 catch 块中,错误对象 e 被添加到了其作用域链前端,这使得在 catch 块内部能够访问到错误对象。执行完后,catch 块内部的变量对象被销毁,因此在 catch 块外部就不能访问到错误对象 e 了

    var test = () => {
      try {
        throw Error("出错误了");
      } catch(e) {
        console.log(e);  //Error: 出错误了
      }
      console.log(e);  //Uncaught ReferenceError: e is not defined
    }
    test();
  • with(obj)语句

    将 obj 对象加入到作用域链前端。语句 with(persion) 将对象 persion 作为 VO 添加到了函数 getName 作用域链的前端,语句var myName = name在查找变量 name 时 会首先在其作用域链前端,即 person 对象中查找,查找到 name 属性为 snow。又因为 with 语句内的 VO 是只读的,在本层定义的变量,不能存储到本层,而是存储到它的上一层作用域。这样在函数 getName 的作用域内就能访问到变量 myName

    var persion = { name: 'snow' };
    var name = 'summer';
    var getName = () => {
      with(persion) {
        var myName = name;
      }
      return myName;
    }
    console.log(getName())  // 打印 => snow

总结

  • 全局环境没有 arguments 对象。
  • 我们编写代码时并不能访问函数的变量对象,但解释器在处理数据使其成为活动对象时就可以使用它。
  • 作用域链的搜索始终是从作用域链的前端开始,然后逐级的向后回溯,直到全局环境,不能反向搜索
文章来自个人专栏
javascript原理
1 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0