ECMAScript 262 5th :执行环境一些概念解释

可执行代码

ECMAScript 5th规定了3种可执行代码:Global code、Eval code和Function code。

根据名字就已经知道,Global codeEval code分别对应全局代码和eval函数中执行的代码。

三种可执行代码中最为复杂的就是Function code,即函数代码。因为javascript的特性,所以Function code中还可以嵌入Function code,导致了Function的执行环境会较为复杂。

执行环境(执行上下文)

当javascript引擎开始执行(进入)一段可执行代码时,就会生成一个执行环境Execution Context,或称执行上下文)。

javascript引擎通过一个栈(Stack)来维护执行环境,当进入一个执行环境,则将当前运行的执行环境压入到这个栈的顶部,代码表示:

var ECStack = [];//维护执行环境的栈
function enterExecutabCode(){
    var ec = new ExecutionContext();
    ECStack.push(ec);
}

一个执行环境是由:LexicalEnvironmentVariableEvironmentThisBinding组成的。

function ExecutionContext(){
    return {
        LexicalEnvironment,
        VariableEnvironment,
        ThisBinding
    }
}

词法环境(Lexical Environments)注意是复数哦~

一个词法环境对象包括:环境数据(Environment Record)和外部环境(outer Lexical Environment)。

外部环境(outer Lexical Environment)

表示外层函数的词法环境,有两种:nullglobal环境),或者外层函数的词法环境(嵌套函数)

环境数据(记录)(Environment Record)

有两种环境数据:declarative environment recordsobject environment records,因为存在两种类型的环境数据,所以词法环境的实现类型包括了两种:DecarativeEnvironmentObjectEnvironment

Declarative environment records

常见标识符绑定基本都是这个类型,例如函数定义,var声明,trycatch子句

Object environment records

包括两种一种是程序级别的(Program),另外一种是with语句,因为这两种绑定标识符过程需要传入一个对象做为环境数据的属性值。

继续说环境数据,环境数据存在于词法环境或者变量环境中,它包含了一个绑定对象(bindingObject),这个对象是一种键值对象,即有name和value一一对应关系,其中name就是标识符,value则为对应的变量值。

既然环境数据是保存数据的地方,必然有一些方法用于存储数据和读取数据。所以环境数据常见的方法有:

再探javascript作用域

对于javascript作用域的理解,之前整理过两篇文章:《javascript作用域和作用域链》《javascript的词法作用域》。最近读了周爱民老师的《javascript语言精髓》,对javascript作用域有了新的认识,特地整理下思路。

javascript的作用域广义来分:语法作用域变量作用域

javascript语法作用域

javascript语法作用域是讨论代码的组织结构上抽象,可以理解为讨论的是“圈地”问题,也有书籍中对纯粹的语法作用域称为“静态作用域”。

语法作用域分四级,下标依次增高

级别 作用域
级别一 表达式
级别二 语句/批语句
级别三 函数
级别四 全局(宿主)
  1. 同级别可以平行也可以相互嵌套(包含)
  2. 高级别可以嵌套(包含)低级别
  3. 低级别不可以嵌套(包含)高级别作用域;假如低级别嵌套高u别,可以理解成相互平行,例如下面的代码是完全等效的
if(true){
    //
    function fn(){
        //
    }
}

if(true){
    //
}
function fn(){
    //
}

ECMAScript 5的严格模式:语法限制

ECMAScript 5严格模式介绍的第一篇(ECMAScript 5的严格模式:strict mode介绍)文章中提到了严格模式会在语法解析和代码执行两个方面做限制,抛出更多的异常。今天介绍的是ECMAScript 5严格模式的第一部分:语法限制。

ECMAScript 5严格模式的语法限制有七个,下面一一介绍:

禁止函数出现相同的参数

我们知道在普通的函数中,多个参数可以相同(即使我们很少或者没有这样写过),但是在严格模式下,这样的行为是被禁止!
例如下面的代码:

function foo(x,x,y){
    alert(x+y);
}
foo(1,2,3);

在非严格模式下,后面的x会覆盖第一个x的传参,即运行结果实际为2+3=5

但是在严格模式下,上面的代码却会抛出类似“Strict mode function may not have duplicate parameter names”的语法异常。

function foo(x,x,y){
    "use strict";
    alert(x+y);
}
foo(1,2,3);

禁止对象直接量的相同属性名

跟第一条相同,下面的代码在非严格模式是正常的,但是在严格模式却是禁止的,也是抛出语法异常(chrome 下为“Duplicate data property in object literal not allowed in strict mode”)

var obj = {
    a: 1,
    a: 2
}
foo(1,2,3);

禁止重新定义eval和arguments

严格模式中不能声明或重写 evalarguments
这两个标识符,亦即是说,它们不能出现在赋值运算的左边,也不能使用 var 语句来声明。

