这些题的考点涉及到了,变量的提升、new和点运算符的优先级、原型链、this的指向等知识点。
function Foo() {
getName = function() {
console.log(1)
}
return this
}
Foo.getName = function F() {
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
new Foo.getName() // 2
new Foo().getName() // 3
new new Foo().getName() // 3
// 输出 2 4 1 2 3 3
上面简单的就不重复解析:
从 Foo().getName() 开始解析:
Foo() 返回的是全局对象,那么语句相当于GO.getName(), 也就是取得全局对象中此时的 getName 属性,全局对象中的 getName 最后一次被Foo函数修改了,它的值为:
getName: function() { console.log(1)}
new Foo.getName() 解析这一行是这道题内比较难的一点,究竟是 new Foo 先还是 Foo.getName() 执行。这就涉及到了点的运算法和new的运算符的优先级问题。
看图:
这里重点关注new(带参数)和new(不带参数)于点运算符的优先级。
-
new (带参数列表)和成员访问(点运算符)的优先级是一样的(都是20),优先级一样的情况下,表达式从左向右运算,就好像加和减的优先级一样,计算时从左向右算。因此出现new Foo().getName这样的表达式时,从左向右,先计算new Foo(), 再计算**.getName,相当于(new Foo()).getName。就像3+2-1,就相当于(3+2)-1,我们知道这里的括号可以省略,是因为熟悉这样的运算顺序。带参数列表不一定要有实参,带了括号就表示能带参数的表达式,new Foo()和new Foo(10)是一样的表达式,前者的参数为0个而已。
-
new (无参数列表)的优先级是 19,成员访问(点运算符)的优先级是20,点运算符优先级更高,所以new Foo.getName先计算优先级更高的,也就是计算Foo.getName,再计算new **。
所以 new Foo.getName() 先执行 Foo.getName 在执行 new ,输出 2
new Foo().getName() 先执行 new Foo() 再执行 .getName() 输出 3
最后new new Foo().getName() 的解析:
要看懂这个表达式,要弄懂两点:
- 首先,要明白一元运算符的关联顺序。当有多个一元运算符连接时,从右向左执行。new是一元运算符,对
new new expression这样的表达式,先计算右边的new expression,然后将计算结果作为第一个new的表达式,相当于new (new expression)。 - 其次,new 运算符可以带括号也可以不带括号,带括号表示带参数列表,也就是传进构造函数设置对象属性的那些参数,如
new Dog('pappy'),传入name,或者new Dog(), 预备带参数,只是没传;不带括号则是无参数列表,如new Dog,name为undefined。
function Dog(name) {
this.name = name
}
对于new new Foo().getName();,先计算右边表达式new Foo().getName,得出结果再作为第一个new的表达式,相当于new (new Foo().getName)()
这里容易产生疑问,为什么不是
new (new Foo().getName())?试想一下,执行new Dog()这个语句,是先执行Dog(),然后将结果和new结合吗?不是。实际上这里是js new语法的解析,new Dog是一个表达式,()是执行这个表达式,()执行运算符的优先级最低。再想想,Dog.run(),是不是先求Dog.run,然后再执行这个函数,而不是先执行run(),再执行Dog.**? 实际上,new new Foo().getName()把括号去掉也可以,就是new new Foo().getName,变成无参数的new表达式。
那好了,最后表达式变成new (new Foo().getName)(), 在第15行代码中已经得知: 输出 3
myfunc = new Foo().getName = function(){ console.log(3);}