search

Home  >  Q&A  >  body text

javascript关于函数声明和函数表达式的一道题?解释一下为什么?

function Foo() {
    getName = function () { console.log (1); };
    return this;
}
Foo.getName = function () { console.log (2);};
Foo.prototype.getName = function () { console.log (3);};
var getName = function () { console.log (4);};
function getName() { console.log (5);}

//以下输出值为多少?
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

解析一下为什么?

大家讲道理大家讲道理2897 days ago258

reply all(4)I'll reply

  • 伊谢尔伦

    伊谢尔伦2017-04-10 17:27:30

     1        function Foo() {
     2            getName = function () { console.log (1); };
     3            return this;
     4        }
     5        Foo.getName = function () { console.log (2);};
     6        Foo.prototype.getName = function () { console.log (3);};
     7        var getName = function () { console.log (4);};
     8        function getName() { console.log (5);}
     9
    10        //以下输出值为多少?
    11        Foo.getName(); 
    12        getName();
    13        Foo().getName();
    14        getName();
    15        new Foo.getName();
    16        new Foo().getName();
    17        new new Foo().getName();
    

    浏览器执行Js程序的时候,分两步:

    (1)预解析
        在代码解读之前发生,相当于一个"仓库",放一些东西,比如var、function、参数等。
        预解析时变量都是未定义的,函数则是整个函数块。
        预解析时遇到重名的,会覆盖。变量与函数,留函数;函数与函数,留后面解析的。
        当代码在一个环境中执行时,会创建变量对象的一个作用域链。是域就要预解析。
    
    (2)执行代码
        代码自上而下,从左往右的执行,读到表达式时,预解析仓库中的东西发生改变。
    

    我们来分析题目
    先预解析,预解析仓库里有:

    注意预解析时getName重名了,留函数。
    
    function Foo() {
        getName = function () { console.log (1); };
        return this;
    }
    function getName() { console.log (5);}
    

    执行代码:

            注意下面这个:
            function fn(){console.log(1)}
            console.log(fn())  //  2  
            function fn(){console.log(2)}
    
    前4行由于是函数声明,所以没卵用
    第5行:给Foo对象添加getName方法
    第6行:给Foo的原型对象添加getName方法
    第7行: *注意*遇到了表达式,域解析仓库中getName变为第7行的赋值语句
        此时预解析仓库变为:
            function Foo() {
                getName = function () { console.log (1); };
                return this;
            }
            getName = function () { console.log (4);};
    第8行:*注意*由于是函数声明,所以不影响预解析仓库
    

    下面我们来分析这几个题:

    Foo.getName(); //2 
        相当于执行第5行的console
        
    getName(); //4
        预解析里直接取,相当于执行第7行的console
        
    Foo().getName(); // 1
        Foo执行了,函数内部发生预解析,然而内部预解析仓库里什么都没有。
        执行函数Foo,第2行是个表达式,影响外面的预解析仓库,外面的预解析仓库变为:
            function Foo() {
                getName = function () { console.log (1); };
                return this;
            }
            getName = function () { console.log (1); };
        然后返回this,由于Foo()相当于window.Foo(),所以this指向window,
        Foo().getName()相当于window.getName(),window作用域下一切变量都可以看作他的属性,
        相当于执行第2行的console
        
    getName(); //1
        跟window.getName()一样,
        当于执行第2行的console
        
    new Foo.getName(); // 2
        执行第5行的Foo.getName(),
    
        new操作符调用构造函数创建对象的步骤?
        (1)创建一个新对象;
        (2)将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
        (3)执行构造函数中的代码(为这个新对象添加属性)
        (4)返回新对象
    
        new操作符是要执行构造函数中的代码的,打印2
        执行第5行的console
    
    new Foo().getName(); // 3
        Foo()执行返回this,此时this指向new出来的新实例对象,
        实例对象从本身找不到getName属性,顺着原型链找到第6行的getName,打印3
        执行第6行console
    
    new new Foo().getName(); // 3
        以实例的getName方法为构造函数new实例,执行构造函数,打印3
        执行第6行
    

    这题不错,欢迎指正。

    reply
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-10 17:27:30

    Foo.getName();  //  2 不解释
    getName();        // 4 
    /* 由于 js 会对变量和函数声明进行变量提升,
    所以,虽然 function getName 在后面,但是其实相当于在最前面
    */
    
    Foo().getName();  
    // 1 
     /*直接调用 Foo其中this是 widnow,
    而Foo中的getName没有加 var 所以是全局变量,也就是给 window.getName 重新赋值。
    所以上面相当于 window.getName(); 输出1/*
    
    getName(); //getName已经被重新赋值,所以是 1
    
    new Foo.getName(); 
    //2 从优先级来书相当于 (new (Foo.getName)()),所以调用的是 Foo.getName,输出2
    
    new Foo().getName();
    //3 
    /*(new Foo()).getNmae(),
     用new的时候,Foo中的this是以Foo为原型的新创建的对象,所以调用的getName是 Foo.prototype中的那个。
    此外 new 的返回值是,如果函数返回对象,那么返回值就是这个对象,否则返回this*/
    
    
    new new Foo().getName(); 
    //3 
    //new ((new Foo()).getName)(),同上,只是多加了一个 new,而new 只是改变了函数中this的值而已。

    reply
    0
  • 怪我咯

    怪我咯2017-04-10 17:27:30

    一道常被人轻视的前端JS面试题

    这篇博客介绍的特别详细。

    reply
    0
  • 黄舟

    黄舟2017-04-10 17:27:30

    你会用到这个

    reply
    0
  • Cancelreply