美文网首页javaScript
典型的JavaScript面试题(附答案)

典型的JavaScript面试题(附答案)

作者: flyingjimmy | 来源:发表于2019-05-30 11:30 被阅读0次

    原文:https://dev.to/maxpou/typical-javascript-interview-exercises-explained

    题目一

    下面的代码,我想要打印出hey amy,结果却打印出hey arnold,为什么?

    function greet (person) {
      if (person == { name: 'amy' }) {
        return 'hey amy'
      } else {
        return 'hey arnold'
      }
    }
    greet({ name: 'amy' })
    
    答案

    这里问题出在{ name: 'amy' } != { name: 'amy' }。当比较两个对象时,JavaScript都会比较对象在内存中的引用地址。这个例子中,两个对象虽然都有相同的属性和值,但它们在内存中地址是不同的,所以是两个不同的对象。

    正确的解决方法应该是比较对象属性的值:

    function greet (person) {
      if (person.name === 'amy') {
        return 'hey amy'
      }
      return 'hey arnold'
    }
    greet({ name: 'amy' }) // "hey amy"
    

    题目二

    我想让下面的代码按顺序输出0,1,2,3,但是运行结果却不符合我的预期,为什么以及怎么解决?

    for (var i = 0; i < 4; i++) {
      setTimeout(() => console.log(i), 0)
    }
    
    问题

    我喜欢这道题,因为它有点棘手,并且它涉及到作用域和JavaScript的事件循环。

    这里的陷阱是零延迟。setTimeout(callback, 0)并不意味着callback函数会在0毫秒后执行。

    下面是事件循环的经过:

    1. 当前调用栈设为第一个setTimeout()
    2. window.setTimeout()是一个web api,在经过0毫秒后,回调函数(在这里是匿名函数)会被放到队列中,而不是调用栈中。
    3. 当调用栈空闲时,for循环会将第二个setTimeout放进来,然后将第二个callback放到队列中。。。直到循环结束。
    4. 当for循环结束i === 4时,JS就会执行队列里的callback函数了。每一个console.log(i)会将i打印出来,也就是4。

    用动画演示整个事件循环的过程:Loupe (try it it's fun!)

    还有一个问题是变量i的作用域的问题,我们可以看到4个setTimeout函数都共享着同一个i = 4(并不是第一个setTimeout的i = 0,第二个i = 1等等)

    答案

    针对以上问题我们有不同的解决方案:

    • 使用立即自执行函数(IIFE),这种“包裹”函数会在它被定义时立即执行:
    for (let i = 0; i < 4; i++) {
      (function (i) {
        setTimeout(() => console.log(i), 0)
      })(i)
    }
    
    • 用let代替var。let作用于块作用域,能让作用域更容易理解。
    for (let i = 0; i < 4; i++) {
      setTimeout(() => console.log(i), 0)
    }
    

    题目三

    我想让下面的代码输出doggo,但结果却输出undefined!

    let dog = {
      name: 'doggo',
      sayName () {
        console.log(this.name)
      }
    }
    let sayName = dog.sayName
    sayName()
    
    答案

    上面的代码执行返回undefined,为什么呢?首先,我们定义了一个对象名叫dog,这个对象有两个属性,分别是name和sayName方法。然后我们再定义了一个变量叫做sayName,并将dog对象的sayName方法赋值给它。最后在当前环境也即是全局环境执行sayName方法,这时函数里的this指向的是window,而window对象下面并没有name属性,所以返回undefined.

    • 如果我们要访问dog对象的name属性,我们就要将sayName的执行环境变成dog:
    sayName.bind(dog)()
    // 或者:
    dog.sayName.bind(dog)()
    
    • 直接在dog对象上调用sayName方法:
    let dog = {
      name: 'doggo',
      sayName () {
        console.log(this.name)
      }
    }
    dog.sayName() // will log "doggo"
    

    题目四

    我想执行bark方法,但却报错了,为什么?

    function Dog (name) {
      this.name = name
    }
    Dog.bark = function () {
      console.log(this.name + ' says woof')
    }
    let fido = new Dog('fido')
    fido.bark()
    
    答案

    执行上面代码,我们会得到如下错误:TypeError: fido.bark is not a function. 上面的代码我们给函数Dog(同时也是构造器)增加一个bark方法。这在JavaScript中是可以的,因为函数也是对象。(译者补充:bark是Dog的静态方法,fido是Dog的实例对象,实例对象无法访问构造器的静态方法)。

    • 虽然fido.bark不是一个方法,但Dog.bark是,所以我们可以用之前的方法来处理,就是用function.prototype.bind来改变this的指向:
    var boundedBark = Dog.bark.bind(fido)
    boundedBark() // "fido says woof"
    
    • 将bark方法放在Dog的原型上:
    function Dog (name) {
      this.name = name
    }
    
    Dog.prototype.bark = function () {
      console.log(this.name + ' says woof')
    }
    
    let fido = new Dog('fido')
    fido.bark() // "fido says woof"
    

    我们还可以使用ES2015中的class关键字,但其实它是上面代码的语法糖。

    class Dog {
      constructor (name) {
        this.name = name
      }
    
      bark () {
        console.log(this.name + ' says woof')
      }
    }
    
    let fido = new Dog('fido')
    fido.bark() // "fido says woof"
    

    题目五

    为什么下面代码执行的结果是这样的?

    function isBig (thing) {
      if (thing == 0 || thing == 1 || thing == 2) {
        return false
      }
      return true
    }
    isBig(1)    // false
    isBig([2])  // false
    isBig([3])  // true
    
    答案

    因为我们在这里使用了==而不是严格相等===,也就是不进行类型的比较:

    • 调用isBig(1),这时thing == 1,结果符合我们的预期,返回false。
    • 调用isBig([2]),thing == [2],这时发生了什么呢?当比较一个数组和数字时,数组会转化成数字。这是Abstract Equality Comparison Algorithm的一部分,所以[2] == 2。

    我们应该避免使用==

    // weird results
    [] == ![]     // true
    [] == false   // true
    
    // Non transitive relation
    "1" == true   // true
    "01" == true  // true
    "01" == "1"   // false
    

    题目六(彩蛋无答案)

    如何让heroes数组不可变?

    const heroes = [
      { name: 'Wolverine',      family: 'Marvel',    isEvil: false },
      { name: 'Deadpool',       family: 'Marvel',    isEvil: false },
      { name: 'Magneto',        family: 'Marvel',    isEvil: true  },
      { name: 'Charles Xavier', family: 'Marvel',    isEvil: false },
      { name: 'Batman',         family: 'DC Comics', isEvil: false },
      { name: 'Harley Quinn',   family: 'DC Comics', isEvil: true  },
      { name: 'Legolas',        family: 'Tolkien',   isEvil: false },
      { name: 'Gandalf',        family: 'Tolkien',   isEvil: false },
      { name: 'Saruman',        family: 'Tolkien',   isEvil: true  }
    ]
    
    const newHeroes = heroes.map(h => {
      h.name = h.name.toUpperCase()
      return h
    })
    

    你的答案是什么?

    相关文章

      网友评论

        本文标题:典型的JavaScript面试题(附答案)

        本文链接:https://www.haomeiwen.com/subject/vqertctx.html