# 代码输出值(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) }