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() //2
getName() //4
Foo().getName() //1
getName() //1
new Foo.getName() //2
new Foo().getName() //3
new new Foo().getName() //3
下面来认真分析一波上面的代码:
-
Foo.getName() //2
这个没什么问题,因为里面的getName不属于实例化也不属于函数的 -
getName() //4 这是因为赋值式函数(var getName = function)是不会提升的,但是声明式函数(function getName)会。这样会导致赋值式函数覆盖声明式函数。结果调用的是
var getName = function () {
console.log(4)
}
验证一下
fn()//1
var fn = function () {
console.log(2)
}
fn()//2
function fn() {
console.log(1)
}
fn() //2
第一个输出1,这是因为function fn函数提升,而var fn = function不会提升
第二个输出2,这是因为在var fn = function下面执行,此时已经将function fn覆盖了
第三个输出2,同理
-
Foo().getName() //1
执行了Foo(),但是输出1并不是调用了getName,里面的getName并不是这样调用的,而是因为return了this,此时this明显是window,注意Foo函数里的Foo并不是用var来定义的,正如上次小熊说的,没有用let/var/const定义的变量是没有函数作用域的,所以此时的getName为全局作用域的函数,Foo().getName()即相当于window.getName(),因为执行了Foo()相当于把这个getName激活了,覆盖了原来了全局的getName,即输出1 -
getName() //4
因为执行了Foo()相当于把这个getName激活了,覆盖了原来了全局的getName,即输出1 -
new Foo.getName() //2
相当于执行了一遍 Foo.getName(),当然是执行2啦 -
new Foo().getName() //3
此时相当于new Foo()后再执行getName方法,此时为啥不执行console.log(2)呢,是因为实例的proto指向构造函数prototype -
new new Foo().getName() //3
这个表达式相当于先new Foo()
再new (new Foo()).getName()
类似于:
let ans = new Foo()
let tem = new ans.getName()
new一个实例的getName,也就是在prototype里找getName,当然执行console.log(3)
经过上述可以发现,new都会找第一个的能new的函数,直到找到函数为止。找到了就是执行实例化的函数,如果没找到就报错。如果再加一个new,按照规则,继续new一个new过的实例化的函数的对象
上面提到了,里面的getName并不是这样调用的,举例:
function Goo() {
getGoo = function () {
console.log(1)
}
}
console.log(Goo().getGoo())//demo.html:48 Uncaught TypeError: Cannot read property 'getGoo' of undefined