javascript两遍式执行

函数在被创建时保存外部作用域,是因为这个 被保存的作用域链(saved scope chain) 将会在未来的函数调用中用于变量查找。



所有的全局变量都可以用window变量来引用。
var  a;//在第一阶段就到window中去了。
b=100;//运行时,添加到window。

函数对象中的变量。
全局变量,运行前创建,运行时进入函数后访问。
var 局部变量,运行时进入函数创建,进入函数后访问
this变量,从外部传入的变量,运行时进入函数后可给其添加属性。



如果一个函数对象,其原型链上有一个函数x;
其作用域链上也有一个函数x,那个会被调用。



function a()
{
    this.x=function  x()
    {
        alert("aa");
    }
}


function b()
{
     function  x()
    {
        alert("ab");
    }

     x();
     b.prototype.x();

};

b.prototype=new a;
b.constructor=b;



b();//  alert("ab");alert("aa");


///

var x = 10;
 
function foo() {
 
  var y = 20;
 
  function barFD() { // 函数声明
    alert(x);
    alert(y);
  }
 
  var barFE = function () { // 函数表达式
    alert(x);
    alert(y);
  };
 
  var barFn = Function('alert(x); alert(y);');
 
  barFD(); // 10, 20
  barFE(); // 10, 20
  barFn(); // 10, "y" is not defined
 
}
 
foo();
我们看到,通过函数构造函数(Function constructor)创建的函数“bar”,是不能访问变量“y”的。但这并不意味着函数“barFn”没有[[scope]]属性(否则它不能访问到变量“x”)。问题在于通过函构造函数创建的函数的[[scope]]属性总是唯一的全局对象。




[[scope]]是所有父变量对象的层级链,处于当前函数上下文之上,在函数创建时存于其中。

注意这重要的一点--[[scope]]在函数创建时被存储--静态(不变的),永远永远,直至函数销毁。即:函数可以永不调用,

但[[scope]]属性已经写入,并存储在函数对象中。

另外一个需要考虑的是--与作用域链对比,[[scope]]是函数的一个属性而不是上下文。

1.javascript的变量是无类型的(untype),类型可以变化。
2.用var声明的变量由gc回收。所以当你试图用delete来删除其声明的变量时会报错。删除变量可以直接设置undefined
3.当试图读取一个未声明的变量时,会报错。alert()无反应。
4.当给一个未用var声明的变量赋值时,该变量 被隐式的声明为全局变量window的属性。
5.在函数内部,局部变量的优先级要高于同名的全局变量(相当于隐藏了该同名的全局变量)。 结合4,5两点。我们给出一个列子来说明其重要性。
var part = "global"; //声明一个全局变量
function checkpart() {
var part = "local"; //声明一个局部变量
document.write(part);
}
checkpart(); //输出local
document.write(part); // 输出global当你在声明part局部变量时省略了var。这就相当于声明了一个全局变量,即改变了全局变量part的值。

6.没有块级作用域。函数中声明所有变量(无论在哪里声明的),在整个函数中他都是有定义的。
var part = "global";
function checkvariable(){
document.write(part);
}
checkvariable(); //输出globalvar


part = "global";
function checkvariable(){
documen.write(part);
var part = "local";
document.write(part);
}
checkvariable();上述代码第一次输出的不是global,而是undefined。局部的part还未赋值。
这告诉我们在函数中声明变量时尽量将其集中放在函数的开头。

7.变量的内容 js中数据类型分为两组:基本数据类型和引用类型。
数值,布尔值,null和未定义的值属于基本类型。
对象,数组,函数属于引用类型(函数也是一种数据类型)。
(字符串属于特殊类型)。
基本数据类型在内存中具有固定的大小:一个数值占8个字节(在基本类型中最大),一个布尔值占一位。
而引用类型具有任意长度,没有固定的大小。无法将其直接与每个相关变量储存在8个字节的内存中。
所以变量储存的是这个值的引用(通常引用有两种形式:指针和内存地址)。这两中数据类型具有很大的差别。
var i = 1;
var j = i;
 i = 2;
alert(j); //输出1
var i = new Array(1,2,3);
j = i;
i[0] = 2;
alert(j); //输出2,2,3
8.作为属性的变量 js解释器在工作之前会在所有js代码运行之前创建一个全局对象。这个全局对象的属性就是所有的全局变量。
同理也会存在一个调用对象。而这个调用对象的属性就是所有的局部变量。一个js解释器可以工作在不同的执行环境中。
这些执行环境彼此独立且又相互联系。(执行环境:不同的窗口,框架)。



下面的代码,根据贵方任何一个function声明都不应该被执行:

