美文网首页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