• 每日20道面试题带解析 — (21


    答案在问题下方的折叠部分,点击即可展开问题。祝你好运 ❤️

    以下题目,本人验证无误,查阅了相关资料,得出解析部分并做了相关总结,希望对正准备跳槽或找工作的你有帮助!

    1. 写出执行结果,并解释原因

    var a = [0];
    if ([0]) {
      console.log(a == true);
    } else {
      console.log("wut");
    }
    // 输出什么?
    
    点此查看答案及解析
    
    答案 : false
    解析 : if(condition)判断时,会把condition转换成boolean然后做判断,[0]是一个有值的list,所以转成boolean是true, 
           而A == B的比较时,如果A和B的类型不一样,会先把A和B转化成相同的type,通常转为number
      //分成以下步骤
      //把true转化成number,true变成1
      [0] == 1;
      //list是object
      //先看[0].valueOf(),结果还是[0]
      //再看[0].toString(),结果是“0” type是string
      "0" == 1; 
      //把“0” string转化成number,“0”变成0,0不等于1
      0 == 1; //结果是false
    
    

    2. 写出执行结果,并解释原因

    [1 < 2 < 3, 3 < 2 < 1]    //解析成什么?
    
    点此查看答案及解析
    
    答案 : [true,true]
    解析 : 运算符优先级,分步解析
      1 < 2 < 3 => true < 3  => 1 < 3 => true 
      3 < 2 < 1 => false < 1 => 0 < 1 => true
    
    

    3. 写出执行结果,并解释原因

    function foo() { }
    var oldName = foo.name;
    foo.name = "bar";
    [oldName, foo.name]
    
    点此查看答案及解析
    
    答案 : ['foo','foo']
    解析 : 函数的name是只读属性不可修改 
    
    

    4. 写出执行结果,并解释原因

    var lowerCaseOnly =  /^[a-z]+$/;
    [lowerCaseOnly.test(null), lowerCaseOnly.test()]
    
    点此查看答案及解析
    
    答案 : [true, true]
    解析 : 正则容易忽视的坑,test在检测时会隐性将内容转为字符串,其实等同于:[lowerCaseOnly.test('null'), lowerCaseOnly.test('undefined')]
    
    

    5. 写出执行结果,并解释原因

    if ('http://giftwrapped.com/picture.jpg'.match('.gif')) {
      console.log('a gif file')
    } else {
      console.log('not a gif file')
    }
    
    点此查看答案及解析
    
    答案 : 'a gif file'
    解析 : 正则的隐式转换,match方法第一个参数接收一个正则表达式或者一个字符串,但如果是字符串会隐式转为正则,
      所以上述代码等同于:'http://giftwrapped.com/picture.jpg'.match(/.gif/)
      而在正则中 点 . 表示通配符,所以成功匹配到/gif,匹配成功,输出a gif file。
    
    

    6. 写出执行结果,并解释原因

    function user(obj) {
      obj.name = "北京"
      obj = new Object()
      obj.name = "上海"
    } 
    let person = new Object();
    user(person);
    console.log(person.name);
    
    点此查看答案及解析
    
    答案 : 北京
    解析 : 对象作为参数,传递进去的是这个对象的地址,
     1. obj.name是给person这个对象赋值;
     2. obj = new Object(),把obj指向另一个对象,
     3. obj.name现在是给这个新对象赋值,不影响person这个变量指向的对象;
     4. 两个obj指向的对象的引用地址不同。
    所有函数的参数都是按值传递的。
     5. 基本类型的传递同基本类型变量的赋值一样,按值传递,在函数体内修改参数的值,不会影响到函数外部。
     6. 引用类型的值传递同引用类型变量的赋值一样,按引用传递,传入函数的是原始值的地址,因此在函数内部修改参数,将会影响到原始值。
    
    

    7. 写出执行结果,并解释原因

    let x, y;
    try {
        throw new Error();
    } 
    catch (x) {
        x = 1;
        y = 2;
        var a = 3
    	
        console.log(a);  // ?
        console.log(x);  // ?
    }
    console.log(a);  // ?
    console.log(x);  // ?
    console.log(y);  // ?
    
    点此查看答案及解析
    
    答案 : 3  1  3  undefined  2
    解析 : 
      1. catch的作用域,其实并不是常见的块级作用域,且不能绑定自己的内部声明的变量(如a)。
      2. catch创建的块作用域,只对catch的参数x有效。
      3. 对于在内部声明的变量,catch并没有创建一个新的作用域,只是一个普通的代码块。因此块外仍可访问
    
    

    8. 写出执行结果,并解释原因

    function fn() {
        getValue = function () { console.log(1); };
        return this;
    }
    fn.getValue = function () { console.log(2);};
    fn.prototype.getValue = function () {console.log(3);};
    var getValue = function () {console.log(4);};
    function getValue() {console.log(5);}
    
     
    getValue();           // ?
    fn().getValue();      // ?
    getValue();           // ?
    new fn.getValue();    // ?
    new fn().getValue();  // ?
    
    
    点此查看答案及解析
    
    答案 : 4  1  1  2  3
    考察 : 变量定义提升、this指向、运算符优先级、原型、继承、全局变量污染、对象属性及原型属性优先级
    解析 : 为强调重点内容,在下方使用标记语言描述。
    
    

    解析:
    第一问 getValue():

    1. 直接调用,关注点在4,5上:
    2. JS存在一种变量声明被提升的机制,函数声明会被提升到作用域的最前面,即使写在最后,也还是会被提升至最前面。
    3. 函数表达式和函数声明的区别,函数声明解析时会提升,函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用
    4. 函数声明5被函数表达式4覆盖输出 4

    第二问 fn().getValue():

    1. 执行fn函数,调用fn函数返回值对象的 getValue 属性函数
    2. 此时 getValue 函数没有用var进行声明,已将外层作用域的getValue函数修改;
    3. fn函数返回this,此时函数执行确定this指向window对象,相当于执行window.getValue(),而getValue已经被修改成console.log(1), 输出 1

    第三问 getValue():

    1. 执行完第6步,getValue函数已被修改,console.log(1), 输出 1

    第四问 new fn.getValue():

    1. 考察JS的运算符优先级问题,
    2. 点的优先级高于new无参数列表,相当于new (fn.getValue())
    3. 当点运算完后有个括号(),此时就是变成new有参数列表,优先级高于函数执行,所以直接执行new。这也是为什么遇到()不先函数调用再new。
    4. 最终相当于将 getValue函数,作为构造函数来执行, 输出 2

    第五问 new fn().getValue()

    1. 这里带括号是new 有参数列表,new有参数列表的优先级与点的优先级相同,按从左到右的顺序执行。
    2. 先执行有参数列表,再执行点的优先级,最后再函数调用
    3. fn作为构造函数有返回值,在JS中构造函数的返回值可有可无
      • 没有返回值:返回实例化的对象
      • 有返回值:检查其返回值是否为引用类型。
        • 非引用类型:基本类型则与无返回值相同,实际返回其实例化对象。
        • 引用类型:实际返回值为这个引用类型
    4. fn 函数返回的是this,this在构造函数中本来就代表当前实例化对象, 最终fn返回实例化对象。调用对象的getValue方法,而构造函数中没有getValue,调用原型对象(prototype)上的getValue函数。输出 3

    9. 写出执行结果,并解释原因

    let length = 10;
    function fn() {
    	console.log(this.length);
    }
    var obj = {
    	length: 5,
    	method: function (fn) {
    		fn();
    		arguments[0]();
    	}
    };
    obj.method(fn, 1);
    
    点此查看答案及解析
    
    答案 : 0  2 
    解析 : 为强调重点内容,在下方使用标记语言描述。  
    
    

    解析
    第一问:fn()

    1. 任意函数里嵌套非箭头函数,嵌套函数内this 在未指定的情况下,指向的是 window 对象,这里执行 fn会打印window.length,
    2. let声明的变量有形成块级作用域,且不存在声明提升,length属性并没有挂载到window对象中。(test:let a = 1; window.a // undefined)
    3. 此时打印的便是window自带的length属性,表示iframe个数,默认为0。输出 0

    第二问:arguments[0]()

    1. arguments类数组是函数参数的引用, arguments[0]指向 fn,
    2. arguments[0]() 是作为 arguments对象的属性[0]来调用 fn的,谁调用 this 就指向谁;所以 fn 中的 this 指向arguments(对象的属性调用方法,this指向该对象)
    3. arguments有两个参数,fn和1,因此argumengts.length = 2 输出 2

    扩展

    1. [function fn(){console.log(this.length)}][0](); // 1
    2. 数组也是对象,调用数组对象的0属性,函数作为数组对象的属性调用,函数中的this 当然指向这个数组,所以返回数组的length

    10. 写出执行结果,并解释原因

    var a=10;
    var foo={
      a:20,
      bar:function(){
          var a=30;
          return this.a;
        }
    }
    console.log(foo.bar());             // ?
    console.log((foo.bar)());           // ?
    console.log((foo.bar=foo.bar)());   // ?
    console.log((foo.bar,foo.bar)());   // ?
    
    点此查看答案及解析
    
    答案 : 20 20 10 10
    解析 : 为强调重点内容,在下方使用标记语言描述。
    
    

    本题主要考察this指向问题,推荐博文:

    一文搞懂 this、apply、call、bind: https://www.cnblogs.com/echoyya/p/14506269.html

    JS五种绑定彻底弄懂this: https://www.cnblogs.com/echoyya/p/14506742.html

    解析
    第一问 foo.bar()

    1. foo调用,this指向foo , 输出 20

    第二问 (foo.bar)()

    1. 表达式加了括号,括号的作用是改变表达式的运算顺序,而在这加与不加并无影响,相当于foo.bar(), 输出 20

    第三问 (foo.bar=foo.bar)()

    1. 等号运算相当于重新给foo.bar定义,相当于一个匿名函数赋值给一个全局变量,foo.bar是在window作用域下,this指代的是window,输出 10

      foo.bar = function () {
      	var a = 10;
      	return this.a;
      }
      

    第四问 (foo.bar,foo.bar)()

    1. 逗号运算符求解过程是:先计算表达式1的值,再计算表达式2的值,……一直计算到表达式n的值,最后整个逗号运算符的返回值是最后一个表达式的值。
    2. 经过逗号运算符后,就是纯函数,不再是对象方法的引用,所以this指向window,输出 10
    3. 技巧:经过赋值,运算符运算后,都是纯函数,不是对象方法的引用。函数中this指向都是windows。

    11. 写出执行结果,并解释原因

    function getName(){
      return
      {
        name:'Echoyya'
      }
    }
    console.log(getName());     // ?
    
    点此查看答案及解析
    
    答案 : undefined
    解析 : 如果continue、break、return、throw 这四个语句后面,直接跟换行符,则会自动添加分号。 
    
    

    12. 写出执行结果,并解释原因

    const num = parseInt("2*4",10);
    
    console.log(num);      // ?
    
    点此查看答案及解析
    
    答案 : 2
    解析 : parseInt会检查字符串中的字符是否合法. 一旦遇到一个在指定进制(第二个参数)中不合法的字符后,立即停止解析并且忽略后面所有的字符。
        *为非法数字。所以只解析到 2,并将其解析为十进制的2. 值即为 2
    
    

    13. 写出执行结果,并解释原因

    var x = 20;
    var temp = {
      x: 40,
      foo: function () {
        var x = 10;
        console.log(this.x);
      }
    };
    (temp.foo, temp.foo)();    // ?
    
    
    点此查看答案及解析
    
    答案 : 20
    技巧 : 经过赋值,运算符运算后,都是纯函数,不是对象方法的引用。函数中this指向都是windows。
    解析 : 逗号操作符会从左到右计算它的操作数,返回最后一个操作数的值。所以(temp.foo, temp.foo)();等价于var fun = temp.foo; 
          fun();fun调用时this指向window,因此返回 20。
    
    

    14. 写出执行结果,并解释原因

    const company = { name: "Echoyya" };
    Object.defineProperty(company, "address", { value: "北京" });
    
    
    console.log(company);               // ?
    console.log(Object.keys(company));  // ?
    
    点此查看答案及解析
    
    答案 : {name:"Echoyya",address:"北京"},  ["name"]
    解析 : defineProperty方法可以给对象添加一个新属性,或者修改已经存在的属性。而使用defineProperty给对象添加属性之后,属性默认为不可枚举,
         Object.keys方法仅返回对象中可枚举的属性,因此只打印name
    
    

    15. 写出执行结果,并解释原因

    let num = 10;
    const inNum = () => num++;
    const inPaNum = number => number++;
    const num1 = inNum();
    const num2 = inPaNum(num1);
    
    console.log(num1);  // ?
    console.log(num2);  // ?
    
    点此查看答案及解析
    
    答案 : 10 10 
    解析 : 一元操作符 ++ 先返回操作值, 再执行自增操作值。
        1. num1 是10,因为 inNum 函数先返回 num 的值,在执行 num 自增
        2. num2 是10,因为 num1 作为参数传入 inPaNum,同理函数先返回 number 的值,在执行 number 自增
     
    

    16. 写出执行结果,并解释原因

    const value = { number: 10 };
    const multiply = (x = { ...value }) => {
      console.log(x.number *= 2);
    };
    
    multiply();      // ?
    multiply();      // ?
    multiply(value); // ?
    multiply(value); // ?
    
    点此查看答案及解析
    
    答案 : 20 20 20 40
    解析 : 
      1. ES6中可以使用参数默认值, 函数未传参,或参数为undefined,将使用参数默认值。
      2. 解构 value 对象并赋值给一个新对象,因此 x 的默认值为 {number:10} 。	
      3. 默认参数在调用时才会计算,每次调用函数,都会创建一个新的对象。调用 multiply(),x的默认值都为 {number:10},因此输出 20 
      4. 调用 multiply(value),实际上修改了 value.number的值,输出 20
      5. 再次调用调用 multiply(value),value.number之前被修改为 20,因此输出 40。
    
    

    17. 写出执行结果,并解释原因

    // index.js
    console.log('running index.js');
    import { sum } from './sum.js';
    console.log(sum(1, 2));
    
    // sum.js
    console.log('running sum.js');
    export const sum = (a, b) => a + b;
    
    
    点此查看答案及解析
    
    答案 : running sum.js,  running index.js,  3
    解析 : import命令是编译阶段执行的,在运行之前。因此被导入的模块会先运行,而导入模块的文件会后执行。
        这是CommonJS 中 require() 和 import之间的区别。require()可以在运行代码时按需加载。
        如果使用 require,那么running index.js、running sum.js、 3会被依次打印。
    
    

    18. 写出执行结果,并解释原因

    function addToList(item, list) {
    	return list.push(item);
    }
    const result = addToList("company", ["yideng"]);
    console.log(result);    // ?
    
    点此查看答案及解析
    
    答案 : 2
    解析 : push()方法返回新数组的长度。若想返回新数组,应该在push之后返回list。
    
    

    19. 实现(5).add(3).minus(2) 功能

    // 实现 (5).add(3).minus(2) 功能
    
    console.log((5).add(3).minus(2)); // 6
    
    点此查看答案及解析
    
    答案 : 
        Number.prototype.add = function (number) {
            if (typeof number !== 'number') {
                throw new Error('请输入数字~');
            }
            return this + number;
        };
        Number.prototype.minus = function (number) {
            if (typeof number !== 'number') {
                throw new Error('请输入数字~');
            }
            return this - number;
        };
        console.log((5).add(3).minus(2));    // 6
    
    

    20. 不使用模运算符的情况下,检查一个数是否是偶数

    isEven(num)   // true Or false
    
    点此查看答案及解析
    
    答案 : 
    1)递归方式
      function isEven(num){
        const number = Math.abs(num);   // 取绝对值
        if(number === 1)  return false;
        if(number == 0 )  return true;
        return isEven(number -2);
      }
    -------------------------------------------------
    2)通过Math.round,利用奇数除以2会有小数的特点
      function isEven(num){
        return parseInt(num/2) === Math.round(num/2);
      }
    
    
    作者:Echoyya
    著作权归作者和博客园共有,商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    c++ 中bool 的默认值
    cocos2d CCLOG格式符号表
    c++数组指针bug
    cocos2d-x-2.2.6创建工程
    Nape实现坐标旋转角度回弹
    haxe 中使用音效
    haxe 嵌入swf 读取里面的内容
    haxe 配置
    Spring Tool Suite(STS)基本安装配置
    git提交忽略文件.gitignore内容
  • 原文地址:https://www.cnblogs.com/echoyya/p/14550258.html
Copyright © 2020-2023  润新知