if (true) {
 
  function foo() {
    alert(0);
  }
 
} else {
 
  function foo() {
    alert(1);
  }
 
}
 
foo(); // 1 or 0 ?实际在上不同环境下测试得出个结果不一样

这里有必要说明的是,按照标准,这种句法结构通常是不正确的,因为我们还记得,一个函数声明(FD)不能出现在代码块中(这里if和else包含代码块)。我们曾经讲过,FD仅出现在两个位置:程序级(Program level)或直接位于其它函数体中。

因为代码块仅包含语句,所以这是不正确的。可以出现在块中的函数的唯一位置是这些语句中的一个——上面已经讨论过的表达式语句。但是,按照定义它不能以大括号开始(既然它有别于代码块)或以一个函数关键字开始(既然它有别于FD)。

但是,在标准的错误处理章节中,它允许程序语法的扩展执行。这样的扩展之一就是我们见到的出现在代码块中的函数。在这个例子中,现今的所有存在的执行都不会抛出异常,都会处理它。但是它们都有自己的方式。

if-else分支语句的出现意味着一个动态的选择。即,从逻辑上来说,它应该是在代码执行阶段动态创建的函数表达式(FE)。但是,大多数执行在进入上下文阶段时简单的创建函数声明(FD),并使用最后声明的函数。即,函数foo将显示”1″,事实上else分支将永远不会执行。

但是,SpiderMonkey (和TraceMonkey)以两种方式对待这种情况:一方面它不会将函数作为声明处理(即,函数在代码执行阶段根据条件创建),但另一方面,既然没有括号包围(再次出现解析错误——”与FD有别”),他们不能被调用,所以也不是真正的函数表达式,它储存在变量对象中。

我个人认为这个例子中SpiderMonkey 的行为是正确的,拆分了它自身的函数中间类型——(FE+FD)。这些函数在合适的时间创建,根据条件,也不像FE,倒像一个可以从外部调用的FD,SpiderMonkey将这种语法扩展 称之为函数语句(缩写为FS);该语法在MDC中提及过。



第一遍扫描声明,包括var开头的变量声明,无论是直接的,还是if语句块内的,并将声明对应的变量均初始化为undefined。



对于if语句块内的函数声明,初始化为undefined.

对于不在if块内的函数声明,初始化函数对象。


对于if,for,while语句块内的函数声明,当执行到相应语句时,才对相应变量(普通对象,函数对象)进行赋值初始化。


第二遍执行变量定义,及代码。


函数对象的创建可能在第二遍之前,也可能在第二遍的过程中。

alert(a); // undefined, 这个大家都知道,
alert(b); //b 不存在

b = 10;
alert(b); // 10, 代码执行阶段创建b
var a=10;





alert(x); // function
 
var x = 10;
alert(x); // 10
 
x = 20;
 
function x() {};
 
alert(x); // 20

/


alert(x); // undefined
 
if (true){

var x = 10;
alert(x); // 10
 
x = 20;
 
function x() {};

};

 
alert(x); //function


/


alert(typeof  x); // undefined
 
if (true){

 x = 10;
alert(x); // 10
 
x = 20;
 
function x() {};

};

 
alert(x); //function



var a = new String('test');
 
alert(a); // 直接访问,在VO(globalContext)里找到:"test"
 
alert(window['a']); // 间接通过global访问:global === VO(globalContext): "test"
alert(a === this.a); // true

delete a;

alert(a);

//变量和属性的区别,变量有个dontdelete属性,不能被 delete,其余每太大区别


alert(g);//存在g// alert("h");

function g(){
    alert("g");
    };


function g(){
    alert("h");
};

同名,对于函数对象,覆盖定义,对于普通对象,忽略。

var g=0; 变量g已存在,第一遍扫描到var g时,忽略,继续往下扫描。

alert(g);//0


//



   alert(typeof(i));
   j=(i=0);//第一阶段未扫描到i;
   alert(i);
   delete window.i;
   alert(typeof(i));


for (var k in {a: 1, b: 2}) {
  alert(k);
}
 
alert(k); // 尽管循环已经结束但变量k依然在当前作用域


if (!("a" in window)) {
    var a = 1;
}
alert(a);//变量a存在,但值undefined.
a是全局变量,因此"a" in window
//
function x()
{
if (!("a" in window)) {
    var a = 1;
}
alert(a);//a=1.
}

a是function x的局部变量。

//

alert(g);//存在g// alert("g");
alert(a);
alert(typeof(f));//存在属性f,但其值undefined
alert(typeof(h));//存在属性h,但其值undefined

