JavaScript深度解析

来源:互联网 发布:下载床上交友软件 编辑:程序博客网 时间:2024/05/16 09:15
Copyright (2013) 郭龙仓. All Rights Reserved.

变量声明与表达式(declarations and expressions)

代码示例:

var a; // declarationvar a = 2; // declaration and expressionvar func1 = function(){ alert(3) }; // declaration and expressionfunction func2(){ alert(4) }; // declaration a function(function(){ alert(5) }); // expression


alert(c);  // 'undefined'alert(a);  // 'undefined'alert(b);  // 'undefined'alert(d);  // 'undefined'alert(func);  // 'function func(){}'var c = a;var a =1;var b = function(){alert(2);}var d = a;;function func(){}


下面这段话可能有助于你理解变量声明与表达式的区别:

  1. Anything to the right of an = sign (or : on object literals).
  2. Anything in braces ().
  3. Parameters to functions (this is actually already covered by 2).

变量声明的结果是创建一个标识符,表达式的结果是生成一个值或者对象。

编译与执行(compilation and execution)

JavaScript代码执行分为两个阶段:
阶段一,编译(compilation)
在这个阶段代码被编译为执行树(字节码或二进制的形式,具体依赖于不同的JavaScript引擎)。
阶段二,执行(execution)
解释并执行阶段一生成的代码

编译阶段处理变量声明,执行阶段处理表达式。因此,在编译阶段完成后和执行阶段开始前,所有已声明变量的值都为undefined。

可执行代码类型(Executable Code)

JavaScript的可执行代码分为三种类型(此处就不进行代码示例了,大家可以从名字猜测每种类型所对应的代码):
  • 全局代码(Global Code)
  • 函数代码(Function Code)
  • Eval 代码(Eval Code)

执行上下文(Execution Contexts)

每当JavaScript引擎进入一段可执行代码,就会生成一个对应的执行上下文。

在为函数代码生成执行上下文之后,JavaScript引擎还会创建一个对应的Arguments对象。

JavaScript引擎通过一个栈(Stack)来维护执行上下文。


图1 Environment Record
图1 Environment Record


图2 Lexical Environment
图2 Lexical Environment

图3 Execution Context
图3 Execution Context

执行上下文由三个部分组成:

  • Lexical Environment(词法环境,也可以称之为Variable Object,用于构造作用域链)
  • Variable Environment(变量环境,也可以称之为Activation Object, 用于存储本地变量)
  • This Binding(this 绑定)


Lexical Environment 与 Variable Environment 均是 Lexical Environment 类型,都用来存储外部词法环境引用(outerLexicalEnvironment,请参考图2)、本地参数/本地变量(environmentRecord,请参考图2)。它们的区别在于:Variable Environment 在代码执行过程中是不变的,而 Lexical Environment 会随着代码执行进入/退出with或者catch语句块的时候动态改变(with语句块和catch语句块会引起变量环境的临时改变)。

Environment Record用来存储本地参数/本地变量。有两种类型的Environment Record存在:

  • Declarative Environment Record
  • Object Environment Record

一般情况下,Environment Record 是 Declarative Environment Record 类型的,但是也有少数情况下是 Object Environment Record 类型(比如浏览器环境下,window对象的属性之所以成为全局的,就是因为它的全局 Environment Record 是 Object Environment Record 类型的,还有一种情况就是 with 语句)。

执行上下文栈代码示例:
// 执行上下文栈(ECStack)状态变化示例/*ECStack = [  globalContext];*/ eval('var x = 10');/*eval执行上下文入栈ECStack = [  evalContext,  callingContext:globalContext];*/ // eval执行上下文出栈/*ECStack = [  globalContext];*/  (function foo(){ alert(10) })();// function执行上下文入栈/*ECStack = [  <foo> functionContext,  globalContext];*/ // function执行上下文出栈/*ECStack = [  globalContext];*/ (function outfool(){  function infoo1(){ alert(20) }    eval('var y = 30')    (function infoo2(){ alert(40) })();    return infoo1()})()();  // function执行上下文入栈/*ECStack = [  <outfool> functionContext,  globalContext];*/ // eval执行上下文入栈/*ECStack = [  evalContext,  callingContext:<outfool> functionContext,  globalContext];*/ // eval执行上下文出栈/*ECStack = [  <outfool> functionContext,  globalContext];*/ // function执行上下文入栈/*ECStack = [  <infoo2> functionContext,  <outfool> functionContext,  globalContext];*/ // function执行上下文出栈/*ECStack = [  <outfool> functionContext,  globalContext];*/ // function执行上下文出栈/*ECStack = [  globalContext];*/ // function执行上下文入栈/*ECStack = [  <infool1> functionContext,  globalContext];*/ // function执行上下文出栈/*ECStack = [  globalContext];*/

作用域链(Scope Chain)

Lexical Environment通过持有一个外部词法环境(outerLexicalEnvironment,请参考图2)的引用来形成作用域链。

闭包(Closure)

当函数B是函数A的内部函数,且函数A的返回值为函数B。那么,每调用一次函数A,并把A的返回值赋值给一个变量,就完成了一个闭包的构造。

闭包代码示例:
function buildClosure(arg1, arg2){    var localVar = 8;    function innerFunc(innerArg){        return ((arg1 + arg2)/(innerArg + localVar));    }        return innerFunc;} var closureVar = buildClosure(2, 4);

new 关键字做了什么?

new 代码示例:
function Dog( host ) {  this.host = host;} var dog = new Dog( "Jack" );

当 newDog("Jack")被调用的时候,JavaScript引擎做了下面这些事情:

  1. 创建一个空的Object对象
  2. 将这个Object对象的原型设置为Dog
  3. 准备执行Dog函数,并用这个Object作为this参数,字符串"Jack"作为host参数
  4. 执行Dog函数
  5. 返回该Object对象

用不用 var 关键字有什么区别?

如果在全局作用域,用不用 var 关键字没有任何区别。
如果在局部作用域,使用 var 关键字会使JavaScript引擎创建一个局部变量,不使用 var 关键字会导致JavaScript沿着作用域链一直向上查找,直到找到该变量,如果最终没找到该变量,JavaScript引擎会在全局作用域创建该变量。

this 关键字怎么绑定的?

this值是在JavaScript引擎执行过程中动态绑定的,与源代码的位置无关,与代码执行过程有关(请参考图3)。

如果执行的是全局代码,那么this指向的是全局对象。如果执行的是函数代码,那么this指向的则是函数的调用者。如果执行的是Eval代码,那么默认情况下this的值与Eval代码外部环境中this的值一致(除非你在执行Eval代码的时候通过参数明确指定eval的执行上下文)。

以下方法可以修改默认的this值:

  • Function.prototype.apply( thisArg, argArray )
  • Function.prototype.call( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Function.prototype.bind( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Array.prototype.every( callbackfn [ , thisArg ] )
  • Array.prototype.some( callbackfn [ , thisArg ] )
  • Array.prototype.forEach( callbackfn [ , thisArg ] )
  • Array.prototype.map( callbackfn [ , thisArg ] )
  • Array.prototype.filter( callbackfn [ , thisArg ] )

delete 为什么会返回 false ?

JavaScript的对象属性具有(Property)一些特性(Attribute),包括DontDelete特性。具有DontDelete特性的属性在使用delete删除的时候会返回false。

任何明确声明的变量会成为Variable Object的属性,并且具备DontDelete特性。任何通过赋值的方式声明的对象属性都不具备DontDelete特性,因此可以成功delete。

另外,一些内置对象属性也具备DontDelete特性。



原创粉丝点击