很庆幸 在 javascript高级群 中认识了 wait. 因为他的 激情 和一些 奇思妙想.敦促我们大家不断的 研究更多更有趣的 怪异问题.

同时 真的加深了 我对js 的理解...   那么下面一些文字 属于 我于 wait 还有其他几位朋友 闲聊时 扯出来的一些js怪异现象的 总结...  其中也有疑问  至尽 不知其原理.

 

我们知道 js  有种机制 叫预编译.

即 在根环境 顺序执行的过程中 会把 var 和function 这两个声明 关键字 提前解释一下. 以告诉浏览器 . 有多少个变量和 方法被声明.

 

对于一个变量声明. 其过程如下.

var x=1;

在预编译阶段 .他等价于 var x;  即 var x=undefined 以便告诉浏览器 这里有个变量. 但不会去执行 赋值语句.

只有在预编译完成后 真正 解释执行 当前代码时 才会执行 x=1; 部分.

这也就解释了.下面两组代码的区别和现象 :

 

Code Snippet

alert(x);//打印undefined

var x=1;

Code Snippet

alert(x) //不含下面的声明语句时 .浏览器报错.

 

 

那么 function  既然也属于预编译 的目标. 为什么其 表现和 var差别如此巨大呢? 我的疑问  就在这里

 

有些观点 提出 function f(){} 和 var f=function(){}  是完全等价的.

很明显.根据前面对 var 的测试. 即说明两者的差别.  这也解释了 为什么 以OO方式去写js时 this.func=function(){} 一定要出现在你调用 this.func()前面 的根本呢原因.

 

那么很明显 对于function 和var 的预编译机制 是不同的. 浏览器会在预编译阶段. 把function 声明部分直接 解释..如果是具名函数. 则会把函数名作为存放该函数引用 的变量. 直接标记出来. 这就解释了 方法可以 先调用 后声明的原因.   这仅仅是我的推断. 如果有该部分的资料 希望各位老师 可以给出.

 

那么看下一个问题. 也是有关浏览器 解释执行时的一些细节

 

很多类库 都会把 其实现 都封装到 一个伪名称空间中. 即 把代码 放到一个 自执行的匿名函数中去 如

(function(){........code....... })();

在我过去逐渐加深对js的学习过程中. 第一次看到 自执行匿名函数时. 实在是觉得 匪夷所思. 他是如此的优雅.

但并没有对他 深入的去理解.  最近通过看 周爱民 老师的blog  才引起了我对它的 反思... 为什么函数声明部分 加个括号.就可以执行了呢? 而function(){}();就会报错呢? 通过不断的测试. 终于摸到些门道.

首先 我们得明白 一个函数声明部分 对 js来说意味着什么.  根据前面的 预编译. 我们知道 他就是一段声明. 而不同于一般的表达式. 表达式 预编译期间. js完全忽略   所以 当 js解释执行 到 function(){}(); 部分时 会忽略掉 声明语句. 因为 那部分 在预编译时.  js已经解释执行过了. 再执行一次 就属于 浪费感情.  所以 它跳过.  然后试图 去执行(); 这显然就会报错.

那么加了( function(){})  有什么区别呢? 很显然 ( ) 是表达式的一部分 所以js遇到 ( ) 会被迫去 对( )中的 部分求解 得到返回值. 这就逼迫 js去再次解释执行 函数声明部分的语句.... 所以到了这里 我猜测 如果js没有 某个机制 去 分辨 是否某个函数 已存在 过 而 此时 把() 中的函数声明 部分看成是 类似某条件分枝 被满足时 才执行的声明语句. 则可能会产生两个 作用相同的 函数.  从而 浪费内存. (这只是我的推断.没有任何资料可以支持我的观点.)

 

接着上面.  我们可以做以下尝试.用类似的语句 同样实现 匿名函数自执行:

+function(){...}();

1,function(){.....}();

结果证明. 完全可行.

 

最后再说一个  我至尽没有找到答案的 怪异问题 一个关于 delete 的问题. 希望各位老师能给予指点.

我们都知道 delete 是无法 删除 一个私有属性 或 原型链中的东西的.

var x=1;

delete x;

alert(x);//打印1 即删除失败.

但下面的代码却没有问题.

 

eval(' var x=1;');

delete x;

alert(x);// 报错 x未定义 .说明删除成功.  很明显 eval 中的val 是无效的. 那么 我很想知道 这个x 到底是怎样一个存在.

因为他 并不简单等价于 eval('x=1') 或 x=1;   用个测试即可证明

 

首先 要提的就是 我们以前 在某个非 window 作用域(即非根环境声明全局变量) 内声明一个 全局变量 是 不使用 var 声明语句 .而直接 使用 如  x=1; 这样的表达式.  一旦js执行到这里 就会产生一个 全局变量

下面的例子. 说明他们的不同

function func(){

  a=1;

}

func();

alert(a)// 或alert(window.a) 打印 1

function func(){

  eval('var a=1;');

}

func();

alert(a) //报错. alert(typeof a) 打印undefined

就是这里. 彻底把我搞晕了. 那么 eval 中出现的 var 变量声明 到底意味着什么呢?

 

 

我们再来看看  var 的威力有多大.

 

window={}; //ie报错 . ff忽略之.

那么

var window={};// ie opera safari 全过  ff3.0 报错

alert(window.location);// 报错.对象不支持此属性或方法.

 

说明 只有ff 的js  moneky引擎 做了预防. 即 不允许你声明的变量名 同 核心对象名 重复.

而其他引擎 都 允许..  那么我们暂时不讨论引擎的问题.  只看 var window={} 成功执行之后的情况

 

 上面两组代码 充分说明 一下2点问题:

1.js的预编译机制的 特权. 

  因为我们根据上面2组代码 可以看出 真正起作用的 不是 赋值语句 而是 变量声明部分 即 var window;//此时强制window 指向undefined对象. 然后 执行到 window={}; 因为此时 window指向的 已经不是 我们的那个全局环境(平常我都叫他window对象...) 而是undefined . 所以js 允许 我们为这个变量 赋值...为一个 new Object()...

2. window!=全局环境. 而仅仅是一个指针 一个引用 一个句柄.

因为  下面的代码 说明了此问题:

ie opera9  safari  chrome 可过

var window={};

alert(123);//正常打印 123

window.alert(123)//失败.不存在该方法. 因为window引用的 目前是一个空对象.  而不是 原始的window 引用即 全局环境 .

 

posted on 2009-06-17 23:48  钱途无梁  阅读(483)  评论(0编辑  收藏  举报