zoukankan      html  css  js  c++  java
  • Dmitry Baranovskiy的javascript谜题

    Dmitry Baranovskiy是何许人也?他是目前世界最优秀的JS开源图形库Raphaël的作者,还做了许多JS游戏自娱,是JS界顶尖高手之一。

    以下五道题是放于他的博客上,直到另一个它们被另一个JS高手Nicholas C. Zakas(Yahoo!主页首席前端工程师)提到,才迅速在网络蔓延开去。大家先试着自己做一下,想不明白才看解析吧。

         if (!("a" in window)) {
            var a = 1;
        }
        alert(a);
    
        var a = 1,
        b = function a(x) {
            x && a(--x);
        };
        alert(a);
    
        function a(x) {
          return x * 2;
       }
       var a;
       alert(a);
    
          function b(x, y, a) {
            arguments[2] = 10;
            alert(a);
          }
          b(1, 2, 3);
    
          function a() {
            alert(this);
          }
          a.call(null);
    

    第一题(请经过思考后再按运行框)

    第二题(请经过思考后再按运行框)

    第三题(请经过思考后再按运行框)

    第四题(请经过思考后再按运行框)

    第五题(请经过思考后再按运行框)

    解释

    //第一题
    //undefined
    //★★考察javascript在预编译期干了什么
    javascript分两个阶段,预编译期与运行期
    预编译期,var 变量提前,术语为提前声明。这时javascript引擎是从上到下,从外到内整块地分析我们的源码,
    构建语法树。
         if (!("a" in window)) {
            var a = 1;
        }
        alert(a);
    由于javascript不存在块作用域,因此if里面的东西与if外面属于同一个作用域。在预编译阶段,var a被抽取出来,
    放到作用域的顶部。更专业的解释可见ecma262r3,var 变量被放置到一个叫调用对象的东东中。
    那么这时,我们的代码就变成这样
       var a 
       if(!("a" in window)){
             a = 1             
       }
       alert(a)
    到运行期时,便开始从上到下,从里到外执行那些修改过的脚本。很明显,"a" in window 为true ,再加个否定
    就为false,进不到if语句里面了,当然alert undefined(凡是只声明没赋值的都会被自动赋上window的一个
    属性undefined)
    //第二题 
    // 1
    //★★考察函数声明,函数表达式与命名函数表达式
    这是函数声明:
    function aa(){
       alert("司徒正美!")
    }
    这是函数表达式:
    var bb = function(){
         alert("司徒正美!!")
    }
    那这个算什么?
            var cc = function dd(){
              alert("司徒正美!!!")
            }
    简单,也是函数表达式,不过是叫命名函数表达式。但这东西并不统一,先说标准浏览器下,dd这个函数名只对
    其函数体内可见,因此外围作用域一调用它就会出错,因为它并不存在此引用(或叫变量吧)。这特性好像比较
    新的标准浏览器支持,如FF3+,safari3+(safari2有bug)。再回头看IE,IE是支持这东西,还支持更多奇怪
    的写法,如
    http://bbs.51js.com/viewthread.php?tid=86272&highlight=%2Binfinte
    但IE的命名函数表达式很显然与标准的出入太大了。首先dd这函数名对函数的内外作用域是可见的,这很要命,
    很容易污染全局作用域,造成的命名冲突,其次,它会创建两个对象,cc是一个,dd是一个,修改其中一个会不
    会同步更新另一个,是内存泄漏的根源之一。命名函数表达式在IE中也作为函数声明,换言之它会在预编译阶阶
    就会干掉我们的同名函数声明,
    你怎样调试也打不出原因,因为调试是在运行期运行的……
    
    嘛,由于这个alert是位于外围作用域,因此在标准浏览器,我们的代码可以看作是这样:
       var  a = 1;
       var  b = function (x) {
            x && a(--x);
        };
        alert(a);
    因此是1
    在IE中,预编译阶段,其实有两个a,不过没有关系,反正前面的会覆盖后面的,然后到运行期,a会赋予1,这时它
    不会赋给另一个函数对象,因为另一个a已在预编译阶段就被干掉了。换言之,这次很幸运避免了创建两个函数对象,
    于是alert(1)。
    
    但命名函数表达式在IE下的糟糕表现,我们还是避免使用它吧,要想在内部调用自身,arguments.callee完成能胜任。
    
    //第三题
    //弹出函数的toString(),即
    //function a(x) {
    //    return x * 2;
    //}
    思路基本同第一题,var变量声明在预编译阶段被提前了,然后被重写。
    var a;
    function a(x) {
        return x * 2;
    }
    alert(a);
    //第四题
    //10
    //★★考察arguments对象
    在执行一个函数前,它会把它自身(callee),它的运行环境(callee.caller),传入参数的个数,构建成一个
    argument对象,接着就像数组一样,把参数一个个塞进argument中。运行时,如果某个参数被修改了,
    arguments对应的值也同步更新。
    
    理解这些就简单了。原函数的意思是传入三个参数,内部会把第三个参数修改成10,然后弹出第三个参数10,
    因此它根本不在乎你传什么,只要传够三个或三个以上参数,它就会alert 10。
    
    //第五题
    //弹出window对象的toString()
    //基本上不是[object Window] 就是[object],[object DOMWindow]
    //★★考察动态绑定的用法,详见我的博文《javascript的动态this与动态绑定》
    
    function a() {
        alert(this);//这里将返回其调用者
    }
    
    如果现在直接运行,肯定返回全局对象window。
    
    但如果使用apply或call来改变调用对象呢,原则上其第一个参数就是调用对象,通常也被称之为thisObject。但
    有几个特殊情况,当第一参数为null,undefined,或干脆一个参数也没有,就会默认为是传入window对象。
    
    
    

    补充实验:

    嘛,解释就完全按我的理解去写啦,应该大体上和Nicholas C. Zakas差不多。可能他的更详细,毕竟从原文篇幅来看也确实如此,英文好的就看英文吧。本时我搜到一些俄罗斯文,法文什么的,也基本上贴在google在线翻译上,连蒙带猜地学习。外国人的技术比我们强多了,大家应该多上外国网站。

  • 相关阅读:
    谷粒商城心得(四)
    centos7设置rc.local开机执行命令
    密码学简介
    如何解决 kubernetes 重启后,启来不来的问题
    谷粒商城安装ES及入门(十六)
    谷粒商城读写分离(十五)
    谷粒商城创建mysql主从(十四)
    虚拟机LVM在线扩容
    Builder 模式初探
    Mysql 导入实战
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/1658434.html
Copyright © 2011-2022 走看看