【前端面试篇】JavaScript基础(二)

前言

上一篇文章中详细介绍了JavaScript中重要的概念,涉及到闭包、原型、作用域、执行上下文等知识点,本篇文章将注重总结JavaScript中手写代码的一些面试题。

问题1:JavaScript中模块加载方案有哪些?

  • CommonJS:通过require来引入模块,通过module.exports定义模块的输出接口。这种模块加载方案是服务端的解决方案,它是以同步的方式引入模块的,因为在服务端文件都是存储在本地磁盘,所以读取非常快,所以采用同步的方式加载是没有问题的,但是如果在浏览器端,由于模块的加载需要网络,所以采用异步加载方式更加合适。
  • AMD:这种方案是采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的我遇见都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js实现了AMD规范。
  • CMD:这种方案和AMD方案都是为了解决异步模块加载的问题,sea.js实现了CMD规范,他和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。
  • ES6提出的方案:使用import和export的形式来导入导出模块。这种方案和上面三种方案不同。

衍生问题1:AMD和CMD的区别是什么?

  1. 在模块定义时对依赖的处理不同。AMD推崇依赖前置,在定义模块的时候要声明其依赖的模块;CMD推崇就近依赖,只有在用在某个模块的时候才去require。
  2. 对依赖模块执行时机的处理不同。AMD在依赖模块加载完成后直接执行依赖模块,依赖的执行顺序和属性顺序不一定一致。CMD在依赖模块加载完成后并不执行,只是下载。等到所哦呦哦的依赖模块都加载好之后,进入回调的函数逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序就和我们书写的顺序保持一致了。

问题2:in和hasOwnProperty的区别

  • in操作符:检测指定对象原型链上是否有对应的属性。
  • hasOwnProperty:检测指定对象自身上是否有对应的属性值。
  • 二者的区别在于in会查找原型链,而hasOwnProperty不会。

问题3:深度克隆

我之前专门写过一篇关于深度克隆的文章:面试官:能否用JavaScript实现深度克隆,大家可以去阅读这篇文章。

    function DeepClone(source) {
      // 判断目标是数组还是对象
      const targetObj = source.constructor === Array ? [] : {}
      for (let key in source) {
        if (source.hasOwnProperty(key)) {
          //   如果是对象就递归
          if (source[key] && typeof source[key] === 'object') {
            targetObj[key] = source[key].constructor === Array ? [] : {}
            targetObj[key] = DeepClone(source[key])
          } else {
            targetObj[key] = source[key]
          }
        }
      }
      return targetObj
    }

问题4:如何在ES5的环境下模拟const?

      function _const(key, value) {
        Object.defineProperty(window, key, {
          enumerable: false,
          configurable: false,
          get() {
            return value
          },
          set(data) {
            if (data !== value) {
              throw new TypeError('error')
            }
            return value
          }
        })
      }
      _const('a',2)
      console.log(a)

问题5:模拟实现call函数

// ES6语法 
 Function.prototype.call2 = function(content, ...keys) {
        content = content || window
        content.fn = this
        const result = content.fn(...keys)
        delete content.fn
        return result
      }


// ES5语法
 Function.prototype.call3 = function(content, ...keys) {
        content = content || window
        content.fn = this
        let args = []
        for (let i = 1; i < arguments.length; i++) {
          args.push('arguments[' + i + ']')
        }
        let result = eval('content.fn(' + args + ')')
        delete content.fn
        return result
      }

问题6:模拟实现apply函数

// ES6语法
      Function.prototype.apply2 = function(content, keys) {
        content = content || window
        content.fn = this
        const result = content.fn(...keys)
        delete content.fn
        return result
      }

// ES5语法

      Function.prototype.apply3 = function(content, keys) {
        content = content || window
        content.fn = this
        let result = eval('content.fn(' + keys + ')')
        delete content.fn
        return result
      }

问题7:模拟实现bind函数

      Function.prototype.bind2 = function(content, ...keys) {
        content = content || window
        const self = this
        function fn() {}
        let bound = function(...keys2) {
          self.apply(
            this instanceof self ? this : content,
            [].concat(keys, keys2)
          )
        }
        fn.prototype = this.prototype
        bound.prototype = this.prototype
        return bound
      }

      var a = '2'
      var obj = { a: 1 }
      function fn(b, c, d) {
        console.log(this.a, b, c, d)
      }
      var fn2 = fn.bind2(obj, 1, 2)
      fn2(4)

 可以参考这篇文章:面试官问:能否模拟实现JS的bind方法