另外,由于 catch 子句以及具名函数都会隐式地声明变量名,因此在它们的语法中也不允许用 eval 和 arguments 作为标识符。最后要强调的是,arguments 或 eval 也不能使用 delete 去删除。所以下面的代码在严格模式下都会抛出语法异常:

//向 eval或 arguments赋值
eval = function() { }

//重新声明 eval或 arguments
var arguments;

//将 eval或 arguments用做 catch子句的异常对象名
try {
    // …
}
catch (eval) {
    // …
}

//将 eval或 arguments用做函数名
function arguments() { }

//删除 arguments,或形式参数名
function foo() {
    delete arguments;
}

禁止使用 0 前缀声明的 8 进制直接量

例如下面的代码在严格模式下面是错误的:

var num = 012;
alert(num);

而在非严格模式中,num因为0而被解析为八进制数值,所以alert出来的是十进制的10

禁止delete显式声明的标识符、具名函数

//删除变量名
var x;
delete x;

//删除具名函数
function foo() {}
delete foo;

//删除 arguments,或形式参数名
function foo(x) {
delete x;
}

//删除 catch子句中声明的异常对象
try{} catch(e) { delete e }

在非严格模式中,通常这些操作只是”无效的”,并不会抛出异常。此外,用 delete操作其他一些不能被删除的对象属性、标识符时将导致执行期异常。

保留字增多

在严格模式中,在代码中使用一些扩展的保留字也会抛出异常。

这些保留字包括:implements, interface, let,package, private, protected, public, static,以及 yield。

禁止使用with(){}

我们知道with会使作用域链延长,如果你使用严格模式编码,那么使用with语句会直接抛出语法异常。

ECMAScript5的严格模式strict mode介绍

严格模式(Strict Mode)是ECMAScript 5中提出的一个新特性,在严格模式中的代码。这种严格的语境会防止某些特定的操作并抛出更多的异常。严格模式向前兼容,所以在不支持ES5的浏览器中是正常运行代码的,而只有支持严格模式的浏览器才会开启严格模式。

严格模式的好处

启用严格模式有以下好处:

  1. 捕获一些编程错误,并抛出异常。
  2. 阻止进行一些相对“不安全”的操作(例如访问全局变量),抛出异常。
  3. 禁用一些让人迷惑的特性。

启用严格模式,需要从下面两个方面来做限制(检查)

  1. 语法解析阶段:如果在语法检测时发现语法问题,则整个代码块失效,并导致一个语法异常。
  2. 阻止进行一些相对“不安全”的操作(例如访问全局变量),抛出异常。
  3. 代码执行阶段:如果在运行期出现了违反严格模式的代码,则抛出执行异常。

使用严格模式

在全局中使用严格模式

如果要在全局范围内使用严格模式,只需要在代码的第一行加上下面的代码:

"use strict";

这样在一个js文件,或者script标签内的所有代码都需要遵循严格模式的代码要求

函数中使用严格模式

如果要在某个函数中使用严格模式需要在函数的第一行加上"use strict";,如下面的代码

function foo(){
    "use strict";
    //……
}

使用严格模式注意的情况

需要注意的是,如果"use strict";不是出现在第一行(全局的、或者函数的),那么严格模式的启用会被忽略,即还是不会使用严格模式来解析执行代码。

另外在函数的严格模式中函数体和函数声明部分就需要遵循严格模式,而不是只有函数体内的代码才遵循严格模式,例如在严格模式中不允许重新定义eval,说以下面的代码是会抛出异常的

function eval(){
    "use strict";
    //……
}

今天写到这,下一篇介绍严格模式下面的语法限制。
ECMAScript 5的严格模式:语法限制

javascript作用域和作用域链

javascript的作用域是一个重要的知识点,javascript作用域(scope)是通过javascript的作用域链(scope chain)来实现的。

javascript作用域

javascript作用域(scope):简单的说,就是创建一个函数时在什么环境下创建的,它控制了javascript代码运行时变量和函数的访问范围。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。

全局作用域(Global Scope)

在代码中任何地方都能访问到的对象拥有全局作用域,注意:全局变量是魔鬼!因为它效率低(后面讲到),污染全局环境!一般有一下三种方式获取全局作用域。

代码最外层定义的函数和变量拥有全局作用域

在代码的最外层,定义的函数、变量,都是拥有全局作用域的。

var a = 1;
function b(){
    var a = 2;
}
b();
alert(a);//1

函数内部不使用var定义的拥有全局作用域

在函数内部,不使用var定义的变量拥有全局作用域,这是个坑!要注意,很多前端开发工程师不习惯写var,其实这时候你已经污染了全局作用域!

var a = 1;
function b(){
    a = 2;
    c = 3;
}
b();
alert(a);//2
alert(c);//3