这里有一道面试题:
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 ? 4 ? 2 ?报错
getName(); // ? 1
new Foo.getName(); // 2
new Foo().getName(); // 3
new new Foo().getName(); // 3
求问最后三个结果是怎么出来的,谢谢各位了。
1
czzhengkw 2019-06-11 17:11:21 +08:00 1
new Foo.getName() => 2
把 Foo.getName 当作构造函数执行 new Foo().getName() => 3 拆成两步: var foo = new Foo() => 实例化一个 Foo foo.getName() => 调用原型链上的 getName 方法 new new Foo().getName(); 拆成两步: var foo = new Foo() => 实例化一个 Foo new foo.getName() => 把原型链上的 getName 方法当作构造函数执行 执行下面这条语句就可以验证了 console.log(new new Foo().getName() instanceof Foo.prototype.getName) |
2
czzhengkw 2019-06-11 17:16:25 +08:00 1
var a = 1;
(function a(){ a = 2; console.log(a)})() a=2 是全局作用域的变量 当 console.log(a)的时候,会先在立即执行函数 a 的作用域里面找 a,如果找不到的话,就找上一层的作用域,即全局作用域 但是它在函数 a 的作用里找到了 a,即函数 a,所以把函数 a 打印了出来 |
4
wly19960911 2019-06-11 17:22:27 +08:00
|
5
palmers 2019-06-11 17:24:48 +08:00
@wly19960911 那时候还没有执行 当然是 1
|
6
littleylv 2019-06-11 17:27:48 +08:00
实际工作中这么写的人坟头草已经好几米高了
|
7
palmers 2019-06-11 17:29:16 +08:00
@wly19960911 对不起 我看错了 我这边也没有复现你这种情况呢?为什么
|
8
palmers 2019-06-11 17:31:16 +08:00
@wly19960911 我非常怀疑你的断点还没有进入匿名函数体 只是在外层
|
9
1KN6sAqR0a57no6s 2019-06-11 17:34:25 +08:00 2
第二条 V 站之前讨论过,也没有看到权威解释。可以看看我的研究过程(不保证正确) https://yux.me/p/8
|
10
MMDeJeVS3GtMVLeu 2019-06-11 17:35:31 +08:00
Foo.getName();
执行函数的静态方法,输出 2 getName(); 重点在于两种声明函数的方法的区别 解析器会先读取函数声明( console.log("5")),并使其在执行任何代码之前可以访问,在任何地方调用都不会有问题; 而函数表达式( console.log("4"))则必须等到解析器执行到它所在的代码行才会真正被执行,提前调用会报错。 这里输出 4 Foo().getName(); 执行 Foo 方法导致 window 上的(没有作用域限制,默认是 window ) getName 函数被重新定义 返回的 this 的值取决于执行的位置,此时返回 window, 执行 window.getName()返回 1 getName(); 即执行 window.getName(),结果同上 new Foo.getName(); 这里我也解释不清楚,调试了一下,和执行属性有关 Foo.getName()先与 new 关键字执行,相当于 new 对象的静态方法 返回 2 new Foo().getName(); 在 js 中,对象在调用一个方法时会首先在自身里寻找是否有该方法(对象方法),若没有,则去原型链(原型方法)上去寻找,依次层层递进 这里没有对象方法,执行原型方法,返回 3 new new Foo().getName(); 请赐教 PS:以前特意研究过这玩意儿,结果还是忘记了不少,话说面试考这个有那么大意义吗,毕竟可以背下来的东西 |
11
kyuuseiryuu 2019-06-11 17:36:10 +08:00
JS 这东西贼玄学,这些花里胡哨的东西了解一下就好了,掌握这么深怕你要么炫技到处写坑爹代码、要么接盘改别人坑爹代码。
JS 啥都好,就这些玩意儿令人诟病。 现实中谁要是这么写,头都要给人剁掉。 |
12
wly19960911 2019-06-11 17:36:37 +08:00
@palmers #8 进肯定进了,我确定下浏览器版本问题,
|
13
strcmp 2019-06-11 17:37:53 +08:00
做这种题简直是毒瘤
|
14
xiangyuecn 2019-06-11 17:38:26 +08:00
append 那段我居然没看懂😂 a=2 这句的这个 a 到底是谁😒???
不过:要是工作中经常会写出题目里面这些代码的人,bug 一定多(不通过运行来检查代码总有一天会被活活绕死)。反观不怎么写这种代码的人 bug 不一定会多。 题外话:虽然代码逻辑性很强,真的强。但命名啊,为什么非要挤着用一个名字?故意写 bug😂 番外篇:论学会合理命名的重要性。 |
15
wly19960911 2019-06-11 17:39:27 +08:00
@palmers 换了 49 版本(我们自己的测试版本)
var a = 1; (function a(){ a = 2; console.log(a);debugger})() 这段代码断点之后,a 仍旧是 1,什么鬼..... |
16
palmers 2019-06-11 17:49:22 +08:00
@wly19960911 变量的问题 你把函数名改一下
|
17
xiangyuecn 2019-06-11 17:50:26 +08:00
#14 配幅图😕
|
18
iccfish 2019-06-11 17:56:44 +08:00
Javascript: 我有一百种方法能把你玩死
|
20
xiangyuecn 2019-06-11 18:02:39 +08:00
#14 加 "use strict"; 就原形毕露了,原来 js 对这种写法自己都看下过去了。。😁😁😁
|
21
palmers 2019-06-11 18:04:26 +08:00
@wly19960911 我一直当做了匿名函数 没看见这是一个函数声明然后变为函数表达式, 所以才会出现 a =1
但是内部的执行逻辑我也没搞懂, 大意了没仔细看就当做匿名函数了 所以我复现不出来 , 我的猜测是 函数声明变为表达式的时候和全局变量冲突的处理方式不一样, 在 firefox 中 a 就是这个函数而不是 number chrome 中依然是 number chrome 也许是把这种冲突吃掉了 求大神解释一下 像这种函数声明变为表达式的执行步骤到底是什么样的 |
22
palmers 2019-06-11 18:12:26 +08:00
在 chrome 中调试: 我上面说错了 在 debugger 之前的 log chrome 并没有认为 a 是 number 最后打印出来的还是一个函数: ƒ a() {a = 2; console.log(a); debugger;}
但是 为什么断点的时候 a 是 number 1 呢? 只能是先后顺序的问题了 这时候还没有执行函数 a 我们之所以能看见 a 为 1 是因为浏览器断点调试器 把全部为 a 的变量当前的值显示出来了而已 |
23
dd31san 2019-06-11 18:30:14 +08:00 via iPhone
验证了下 append,输出是函数 a,全局 a 值为 1。
尝试修改函数名 a1,运行后全局 a 值变成 2,也就是在匿名函数里,给和函数同名的 a 赋值或声明失败?=) |
24
palmers 2019-06-11 18:31:38 +08:00
我说的好像也不对, 彻底 xx 了, 我执行下面的代码
var a = 1; (function a(f) { console.log('...', f); a = 2; console.log(a); })(function() { console.log(''); }); //输出这样的 ... ƒ () { console.log(''); } ƒ a(f) { console.log('...', f); a = 2; console.log(a); } 那个函数执行过又好像没有执行过 好奇怪 |
25
rabbbit 2019-06-11 18:36:53 +08:00 1
|
26
palmers 2019-06-11 18:39:16 +08:00
我有一个猜的解释, 第一对小括号,把函数声明变为函数表达式,所以 a 变为了
a = function a(f) { console.log('...', f); a = 2; console.log(a); }; 然后第二对小括号执行这个函数,所以有上面的打印,但是这时候 a 已经是一个函数了,所以打印了 a 函数体 那在 debugger 的时候 a 是 1 我解释不了 按道理说 只有执行函数的时候才会进入函数体的 只能去研究 chrome 的断掉调试器的机制了 |
28
wmhx 2019-06-11 18:50:40 +08:00
写这种代码的人, 能抗几刀?
|
29
c4f36e5766583218 2019-06-11 19:07:45 +08:00
1. https://blog.csdn.net/xjb19901008/article/details/24930793
hoisting 参考: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types ____1.1 function 提升 ____1.2 var 定义提升( ps: 还可以考 let, var, const 区别以及它们可否重复声明,还有直接赋值和语句块作用域;再还有严格模式) 2. this 的值 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this 3. prototype https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes 4. 运算符优先级 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence 所以可以理解为: ```JavaScript // part1 function function Foo() { getName = function() { console.log("1"); } return this; } function getName() { console.log("5"); } // part2 var var getName; // part3 Foo.getName = function() { console.log("2"); } Foo.prototype.getName = function() { console.log("3"); } getName = function() { console.log("4"); }; // part4 Foo.getName(); // 2 getName(); // 4 Foo().getName(); // 1,直接赋值是全局变量,这里 Foo()执行后返回的是 Window getName(); // 1,被上一句的 Foo()修改了 new Foo.getName(); // 2,new (Foo.getName)() new Foo().getName(); // 3,(new Foo()).getName() new new Foo().getName(); // 3,先 new 再.再 new ``` 第二题就是直接赋值和 https://developer.mozilla.org/en-US/docs/Glossary/IIFE |
30
c4f36e5766583218 2019-06-11 19:11:20 +08:00
@c4f36e5766583218 #29 哦,第二题,没看清,你当我没说。
|
31
wszgrcy 2019-06-11 19:46:41 +08:00 via Android
又看到这道为了考人而考人的提。。。主就是原型链和运算符顺序已经某些不规范的隐藏写法。。。。
|
32
AddOneG 2019-06-11 22:57:23 +08:00
题 1 没啥好讲的。
题 2:非匿名自执行函数,函数变量为 只读 状态,无法修改。 |