JS优秀编码欣赏--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();

第一步 Foo.getName()

这里直接调用的Foo的getName 方法,代码中,第一个getName属性挂在了Foo方法中,第二个挂在了Foo的原型对象中。根据原型链,从当前对象开始找属性,没有的时候进入原型对象找。所以,这里会直接执行Foo上的属性。

第二步 getName()

这里的知识点是 声明提升

var getName = function () { alert (4);};
function getName() { alert (5);}

第一行是声明变量getName ,并赋予一个方法。

第二行是直接函数声明,函数声明会涉及到函数声明提升,提升到当前函数的外部作用域顶部。而var getName则在函数声明之后又进行了覆盖,所以执行的是var getName的方法

第三步 Foo().getName()

Foo() 执行Foo方法,其中getName没有var声明,会直接挂在window上,覆盖之前声明的getName。Foo()返回的是this。函数中的this取决于调用时的执行上下文。Foo()是在window被调用的,所以 返回的是window,即window.getName()。所以,调用的是刚刚覆盖的Foo()方法内的getName。

第四步 getName()

按照之前的逻辑,getName方法已经被覆盖,所以这是调用的还是刚刚覆盖到window的getName

第五步 new Foo.getName()

根据优先级,'new' < '.' < '()'

所以执行顺序是 new ( Foo.(getName()) )

Foo.getName() 执行的是Foo的属性getName,所以出来结果是2

第五步 new Foo().getName()

根据运行顺序,先执行new Foo(),然后执行getName()。Foo构造函数中没有为实例化对象添加任何方法,所以执行的是原型对象上的getName

第六步 new new Foo().getName()

执行顺序 new ((new Foo()).getName)(),返回结果肯定还是 3

注意事项 --- 构造函数的返回值

构造函数一般没有返回值,这时返回的是构造函数的实例对象。有返回值时,如果返回值是基本类型,则还是返回构造函数的实例对象。如果是引用类型,则返回该引用类型。

var obj = {
    "name": "小明"
}
function Fn1() {
    return  obj
}
Fn1.prototype.getName = function () {
    alert('1')
}
console.log(new Fn1())


var stic = "小明"
function Fn2() {
    return  stic
}
Fn2.prototype.getName = function () {
    alert('2')
}
console.log(new Fn2())

随机浏览