# 代码输出值(this/函数/原型)

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()

// 2 4 1 1 2 3 3

解析一下是为什么呢

# 2

函数也是一个 object, 可以拥有属性和方法, Foo.getName 被赋值为一个输出 2 的函数, 所以输出 2

Foo.getName = function () { console.log(2) }
Foo.getName() // 输出 2

# 4

getName()父级作用域为window, 相当于调用window.getName(), Foo()还未被执行, 不需要考虑Foo函数体内对getName的影响, 剩下最后两个

var getName = function () {     // 函数表达式
    console.log(4) 
}
function getName() {            // 函数声明
    console.log(5)
}

/**
 * 函数声明会提升,题中会变成这样
 */
var getName;
function getName() {
    console.log(5)
}
getName = function () {
    console.log(4) 
}
getName()   // 所以此处打印 4

# 1

Foo().getName()

// 我们一步步拆解, 上边的语句相当于
var context = Foo();
context.getName();

来看 Foo 的声明:

function Foo() {
    // 下边这句没有 var, let 或 const, 相当于 window.getName = xxx
    getName = function () { console.log(1) }
    return this     // 这里的 this 要看调用方式, 直接调用 Foo() 则 this 指向 window, new 调用, this 指向 new 出来的实例
}
Foo().getName()

仔细看上边的注释, Foo函数体内对window.getName进行了改写, 这是下一个输出的关键

# 1

如上边分析的, Foo()函数的执行, 对window.getName进行了改写, window.getName此时已经变为function () { console.log(1) }

# 2

该语句先执行Foo.getName, 与第一个结论一致, 输出 2, 只是new会返回一个 object, 这个object指向new出来的实例, 但这里这个实例没被使用, 就不进一步分析了

new Foo.getName();

# 3

new Foo().getName()

// 拆解如下
var foo = new Foo()
foo.getName()

如果你是一路看分析下来的, 就会明白foo这个实例, 就是Foo函数体里的this, 从原型的知识中, 我们可以知道, 如果调用一个实例的方法, 在实例方法中找不到, 就会从实例原型中找.

Foo.prototype.getName = function() { console.log(3) }

# 3

new new Foo().getName()

// 拆解成
var foo = new Foo();
var bar = new foo.getName();

从上边new Foo().getName()的分析, 可以知道foo.getName()是在foo的原型里边的, 这里new了一下原型里边的函数, 相当于先执行了, 再返回了一个新的实例. 这里的执行, 也就是执行了下边这个方法:

Foo.prototype.getName = function() { console.log(3) }