JavaScript 执行环境(执行上下文) 变量对象 作用域链 上下文 块级作用域 私有变量和特权方法

在javascript中,可执行的JavaScript代码分三种类型: 

1. Global Code,即全局的、不在任何函数里面的代码,例如:一个js文件、嵌入在HTML页面中的js代码等。
2. Eval Code,即使用eval()函数动态执行的JS代码。
3. Function Code,即用户自定义函数中的函数体JS代码。
不同类型的JavaScript代码具有不同的执行环境,这里我们不考虑evel code,对应于global code和function code存在2种执行环境:全局执行环境和函数执行环境。

一、执行环境(执行上下文)

执行环境(Execution Context,有时简称“环境”)是javascript中最为重要的一个概念。它定义了变量和函数有权访问的其他数据,决定了它们各自的行为。(每个函数都有自己的执行环境。)

全局执行环境:最外围的一个执行环境,根据ECMAScript实现所在的宿主环境不同而不同,在Web浏览器中,全局执行环境被认为是window对象。

执行环境的销毁:在某个执行环境的所有代码都执行完毕后,环境将被销毁,意味着其中所有变量和函数定义也都随之销毁(全局执行环境直到应用程序退出—例如关闭网页或浏览器—时才会被销毁)。

执行流:每个函数都有自己的执行环境;当执行流进入一个函数时,函数的环境就被推入到一个环境栈的顶部并获得控制权。在函数执行完成,栈会将其环境弹出,(即从栈的顶部弹出)再把控制权返还给之前的执行环境。

作用域 上下文 的区别:

从根本上来说,作用域是基于函数的而上下文是基于对象的。

作用域适用于函数被调用时函数中变量的访问权限

上下文通常是指“this”关键字的值,“this”是拥有当前执行代码的对象的引用

二、变量对象

变量对象:每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函保存在这个对象中(我们无法访问,但解析器在处理数据时会在后台使用它。)

三、作用域

作用域链:当代码在环境中执行时,会创建变量对象的一个作用链;作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问。

作用域链的前端:始终都是当前执行代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。

延长作用域链:

虽然执行环境只有两种——全局作用域和函数作用域,但是还是可以通过某种方式来延长作用域链。因为有些语句可以在作用域链的顶部增加一个临时的变量对象。
有两种情况会发生这种现象:
1、try-catch语句的catch块;
2、with语句;
关于作用域:
1. 声明变量
使用var关键字声明变量的时候,变量将被自动添加到距离最近的可用环境中(在函数内部,最近的作用域就是函数的局部变量,在with语句中,最近的环境是函数环境),不使用var声明的情况下,变量会被直接添加到全局环境中。
2. 查询标识符
查询的过程,就是一个向上搜索的过程。当执行js代码的过程中,遇到一个标识符,就会根据标识符的名称,在执行上下文(Execution Context)的作用域链中进行搜索。从作用域链的第一个对象(该函数的Activation Object对象)开始,如果没有找到,就搜索作用域链中的下一个对象,如此往复,直到找到了标识符的定义。如果在搜索完作用域中的最后一个对象,也就是全局对象(Global Object)以后也没有找到,则会抛出一个错误,提示undefined。 

四、块级作用域(通常叫做私有作用域)

五、私有变量和特权方法

【私有变量】 在对象内部使用'var'关键字来声明,而且它只能被私有函数和特权方法访问。 
【私有方法】 在对象的构造函数里声明(或者是通过varfunctionName=function(){...}来定义),
它能被特权方法调用(包括对象的构造方法)和私有方法调用,私有函数只能访问私有的方法和属性。 
【特权方法】通过this.methodName=function(){...}来声明而且可能被对象外部的代码调用。
它可以使用:this.特权函数() 方式来调用特权函数,使用 :私有函数()方式来调用私有函数。

【公共属性】 通过this.variableName来定义而且在对象外部是可以读写的。不能被私有函数所调用。 
【公共方法】 通过ClassName.prototype.methodName=function(){...}来定义可以从对象外部来调用。 
【原型属性】 通过ClassName.prototype.propertyName=someValue 来定义。 
【静态属性】 通过ClassName.propertyName=someValue 来定义。
【静态方法】 通过ClassName.funName=function(){...} 来定义。

参考  js--属性和方法(私有/公有)

《高程三 P184-P192 》

私有变量:任何在函数中定义的变量,都可以认为是私有变量(因为不能在函数外面访问这些变量)。
             私有变量包括函数的参数,局部变量和在函数内部定义的其它函数。
私有函数在函数内部声明的函数称为私有函数。
特权方法有权访问私有变量和私有函数的公有方法称为特权方法。
 
参考这两篇博客 博客一  博客二 博客三

公有方法:

  1.公有方法是可以在类的外部被调用的,

  2.但是它不可以访问类的私有属性。

  3.公有方法必须在类的内部或者外部通过类的prototype属性添加。

一般把共用的方法,都放在“原型对象“当中,如果放在构造函数中,会重复创建共同的方法。 

特权方法: 

  1.特权方法是可以在类的外部被调用的, 
  2.但是它可以访问类的私有属性,并且也是可以访问类的公有属性,可以勉强的认为它是一种特殊的公有方法。 
  3.但是它与上面的公有方法的声明与定义方式不同。特权方法必须在类的内部声明定义。

 在对象上添加特权方法:    (《高程三》P188)

1. 在构造函数中定义特权方法
 1     function MyObject(){
 2         var privateVariable = 10;
 3         function privateFunction(){
 4             return false;
 5         }
 6         this.publicMethod = function(){         // 特权方法,利用构造函数的缺点是每个实例都会创建同样一组新方法
 7             privateVariable ++;
 8             return privateFunction();
 9         };
10     } 

2.在私有作用域中创建特权方法

    (function(){
        // 私有变量 和 私有函数
        var privateValue = 10;
        function privateFunction(){
            return false;
        }
        // 构造函数
        MyObject = function(){}; //初始化未经声明的变量,总是会创建一个全局变量。因此,//MyObject成为全局变量,可以在私有作用域之外被访问到。没有var 属于全局变量,严格模式下会报错
                                    // 公有/特权方法 (既是公有,又是特权方法)
        MyObject.prototype.publicMethod = function(){
            privateValue ++;
            return privateFunction();
        };
    })();

[参考资料]

总结自《高程三》第四章  

理解Javascript_12_执行模型浅析   

JS的执行环境与作用域  

javascript高级程序第三版学习笔记【执行环境、作用域】

(The End)
原文地址:https://www.cnblogs.com/oneplace/p/5428532.html