function g(){
    alert("g");
    function h(){
    alert("h");
    };

};

//只有函数才能产生作用域

//在g外部始终访问不了h ,在外部存在变量h,但其值始终是undefined.


//if块不产生作用域。

//f未定义

if (true) {
    var a="a1";
    alert("0f");
    alert(typeof(f));
    function f(){
    alert("f");
   };

//f已定义。
   alert(f);
}

a="a0";
alert(a);

alert(h);

alert(typeof(windows.g));//g 不是windows的属性
//alert(typeof(windows.a));//undefined
//alert(typeof(windows.f));



任何时候,变量只能通过使用var关键字才能声明。

上面的赋值语句:

a = 10;

这仅仅是给全局对象创建了一个新属性(但它不是变量)。“不是变量”并不是说它不能被改变,而是指它不符合ECMAScript规范中的变量概念,

所以它“不是变量”(它之所以能成为全局对象的属性,完全是因为VO(globalContext) === global,大家还记得这个吧?)。

让我们通过下面的实例看看具体的区别吧:

alert(a); // undefined
alert(b); // "b" 没有声明
 
b = 10;
var a = 20;
属性可以delete掉,变量却不行。
delete windows.b; 


//函数声明会在任何表达式被解析和求值之前先被解析和求值,即使你的声明在代码的最后一行,

它也会在同作用域内第一个表达式之前被解析/求值,参考如下例子,函数fn是在alert之后声明的,但是在alert执行的时候,fn已经有定义了:

  alert(fn());//'Hello world!'

  function fn() {
    return 'Hello world!';
  }

函数声明只能出现在程序函数体内。 不能出现在Block(块)({ ... })中,例如不能出现在 if、while 或 for 语句中。


扫描变量声明,不处理条件语句内的函数声明

  // 此刻,foo还没用声明
  typeof foo; //变量foo存在,但值 "undefined"

  if (true) {
    // 进入这里以后,foo就被声明在整个作用域内了
    function foo(){ return 1; }
  }
  else {
    // 从来不会走到这里,所以这里的foo也不会被声明
    function foo(){ return 2; }
  }
  typeof foo; // "function"
  alert(foo); // return 1;

函数声明内嵌的函数声明

  function fn() {
    return 'Hello world!';

   function fn2() {
    return 'Hello world!';    
  }
    
  }

   //alert(fn);
   alert(typeof fn2);//undefined


重复声明。后面的声明被忽略。

var x=20;

alert(x);

var x;

alert(x);//20


模块

var blogModule = (function (my) {

    // 添加一些功能   
    
    return my;
} (blogModule || {}))

如果blogModule在其它js中定义,本html包含了该js文件
var blogModule,(blogModule || {}));中的blogModule引用都是同一个blogModule,
相当于对其进行了扩展。
否则先创建一个空的blogModule对象.

注意下面=右侧的的function foo(){}是表达式,而不是声明。

赋值符=右侧只能是表达式,而不能是声明语句。

因此扫描声明时扫不到foo,相当于一个表达式中的局部变量,在外部不可见。

var f = function foo(){
    typeof foo; // foo是在内部作用域内有效
  };
  // foo在外部用于是不可见的
  alert(typeof foo); // "undefined"
  f(); // "function"

命名函数表达式的函数名(上例中的foo)只在新定义的函数作用域内有效,

因为规范规定了标示符不能在外围的作用域内有效,

给它一个名字1.可以让调试过程更方便。2.递归调用。

ECMAScript-262第5版引入所谓的严格模式(strict mode)。开启严格模式的实现会禁用语言中的那些不稳定、不可靠和不安全的特性。据说出于安全方面的考虑,arguments.callee属性将在严格模式下被“封杀”。因此,在处于严格模式时,访问arguments.callee会导致TypeError(参见ECMA-262第5版的10.6节)。

而我之所以在此提到严格模式,是因为如果在基于第5版标准的实现中无法使用arguments.callee来执行递归操作,那么使用命名函数表达式的可能性就会大大增加。

除此尽量不给函数表达式的命名,直接使用匿名。




ie存在bug( ie9修正了此bug),把 上例中的foo也在扫描阶段作为声明处理了,因此存在一个全局变量foo。

而在ie9以前会有两个函数对象f和foo.


 alert(f);//function foo(){};

 在firefox中不存在函数对象foo。


   在ie中运行下面的代码

   alert(f == foo); //ie9中 false 注意对于引用型变量==,===实际上都是比较是否是同一个对象。
    f.expando = 'foo';    
    alert(typeof (foo.expando));// undefined


   var o=Object();
   o2=o;
   alert(o2===o);//true



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值