js的一道经典题

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

    Foo.getName();
    getName();
    Foo().getName();
    getName();
    new Foo.getName();
    new Foo().getName();    
    new new Foo().getName();

2 ----Foo.getName();
    4 ----getName();
    1 ----Foo().getName();
    1 ----getName();
    2 ----new Foo.getName();
    3 ----new Foo().getName();
    3 ----new new Foo().getName();

概念

为了阅读效益最大化,请先了解执行环境变量对象文字性概念。html

执行环境挺常见的,它是长这样的。它的特性是先进后出(stack)。函数

clipboard.png

上述图片是来源于代码执行到Foo( ).getName( )。this

(anonymous function) 其实就是global contextspa

表示方法:prototype

ECStack = [
        functionContext(Foo),    /*这不是函数*/
        GlobalContext    
    ]

变量对象也挺常见,在Chrome中长这样。指针

clipboard.png

表示方法一般是:code

//以上述代码还没有执行时为例
    VO = {    
        Foo: {
            <reference to function>,
            return this
        }
        getName: <reference to function>
    }

分析

代码执行有两个阶段htm

  1. 进入环境(代码未执行,已编译)对象

  2. 代码执行blog

如今一块儿来看看每一行代码的执行

上述代码未执行时,变量对象是这样的。

VO = {
        Foo: {
            <reference to function>,
            return this
        }
        getName: <reference to function(){alert(5)}>
    }

执行"第一行"代码

clipboard.png

该行代码执行后VO会变成这样。

VO = {
        Foo: { 
            <reference to function>,
            getName: <reference to function() {alert(2)},
            return this
        }
        getName: <reference to function(){alert(5)}>
    }

VO的Foo图示是这样的
clipboard.png

执行"第二行"代码

clipboard.png

该行代码执行后VO会变成这样。

VO = {
        Foo: { 
            <reference to function>,
            getName: <reference to function() {alert(2)},
            prototype: {
                getName: <reference to function(){alert(3)}
                },
            return this,
                
        }
        getName: <reference to function(){alert(5)}>
    }

VO的Foo图示是这样

clipboard.png

执行"第三行"代码

clipboard.png
该行代码执行后VO会变成这样。

VO = {
        Foo: { 
            <reference to function>,
            getName: <reference to function() {alert(2)},
            prototype: {
                getName: <reference to function(){alert(3)}
                },
            return this
        }
        getName: <reference to function(){alert(4)}>
    }

执行"第四行"代码(第一问)

clipboard.png
直接去VO里寻找结果发现alert 2。

执行"第五行"代码(第二问)

clipboard.png

getName() //等于
window.getName();

一样直接在VO里能够直接找到答案 alert 4。

执行"第六行"代码(第三问)

clipboard.png
这里比上述多了一个函数调用,Foo()调用返回this(指向window),因此此时

Foo().getName(); //等于
window.getName()

同时注意:
因为Foo()调用,致使VO发生了变化。最后alert 1.

VO = {
        Foo: { 
            <reference to function>,
            getName: <reference to function() {alert(2)},
            prototype: {
                getName: <reference to function(){alert(3)}
                },
            return this
        }
        getName: <reference to function(){alert(1)}>
    }

执行"第七行"代码(第四问)

clipboard.png

结果同上

执行"最后三行"代码(第5、6、七问)

clipboard.png

最后三问考察了new、属性访问符、及函数调用优先顺序和new运算符的做用。

优先顺序参考以下,或点这

clipboard.png

不管是用哪一种属性访问表达式,在"."和"[]"以前的表达式老是会先计算的。

对于new Foo.getName()来讲,点运算级最高,因此先进行计算表达式Foo的值。若是先运算new的话,就证实了new优先级高于点号,与规范不和。

而对于new Foo().getName()来讲,Foo()与new Foo()相比,new优先级更高,因此先算new Foo(),new出来的新对象继承了Foo.prototype属性,所以新对象访问getName时会弹出3.

此处要明白new的做用

  1. 建立新的空对象

  2. 将新对象继承构造函数的prototype属性,并调用构造函数初始化

  3. 将构造函数的this指针指向新建对象。

最后一个问题同理倒数第二个。


谢谢ID:李卫的提醒。

原题和分析连接

相关文章
相关标签/搜索