前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端面试手写代码

前端面试手写代码

作者头像
小小杨
发布2021-10-13 10:31:55
2990
发布2021-10-13 10:31:55
举报
文章被收录于专栏:下落木

单例模式

代码语言:javascript
复制
class SingleObject{
  constructor(name) {
    this.name = name
  }

  login() {
    console.log('name: ', this.name)
  }
}

SingleObject.getInstance = (() => {
  let instance

  return (name) => {
    if (!instance) {
      instance = new SingleObject(name)
    }
    return instance
  }
})()

// 测试
const a = SingleObject.getInstance('yang')
const b = SingleObject.getInstance('guo')

console.log(a === b) // true
console.log(a.login(), b.login()) // yang

// getInstance是类的静态方法,不能使用new SingleObject()
// 创建出来的实例全等

观察者模式

代码语言:javascript
复制
// 目标对象
class Subject{
  constructor() {
    this.subs = []
  }

  addSub(sub) {
    this.subs.push(sub)
  }

  notify() {
    this.subs.map(sub => {
      sub.update()
    })
  }
}

// 观察者
class Observer{
  update() {
    console.log('updating')
  }
}

// 测试
const subject = new Subject()
const observer = new Observer()
subject.addSub(observer)
subject.notify()

// 首先是目标的构造函数,他有个数组,用于添加观察者
// 还有个广播方法notify,遍历观察者数组后调用观察者们的update方法

发布订阅模式

代码语言:javascript
复制
class EventEmitter{
  constructor() {
    this._eventPool = {}
  }

  on(event, cb) {
    this._eventPool.event ?
    this._eventPool.event.push(cb)
      :
    this._eventPool[event] = [cb]
  }

  off(event) {
    delete this._eventPool[event]
  }

  emit(event, ...args) {
     if (this._eventPool[event]) {
      this._eventPool[event].map(cb => {
        cb(...args)
      })
    }
  }

  once(event, cb) {
    this.on(event, (...args) => {
      cb(...args)
      this.off(event)
    })
  }
}

// 测试
const emitter = new EventEmitter()

emitter.on('a', (aaa, bbb) => {
  console.log('fffffff', aaa, bbb)
})

emitter.emit('a', 'windy', 'tom')

emitter.once('b', (aaa, bbb) => {
  console.log('fffffff', aaa, bbb)
})
emitter.emit('b', 'windy', 'tom')
代码语言:javascript
复制

基于一个主题/事件通道,订阅者subscriber通过自定义事件订阅主题,发布者publisher通过发布主题事件的方式发布。

观察者模式和发布订阅模式区别

  1. 在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。
  2. 发布订阅模式相比观察者模式多了个主题/事件通道,订阅者和发布者不是直接关联的。
  3. 观察者模式两个对象之间有很强的依赖关系;发布/订阅模式两个对象之间的耦合度低。

函数柯里化

当我们没有重新定义toString与valueOf时,函数的隐式转换会调用默认的toString方法,它会将函数的定义内容作为字符串返回。

而当我们主动定义了toString/vauleOf方法时,那么隐式转换的返回结果则由我们自己控制了。其中valueOf的优先级会toString高一点。

柯里化好处:参数复用、延迟运行(返回函数,想什么时候运行什么时候运行)

代码语言:javascript
复制
function currying() {
 const [fn, ..._args] = [...arguments]
 const cb = function() {
 if (!arguments.length) {
 return fn.apply(this, _args)
    }
 _args.push(...arguments)
 return cb
  }

 // 可根据需要添加
 cb.toString = fn.apply(this, _args)

 return cb
}

// 测试柯里化
function add() {
 return [...arguments].reduce((a, b) => a + b)
}

const a = currying(add, 12, 24, 36)
console.log(a()) // 72
代码语言:javascript
复制

实现一个add方法,使结果满足如下预期

代码语言:javascript
复制
代码语言:javascript
复制
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
代码语言:javascript
复制
function add() {
  let _args = [...arguments]
  const _adder = function() {
    _args.push(...arguments)
    return _adder
  }

  _adder.toString = function() {
    return _adder.reduce((a, b) => a + b)
  }
  return _adder
}

实现call和apply

代码语言:javascript
复制
代码语言:javascript
复制
Function.prototype.myCall = function() {
  let [context, ...args] = [...arguments]
  if (!context) context = window

  context.fn = this
  const res = context.fn(...args)

  delete context.fn
  return res
}

Function.prototype.myApply = function() {
  let [context, args] = [...arguments]
  if (!context) context = window

  context.fn = this
  let res
  if (args) {
    res = context.fn(...args)
  } else {
    res = context.fn()
  }

  delete context.fn
  return res
}

// 测试
const person = {
  name: 'mike',
  getName: function(a, b) {
    console.log(this.name, a, b)
  }
}

function printName(a, b) {
  console.log(this.name, a, b)
}

printName.myCall(person, 'time', 'fly')
printName.myApply(person, ['time', 'fly'])

实现bind

代码语言:javascript
复制
Function.prototype.myBind = function() {
  const [context, ...args] = [...arguments]
  const _this = this
  return function() {
    return _this.apply(context, args.concat(...arguments))
  }
}

const person = {
  name: 'mike',
  getName: function(a, b) {
    console.log(this.name, a, b)
  }
}

// 测试
const boy = {
  name: 'boy'
}

const getName2 = person.getName.myBind(boy, 'hhhh', 'yyyy')
getName2() // boy hhhh yyyy

实现instanceof

代码语言:javascript
复制
function myInstanceof(left, right) {
  let leftVal = left.__proto__
  let rightVal = right.prototype

  while (true) {
    if (leftVal === null) return false
    if (leftVal === rightVal) return true
    leftVal = leftVal.__proto__
  }
}
代码语言:javascript
复制

