new运算符和点运算符优先级

1,049 阅读3分钟

这些题的考点涉及到了,变量的提升、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的运算符的优先级问题。

看图:

cXAdGd.png

上表详情见MDN

这里重点关注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);}

参考掘金:juejin.cn/post/684490…