浏览器过度优化.产生的一些小问题.

分别测试注释掉 首行代码 alert; 与不注释掉的 执行结果差异. 
 
 alert;//注释后再次在ie6 ie7 ie8中测试下
  var hijack = window.alert;
  window.alert = function(){
    hijack('hijack');
  };
  alert('origin');
 
当然非ie浏览器 是无此问题的.
ie9也已经更改了这种惰性绑定机制. 所以也无此bug了.
是的.之所以出这个问题. 就是因为. IE的工程师们大概觉得. 进入js执行环境后.立刻把window对象上的宿主成员.都给global对象一份.是得不偿失的.所以引入了类似惰性绑定的思路.
 
其过程大概是:
当你第一次在作用域链中查找该成员名,查找到global上时,由于不具备该变量标识符,则继续沿着作用域链向上,直到window对象.终于找到了该成员.随后把该成员赋予给global对象.以便下次,再次发生作用域链查找时.我们可以直接在global上找到.而不需要再次到window上找了.  实现起来也很容易,只需要一个简单的getter就可以做到. 当然.直接window.obj 的方式是不行的. 必须当window出现在作用域链对象末端中,被查找时.即会触发getter的逻辑.
 
这就是为什么.注释掉alert; 这句后.执行结果是弹出 'hijack'. 
当注释掉后 alert('origin'); 这句的执行,其中对alert的查找成为了首次查找.此时global不具备alert方法.导致去window上找.而此时 window.alert已经被劫持掉了.所以给global的方法,也只能是被我们劫持过的引用啦.
有兴趣的话,可以试试其他宿主对象或方法,比如 confirm、prompt、open、execScript、navigator或者IE8的JSON对象(注释1).等等等等.
 

 

现在,我们同样也可以理解,为什么 alert = function(){};这种劫持在IE6,IE7,IE8中会报错,而IE9是OK的了. 
其实呢IE6-IE8也是可以的. 如以下代码:
window.alert = null;
alert = function(x){document.write(x)};
alert('franky'); //顺利在文档流中加入了 'franky' 并输出到页面. 
原因你理解了么?

 

其实不仅仅IE浏览器喜欢干这件事. 早先我在 一篇讨论arguments的随笔中也提到过,chrome浏览器类似的优化导致的 bug. 现摘录原文如下,仅供参考:
(该bug chrome15已修正)
chrome下执行一下代码 ...

     Object.prototype['0'] = 'franky'; 
     function f(a){ 
 
          alert(a); 
          arguments; //如果注释掉arguments调用.则 alert(a)的结果将完全不同.
        //eval(''); 
     } 
     f();


相关解释:
回头看看chrome干的好事..明显 eval 和arguments 把其对arguments这个对象的某些保护机制给干掉了....
最初,我是这样想的.但仔细想想 不大对劲... arguments;或 eval('') 是放到alert后面也生效的...那么问题应该出在词法分析期.而不是执行期,那么我们就可以推断出,chrome 在分析一个函数主体上下文的时候,会看你是不是有 arguments或eval 如果有 则给这个函数arguments 对象,反之则没有.这本质上 是一种优化. 也就是说chrome程序员,压根就没有给arguments任何保护机制.阻止其访问Object.prototype.
证明这一推论的 论据并不是很充分.但是我还是尝试给出2个...
如果eval 也会触发这个bug .那么我们是不是可以考虑为 . eval 具备一个隶属当前执行上下文环境的(作用于当前作用域),独特的执行上下文环境. 他内部一但出现arguments.则就是当前函数的arguments.  所以chrome不得不在 eval出现时, 也派发给当前函数对象 arguments对象.从而出现了这个bug..
因为 不仅仅eval,new Fcunction(),window.eval('') 也都具备动态执行语句的特点..而区别则在于 作用域.即 除了 eval(''),其他两种方法 无法访问到当前函数的arguments. 
 
注释1:如果有朋友看过ECMA V5的话,会认为 JSON应该是原生对象,而不是宿主对象.但是很遗憾.至少IE8的JSON并不是原生的.而确实是由宿主提供的. 关于这个问题.我曾经和某群友争论过.现在想想大可不必啊.我们就已 ECMA 为准吧. 虽然老道有强推JSON的嫌疑.但是个人认为,引擎上实现的JSON要可靠快速的多.
 
额外补充一点.

open;//按照前面的理解.此处, global.open 会被赋予 window.open
eval('var open');

//按照ES3,5的规范. 这样做应该没有任何意义. 因为global 作为variable object 已经具备一个名为 open的属性. 所以此处应该忽略.

//但实际结果是. open 被赋予了 undefined 这种行为.实在让人无语. 可以说 IE 对 window global的绑定关系已经足够奇葩

//但是对global作为variable object 时.属性为一个宿主方法时的行为则更奇葩...

//所以 如果我们在中间插入eval('var alert'); 又会使alert恢复为原始状态....  同时,干掉window.alert

 

 
 
posted @ 2010-12-20 13:56  Franky  阅读(2713)  评论(5编辑  收藏