Hibop.github.io icon indicating copy to clipboard operation
Hibop.github.io copied to clipboard

关于执行上下文、作用域、变量对象和活动对象

Open Hibop opened this issue 7 years ago • 9 comments

执行上下文(context)和作用域(scope)经常容易混淆,两者有什么区别? 函数执行细节有哪些?

执行上下文(context)和作用域(scope)区别?

作用域是你的代码在运行时,各个变量、函数和对象的可访问性。换句话说,作用域决定了你的代码里的变量和其他资源在各个区域中的可见性。是作用域为你的代码提供了一个安全层级。 在 JavaScript 中有两种作用域 全局作用域 局部作用域 块级声明包括if和switch,以及for和while循环,和函数不同,它们不会创建新的作用域。在块级声明中定义的变量从属于该块所在的作用域。 和var关键字不同,let和const关键字支持在块级声明中创建使用局部作用域。

关于this: 在全局作用域中,上下文总是 Window 对象。 作用域定义在一个对象的方法中,上下文this就是这个方法所在的那个对象。 是如果你使用new关键字调用函数时上下文的值会有差异。上下文会设置为被调用的函数的实例。

函数执行到底发生了哪些事情?

当一个函数被执行时, 会生成一个执行上下文. 一个执行上下文的生命周期分为两个阶段:

  • 创建阶段:
    • 生成变量对象
    • 确定作用域链
    • 确定 this 指向 :我们可以简单的理解 this 为调用函数的对象
  • 执行阶段:
    • 变量赋值
    • 函数引用
    • 执行其它代码 总的流程如下:

--> 入栈(ECStack) --> 创建阶段{生成变量, 作用域链, 明确 this} --> 执行阶段{赋值, 引用, 执行其它代码} --> 出栈(等待销毁)

生成变量——变量对象和活动变量

--> 创建 arguments 对象 --> 检查 function 函数声明 --> 检查 var 变量声明

function test() {
    console.log(foo);
    console.log(bar);

    var foo = 'Hello';
    console.log(foo);
    var bar = function () {
        return 'world';
    }

    function foo() {
        return 'hello';
    }
}

test();

// 解释
function test() {
    function foo() {
        return 'hello';
    }
    var foo; // 已经存在了同名函数 foo, 所以此处跳过, 不会用 undefined 覆盖引用
    var bar;
    console.log(foo);// function foo() { return 'hello'; }
    console.log(bar);// undefined
    foo = 'Hello';
    console.log(foo);// Hello
    bar = function () { // 把匿名函数的引用赋值给 bar
        return 'world';
    }
}

test();

变量提升是怎么实现的: 执行上下文的创建规则, 用代码表示:

// 创建阶段
testEC = {
    // 变量对象
    VO: {},
    scopeChain: {},
    this: {}
}

// VO 为 Variable Object的缩写, 即变量对象
VO = {
    arguments: {...},  //注:在浏览器的展示中, 函数的参数可能并不是放在arguments对象中, 这里为了方便理解, 我做了这样的处理
    foo: <foo reference>  // 表示foo的地址引用
    a: undefined
}

// `scopeChain` 和 `this` 暂时省略
//...


// 执行阶段
VO ->  AO   // Active Object
AO = {
    arguments: {...},
    foo: <foo reference>,
    a: 1
}

再来一个this理解:

var value = 1;

var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}

//示例1
console.log(foo.bar()); // 2
//示例2
console.log((foo.bar)()); // 2
//示例3
console.log((foo.bar = foo.bar)()); // 1
//示例4
console.log((false || foo.bar)()); // 1
//示例5
console.log((foo.bar, foo.bar)()); // 1

总结

变量对象和活动对象其实都是同一个对象, 只是处于执行上下文的不同生命周期. 未进入执行阶段之前, 变量对象中的属性都不能访问! 但是进入执行阶段之后, 变量对象转变为了活动对象, 里面的属性都能被访问了, 然后开始进行执行阶段的操作.

Hibop avatar Jan 14 '18 15:01 Hibop

如何理解 JavaScript 中的 this 关键字? https://www.zhihu.com/question/19636194 这篇文章对函数的物种调用方式和this的指向经行了深刻解读

Hibop avatar Jan 23 '18 12:01 Hibop

词法作用域和静态作用域: https://github.com/mqyqingfeng/Blog/issues/3

Hibop avatar Jan 23 '18 13:01 Hibop

作用域和变量提升的两个题https://segmentfault.com/a/1190000003114255

Hibop avatar Jan 24 '18 11:01 Hibop

为什么要变量提升? http://www.cnblogs.com/liuhe688/p/5891273.html

Hibop avatar Jan 24 '18 11:01 Hibop

js执行过程生命周期:https://wanghan0.github.io/2017/05/05/closure1/

Hibop avatar Jan 30 '18 12:01 Hibop

解析引擎看js解析: http://www.html5jscss.com/js-data-scope.html

Hibop avatar Jan 30 '18 13:01 Hibop

求教console.log(foo);输出function foo() { return 'hello'; }是因为函数的优先级最高的关系吗?

zhoubhin avatar Jan 29 '19 06:01 zhoubhin

请教如何理解console.log((foo.bar = foo.bar)());console.log((foo.bar, foo.bar)());最终输出结果为1?

zhoubhin avatar Jan 29 '19 06:01 zhoubhin