事情要从偶然间看到的一个面试题说起,大概长这样:
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();今天并不是要把整道题讲明白,有兴趣的小伙伴可以自己去了解下:
整道题的每一个考点,上面都说的非常清楚了。我也是看懂了之后才动手总结这篇笔记。主要总结一下我在这道题中对 JavaScript 疏漏的部分。而这部分,不是作用域、不是变量提升、也不是原型链,而是大多是前端工程师都容易忽视的:运算符优先级问题
根据这个问题,我们很容易就能从上面的面试题中抽离出一个简单且典型的:
// 已知函数
function Foo() {
this.getName = function () {
alert(1)
}
}
Foo.getName = function() {
alert(2)
}
//请写出以下输出结果:
new Foo().getName()
new Foo.getName()答案是:
new Foo().getName() // alert(1)
new Foo.getName() // alert(2)乍一看,确实应了标题,确实就是构造函数后有无括号的区别。
我们知道,new 构造函数的时候可以传递参数放进括号里,当不需要传参时,括号可以省略。没有参数时,带不带括号对 new 出的对象来说,没什么影响。但是对构造函数本身来说,影响简直不要太刁钻。尤其是配合函数调用的 . 操作符使用时。
先说结果吧!上面两个 new 执行过程其实是:
// new Foo().getName()
new Foo().getName()
// 相当于
(new Foo()).getName()
// 相当于
const foo = new Foo() // { getName() { ... } }
foo.getName()
// new Foo.getName()
new Foo.getName()
// 相当于
new (Foo.getName)()
// 相当于
const getName = Foo.getName
const obj = new getName() // obj instanceof Foo.getName === true是不是有点 amazing。其实 JavaScript 的运算符是有优先级的。先上优先级情况,然后我们在结合上面那道题进行分析:
根据上面的优先级顺序,上面的面试题中出现了一共出现了 3 种运算符:
new Foo().getName() // new . ()
new Foo.getName() // new . ()首先要确定的一点, . 运算符在任何情况下优先级都比 new 和 () 要高。但是除了 . 运算符外,其他两个或多或少都在不同的情况有不同的优先级:
如:new X() > x() > new X
还是结合上面的题来看吧:
// 已知函数
function Foo() {
this.getName = function () {
alert(1)
}
}
Foo.getName = function() {
alert(2)
}
//请写出以下输出结果:
new Foo().getName() // 第一题
new Foo.getName() // 第二题第一题:
第一步,先执行: . 运算符:
new Foo().getName() // => (new Foo()).getName(). 运算符的作用是成员访问。但是在 . 运算符左边的 new Foo() 显然不是个对象。但是它们执行后的结果是个对象呀。于是就先执行了 new Foo()
第二步, . 运算符对 new Foo() 的执行结果进行成员访问:
new Foo().getName第三步,() 运算符执行 . 运算符对 new Foo() 的执行结果进行成员访问的结果,即函数执行:
new Foo().getName() // alert(1)最后,得到结果:alert(1)
第二题:
第一步,先执行: . 运算符:
new Foo.getName() // => new (Foo.getName)(). 运算符的作用是成员访问。 . 运算符左边的 Foo 是个对象。于是执行成员访问
第二步, 执行 new 操作符,此使函数右边的括号变为构造函数的参数列表,优先级大于函数执行:
new getName() // alert(2)最后,得到结果:alert(1)
经过这波分析,我相信你应该对 JavaScript 操作符优先级有了一定的认识。现在再去看最最上面的那到面试题应该没什么问题了。
其实在开发中,我们还是要特别注意 JavaScript 操作符优先级的问题。比如下面这个场景:
const res = 10 + true ? 1 : 0结果是: 1,而不是 11