[关闭]
@Secretmm 2017-11-11T07:41:16.000000Z 字数 3569 阅读 793

变量、对象、作用域、作用域链

js专题


变量/变量解析

js中的变量可以保存两种不同类型的值:基本类型值、引用类型值;
在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值;


基本类型值

eg:

  1. var a = 20;
  2. var b = a;
  3. b = 30;
  4. console.log(a); //20

图解:
描述


引用类型值

eg:

  1. var m = {
  2. a: 10, b: 20
  3. }
  4. var n = m;
  5. n.a = 15;
  6. console.log(n.a);//15

图解:

引用类型值的比较并非值的比较,即使两个对象包含相同的属性和值,它们也是不相等的;各个索引元素完全相等的两个数组也不相等。

引用类型值的比较是引用的比较,当且仅当它们引用同一个对象时,它们才相等。

eg:

  1. var a = {
  2. name: 'jack',
  3. };
  4. var b = {
  5. name: 'jack',
  6. }
  7. var c = a;
  8. console.log(a === b); //false
  9. console.log(a === c); //true

执行上下文(Execution Context)

当前代码的执行环境,它会形成一个作用域。

js中的运行环境大概包括三种情况:
- 全局环境: js代码运行起来会首先进入该环境
- 函数环境: 当函数被调用执行时,会进入当前函数中执行代码
- eval

在一个js程序中,必定会产生多个执行上下文,js引擎会以栈的方式来处理它们,这个栈被称为函数调用栈。【栈:先进后出】

栈底永远都是全局上下文,栈顶是当前正在执行的上下文

当代码在执行过程中,遇到以上三种情况,都会生成一个执行上下文,放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈

eg1:

  1. var color = 'blue';
  2. function changeColor() {
  3. var anotherColor = 'red';
  4. function swapColors() {
  5. var tempColor = anotherColor;
  6. anotherColor = color;
  7. color = tempColor;
  8. }
  9. swapColors();
  10. }
  11. changeColor();
  1. 全局上下文入栈
  2. 代码执行遇到changeColor(),激活函数changeColor创建他自己的上下文,changeColor执行上下文入栈
  3. changeColor的上下文入栈之后,控制器开始执行其中的可执行代码,遇到swapColors()之后又激活了一个执行上下文。因此第三步是swapColors的执行上下文入栈
  4. 在swapColors的可执行代码中,再没有遇到其他能生成执行上下文的情况,因此这段代码顺利执行完毕,swapColors的上下文从栈中弹出
  5. swapColors的执行上下文弹出之后,继续执行changeColor的可执行代码,也没有再遇到其他执行上下文,顺利执行完毕之后弹出。这样,ECStack中就只身下全局上下文了。
  6. 全局上下文在浏览器窗口关闭后出栈。

图解:

描述

执行上下文总结:

  • 单线程
  • 同步执行,只有栈顶的上下文处于执行中,其他的上下文需等待
  • 全局的上下文只有唯一的一个,它在浏览器关闭时出栈
  • 函数执行上下文的个数没有限制
  • 每次某个函数被调用,就会有新的执行上下文被创建,即使是自身的函数也是如此

eg2:

  1. function f1(){
  2. var n=999;
  3. function f2(){
  4. alert(n);
  5. }
  6. return f2;
  7. }
  8. var result = f1();
  9. result(); // 999

过程:
1. 全局上下文入栈
2. var result = f1()相当于f1()被调用【一个执行过程】,所以f1()入栈;【这里result = f2】
3. return f2后,f1()出栈,
4. 执行到result()时,result入栈;【相当于f2()】
5. alert(n)后,result出栈

图解
描述

练习:

  1. var name = "window";
  2. var p = {
  3. name: 'Perter',
  4. getName: function() {
  5. var self = this;
  6. return function() {
  7. return self.name;
  8. }
  9. }
  10. }
  11. var getName = p.getName();
  12. var _name = getName();
  13. console.log(_name);

恩。过程
1. 全局上下文入栈;
2. 我擦,老子感觉还不会
原文链接


执行上下文生命周期(函数)

当调用以一个函数时,一个新的执行上下文就会被创建。而一个执行上下文的生命周期可以分为两个阶段

图解:


变量对象(Variable Object)

变量对象的创建


  1. 建立arguments对象。检查当前上下文中的参数,建立该对象下的属性与属性值。
  2. 检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。
  3. 检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。

    function声明会比var声明优先级更高


变量提升

通过变量对象的创建过程可以很好的理解变量提升;【先创建后执行】
eg1:

  1. console.log(foo);
  2. function foo() {
  3. return 'girl';
  4. }

结果:

ƒ foo() {
    return 'girl';
}

解析:

  • 首先是创建变量对象:
    foo: <foo reference> // 表示foo的地址引用
  • 其次开始执行代码
    console.log(foo)

eg2:

  1. console.log(foo);//undefined
  2. var foo = 'mark'

解析:

  • 首先是创建变量对象:
    var foo = undefined
  • 其次开始执行代码
    1. console.log(foo);//undefined
    2. foo = 'mark'

eg3

  1. var foo = 20;
  2. function foo() {
  3. console.log('function foo')
  4. }
  5. console.log(foo); // 20

解析:
- 创建变量对象
1. foo: <foo reference> //foo内存地址的引用【function声明优先级大于var】
2. 因为foo的属性已经存在,所以var foo 的变量声明被跳过;
- 执行代码
1. foo = 20
2. console.log(foo)

未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。

变量对象和活动对象有什么区别?

他们其实都是同一个对象,只是处于执行上下文的不同生命周期。不过只有处于函数调用栈栈顶的执行上下文中的变量对象,才会变成活动对象。


作用域链


作用域

变量和函数可访问的范围

作用域分全局作用域、函数作用域、块级作用域(es6)

作用域链

当前环境与上层环境的一系列变量对象组成

保证当前执行环境对符合访问权限的变量和函数的有序访问

eg:

  1. var a = 20;
  2. function test() {
  3. var b = a + 10;
  4. function innerTest() {
  5. var c = 10;
  6. return b + c;
  7. }
  8. return innerTest();
  9. }
  10. test();

在上面的例子中,全局,函数test,函数innerTest的执行上下文先后创建。我们设定他们的变量对象分别为VO(global),VO(test), VO(innerTest)。而innerTest的作用域链,则同时包含了这三个变量对象,所以innerTest的执行上下文可如下表示:

  1. innerTestEC = {
  2. VO: {...}, // 变量对象
  3. scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域链
  4. }

作用域链图解:

作用域链是由一系列变量对象组成,我们可以在这个单向通道中,查询变量对象中的标识符,这样就可以访问到上一层作用域中的变量了。



添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注