问题8:模拟实现new操作符

      function newObj(ctor, ...keys) {
        if (typeof ctor !== 'function')
          throw new Error('ctor is not a function')
        // new.target 指向构造函数
        newOperator.target = ctor
        const obj = Object.create(ctor.prototype)
        const result = ctor.apply(obj, keys)
        const isObject = result !== null && typeof result !== 'object'
        const isFunction = typeof result !== 'function'
        if (isObject || isFunction) {
          return result
        } else {
          return obj
        }
      }

 可以参考这篇文章:面试官问:能否模拟实现JS的new操作符

问题9:实现数组的扁平化

 对于[1, [1, 2], [1, 2, [4, 4, 4]]]这样多层嵌套的数组,我们如何将其扁平化为[1, 1, 2, 1, 2, 4,4,4]这样的一维数组呢:

// 方法1
      const arr = [1, [1, 2], [1, 2, [4, 4, 4]]]
      const test = arr.flat(Infinity)
      console.log(test)   //[1, 1, 2, 1, 2, 4, 4, 4]

// 方法2
      const test = JSON.stringify(arr).replace(/(\[|\])/g, '')
      console.log('[' + test + ']')

// 方法3
      function flat(target) {
        return target.reduce(function(arr, item) {
          return [].concat(arr, typeof item === 'object' ? flat(item) : item)
        }, [])
      }
      console.log(flat(arr))

问题10:模拟实现promise

      class MyPromise {
        constructor(executor) {
          // 初始化状态
          this.state = 'pending'
          // 保存成功的值
          this.value = undefined
          // 保存成功的回调函数
          this.onFulfilledCallBacks = []
          // 保存失败的值
          this.reason = undefined
          // 保存失败的回调函数
          this.onRejectedCallBacks = []
          //   成功后调用的函数
          let resolve = value => {
            if ((this.state = 'pending')) {
              this.state = 'fulfilled'
              this.value = value
              this.onFulfilledCallBacks.forEach(item => item())
            }
          }
          //   失败后调用的函数
          let reject = value => {
            if ((this.state = 'pending')) {
              this.state = 'rejected'
              this.reason = value
              this.onRejectedCallBacks.forEach(item => item())
            }
          }
          //   如果executor报错,直接reject
          try {
            executor(resolve, reject)
          } catch (error) {
            reject(error)
          }
        }

        then(onFulfilled, onRejected) {
          if (this.state === 'fulfilled') {
            onFulfilled(this.value)
          }
          if (this.state === 'rejected') {
            onRejected(this.reason)
          }
          if (this.state === 'pending') {
            this.onFulfilledCallBacks.push(() => {
              onFulfilled(this.value)
            })
            this.onRejectedCallBacks.push(() => {
              onRejected(this.reason)
            })
          }
        }
      }

      let promise = new MyPromise(function(resolve, reject) {
        setTimeout(function() {
          reject(213213123)
        }, 2000)
      })
      promise.then(
        function(value) {
          console.log(value)
        },
        function(error) {
          console.log(error)
        }
      )

推荐大家阅读这篇文章:BAT前端经典面试问题:史上最最最详细的手写Promise教程

问题11:ES5如何实现继承?

这里就不详细展开说了,本人之前总结过:JS中对象的继承模式

问题12:模拟实现instanceof

      function myInstanceOf(leftValue, rightValue) {
        if (typeof leftValue !== 'object' || rightValue === null) return false
        let rightProto = rightValue.prototype
        let leftProto =leftValue.__proto__
        while(true){
            if(leftProto === null){
                return false
            }
            if(leftProto === rightProto){
                return true
            }
            leftProto = leftProto.__proto__
        }
      }

 具体可以参考这篇文章:聊一聊typeof instanceof 实现原理

最后

以上就是总结的前端面试时手写代码的部分题及答案,最后给大家总结了一些面试题集合,欢迎大家学习!祝大家都能拿到自己心仪的offer! 最近也会陆续总结css和vue相关知识点,欢迎大家关注!!

「面试」45 道牛客网 JavaScript 经典题总结(8500字)

40道+JavaScript基础面试题(附答案)

70个JavaScript面试题集锦,内含解答,自测 JS 掌握程度

100道+ JavaScript 面试题,助你查漏补缺

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值