new的本质

代码语言:javascript
复制
function myNew(fun) {
  return function() {
  const obj = {
    __proto__: fun.prototype
  }

  fun.call(obj, ...arguments)
    return obj
  }
}

// 测试
function Person(name, age) {
  this.name = name
  this.age = age
}

const obj = myNew(Person)('yang', 18)
// Person { name: 'yang', age: 18 }
代码语言:javascript
复制

Object.create的基本原理

代码语言:javascript
复制
function myCreate(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}
代码语言:javascript
复制

实现promise

代码语言:javascript
复制
class MyPromise{
  constructor(process) {
    this.status = 'pending'
    this.msg = ''
    process(this.resolve.bind(this), this.reject.bind(this))
    return this
  }

  resolve(val) {
    this.status = 'fulfilled'
    this.msg = val
  }

  reject(val) {
    this.status = 'rejected'
    this.msg = val
  }

  then(fulfilled, reject) {
    if (this.status === 'fulfilled') {
      fulfilled(this.msg)
    }
    if (this.status === 'rejected'){
      reject(this.msg)
    }
  }
}

// 测试
const mm = new MyPromise((resolve, reject) => {
  resolve('123')
})

mm.then(res => {
  console.log(res, 'success')
})
代码语言:javascript
复制

防抖和节流

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。

区别:

  1. 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。
  2. 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。

应用:进行窗口的resize、scroll,输入框内容校验或请求ajax时

代码语言:javascript
复制
// 防抖
function debounce(fn, time) {
  let timeout
  return function() {
    const _this = this
    const args = arguments

    if (timeout) clearTimeout(timeout)
    timeout = setTimeout(() => {
      fn.apply(_this, args)
    }, time || 500)
  }
}

// 节流
function throttle(fn, time) {
  let timeout
  return function() {
    const _this = this
    const args = arguments

    if (!timeout) {
      timeout = setTimeout(() => {
        fn.apply(_this, args)
        timeout = null
      }, time || 500)
    }
  }
}



function count() {
  console.log('counting')
}

window.onscroll = debounce(count, 1000)
window.onscroll = throttle(count, 1000)
代码语言:javascript
复制


for循环和reduce实现map和filter

代码语言:javascript
复制
// for循环实现map
Array.prototype.map2 = function() {
 const arr = this
 const [fn, thisArg] = [...arguments]
 let res = []

 for ( let i = 0; i < arr.length; i++) {
 res.push(fn.call(thisArg, arr[i], i, arr))
  }
 return res
}

// reduce实现map
Array.prototype.map3 = function() {
 const arr = this
 const [fn, thisArg] = [...arguments]

 return arr.reduce((acc, cur, i) => {
 acc.push(fn.call(thisArg, cur, i, arr))
 return acc
  }, [])
}

// 测试map
const m = [1,2,3,4,54].map2(item => item * item)
console.log(m) // [ 1, 4, 9, 16, 2916 ]

// for循环实现filter
Array.prototype.filter2 = function() {
 const arr = this
 const [fn, thisArg] = [...arguments]
 let res = []

 for ( let i = 0; i < arr.length; i++) {
 if (fn.call(thisArg, arr[i], i, arr)) {
 res.push(arr[i])
    }
  }
 return res
}

// reduce实现filter
Array.prototype.filter3 = function() {
 const arr = this
 const [fn, thisArg] = [...arguments]

 return arr.reduce((acc, cur, i) => {
 fn.call(thisArg, cur, i, arr) && acc.push(cur)
 return acc
  }, [])
}

// 测试filter
const n = [0, 1, 2, 3, 4, 5]
const n2 = n.filter3(item => item % 2)
console.log(n2) // [ 1, 3, 5 ]

使用for循环打印1-10, 每个数字出现间隔500ms

错误方法:

代码语言:javascript
复制
// 结果虽然依次输出了1-10,但是每个数字之间没有间隔,是一次性输出的,所以不正确
for (var i = 1; i <= 10; i++) {
 setTimeout((function(i) {
   console.log(i);
 })(i), 500); 
}
代码语言:javascript
复制

正确方法:

代码语言:javascript
复制
代码语言:javascript
复制
// 使用闭包,注意setTimeout, 每隔500ms,因此每次传递的间隔时间要乘以i
for(var i=1;i<=10;i++){
 (function(i){
   setTimeout(function(){
     console.log(i);
   },500 * i);
 })(i);
}
代码语言:javascript
复制

或者使用let,let本身就是块级作用域

代码语言:javascript
复制
for(let i=1;i<=10;i++){
 setTimeout(function(){
   console.log(i);
 },500 * i);
}

使用setTimeout模拟setInterval

代码语言:javascript
复制
代码语言:javascript
复制
function fn() {
  console.log('123')
}

setTimeout(function f() {
  fn()
  setTimeout(f, 500)
}, 500)

ES5实现继承

代码语言:javascript
复制
function Parent() {
  this.name = 'parent'
  this.play = [1, 2, 3]
}

function Child() {
  Parent.call(this)
  this.name = 'child'
}

Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child

// 测试
const ss = new Child()
console.log(ss instanceof Child, ss instanceof Parent) // true true
console.log(ss.constructor) // Child
代码语言:javascript
复制
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 下落木 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单例模式
  • 观察者模式
  • 发布订阅模式
  • 函数柯里化
  • 实现一个add方法,使结果满足如下预期
  • 实现call和apply
  • 实现bind
  • 实现instanceof
  • new的本质
  • Object.create的基本原理
  • 实现promise
  • 防抖和节流
  • for循环和reduce实现map和filter
  • 使用for循环打印1-10, 每个数字出现间隔500ms
  • 使用setTimeout模拟setInterval
  • ES5实现继承
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档