V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
noviceiOS
V2EX  ›  JavaScript

一道 javascript 面试题求教

  •  
  •   noviceiOS · 2019-06-11 15:21:10 +08:00 · 4990 次点击
    这是一个创建于 1994 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这里有一道面试题:

    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 条附言  ·  2019-06-11 16:32:43 +08:00
    还有一道也不懂:
    var a = 1;
    (function a(){
    a = 2;
    console.log(a)})()
    32 条回复    2019-06-11 22:57:23 +08:00
    czzhengkw
        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)
    czzhengkw
        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 打印了出来
    noviceiOS
        3
    noviceiOS  
    OP
       2019-06-11 17:20:37 +08:00
    @czzhengkw

    a=2 为什么是全局作用域的变量啊?
    wly19960911
        4
    wly19960911  
       2019-06-11 17:22:27 +08:00
    @czzhengkw #2 但是调试的时候,你发现 console.log(a)里面的 a 还是 number 值

    palmers
        5
    palmers  
       2019-06-11 17:24:48 +08:00
    @wly19960911 那时候还没有执行 当然是 1
    littleylv
        6
    littleylv  
       2019-06-11 17:27:48 +08:00
    实际工作中这么写的人坟头草已经好几米高了
    palmers
        7
    palmers  
       2019-06-11 17:29:16 +08:00
    @wly19960911 对不起 我看错了 我这边也没有复现你这种情况呢?为什么
    palmers
        8
    palmers  
       2019-06-11 17:31:16 +08:00
    @wly19960911 我非常怀疑你的断点还没有进入匿名函数体 只是在外层
    1KN6sAqR0a57no6s
        9
    1KN6sAqR0a57no6s  
       2019-06-11 17:34:25 +08:00   ❤️ 2
    第二条 V 站之前讨论过,也没有看到权威解释。可以看看我的研究过程(不保证正确) https://yux.me/p/8
    justyeh
        10
    justyeh  
       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:以前特意研究过这玩意儿,结果还是忘记了不少,话说面试考这个有那么大意义吗,毕竟可以背下来的东西
    kyuuseiryuu
        11
    kyuuseiryuu  
       2019-06-11 17:36:10 +08:00
    JS 这东西贼玄学,这些花里胡哨的东西了解一下就好了,掌握这么深怕你要么炫技到处写坑爹代码、要么接盘改别人坑爹代码。

    JS 啥都好,就这些玩意儿令人诟病。

    现实中谁要是这么写,头都要给人剁掉。
    wly19960911
        12
    wly19960911  
       2019-06-11 17:36:37 +08:00
    @palmers #8 进肯定进了,我确定下浏览器版本问题,
    strcmp
        13
    strcmp  
       2019-06-11 17:37:53 +08:00
    做这种题简直是毒瘤
    xiangyuecn
        14
    xiangyuecn  
       2019-06-11 17:38:26 +08:00
    append 那段我居然没看懂😂 a=2 这句的这个 a 到底是谁😒???

    不过:要是工作中经常会写出题目里面这些代码的人,bug 一定多(不通过运行来检查代码总有一天会被活活绕死)。反观不怎么写这种代码的人 bug 不一定会多。

    题外话:虽然代码逻辑性很强,真的强。但命名啊,为什么非要挤着用一个名字?故意写 bug😂

    番外篇:论学会合理命名的重要性。
    wly19960911
        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,什么鬼.....
    palmers
        16
    palmers  
       2019-06-11 17:49:22 +08:00
    @wly19960911 变量的问题 你把函数名改一下
    xiangyuecn
        17
    xiangyuecn  
       2019-06-11 17:50:26 +08:00
    #14 配幅图😕

    iccfish
        18
    iccfish  
       2019-06-11 17:56:44 +08:00
    Javascript: 我有一百种方法能把你玩死
    czzhengkw
        19
    czzhengkw  
       2019-06-11 17:59:38 +08:00
    @noviceiOS 我想得太简单了
    xiangyuecn
        20
    xiangyuecn  
       2019-06-11 18:02:39 +08:00
    #14 加 "use strict"; 就原形毕露了,原来 js 对这种写法自己都看下过去了。。😁😁😁

    palmers
        21
    palmers  
       2019-06-11 18:04:26 +08:00
    @wly19960911 我一直当做了匿名函数 没看见这是一个函数声明然后变为函数表达式, 所以才会出现 a =1
    但是内部的执行逻辑我也没搞懂, 大意了没仔细看就当做匿名函数了 所以我复现不出来 , 我的猜测是 函数声明变为表达式的时候和全局变量冲突的处理方式不一样, 在 firefox 中 a 就是这个函数而不是 number chrome 中依然是 number chrome 也许是把这种冲突吃掉了
    求大神解释一下
    像这种函数声明变为表达式的执行步骤到底是什么样的
    palmers
        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 的变量当前的值显示出来了而已
    dd31san
        23
    dd31san  
       2019-06-11 18:30:14 +08:00 via iPhone
    验证了下 append,输出是函数 a,全局 a 值为 1。
    尝试修改函数名 a1,运行后全局 a 值变成 2,也就是在匿名函数里,给和函数同名的 a 赋值或声明失败?=)
    palmers
        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);
    }

    那个函数执行过又好像没有执行过 好奇怪
    palmers
        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 的断掉调试器的机制了
    palmers
        27
    palmers  
       2019-06-11 18:42:42 +08:00
    @rabbbit 谢谢 这个解释可以
    wmhx
        28
    wmhx  
       2019-06-11 18:50:40 +08:00
    写这种代码的人, 能抗几刀?
    c4f36e5766583218
        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
    c4f36e5766583218
        30
    c4f36e5766583218  
       2019-06-11 19:11:20 +08:00
    @c4f36e5766583218 #29 哦,第二题,没看清,你当我没说。
    wszgrcy
        31
    wszgrcy  
       2019-06-11 19:46:41 +08:00 via Android
    又看到这道为了考人而考人的提。。。主就是原型链和运算符顺序已经某些不规范的隐藏写法。。。。
    AddOneG
        32
    AddOneG  
       2019-06-11 22:57:23 +08:00
    题 1 没啥好讲的。
    题 2:非匿名自执行函数,函数变量为 只读 状态,无法修改。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1034 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 20:39 · PVG 04:39 · LAX 12:39 · JFK 15:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.