美文网首页
手写JS面试题-10道(一)

手写JS面试题-10道(一)

作者: 小二儿上酒 | 来源:发表于2021-06-21 14:38 被阅读0次

是什么

通常面试的时候,面试官会提一些JS手写的面试题,主要是为了考察面试者JS 基础的掌握程度。

以下是整理收集的手写面试题,方便大家学习与巩固 JS 基础知识

数组去重

const _arr = [1, 2, 3, 4, 5, 4]

function uniqueArr () {
  return [...new Set(_arr)]
}

console.log(uniqueArr()) 

数组扁平化

const _arr = [1, 2, [1, [2, 3, [4, 5, [6]]]]]
// 递归实现
function flatter (arr) {
  if (!arr.length) return
  while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr
}
// reducer实现
function flatter (arr) {
  if (!arr.length) return
  return arr.reduce(
    (prev, cur) =>
      Array.isArray(cur) ? [...prev, ...flatter(cur)] : [...prev, cur],
    []
  )
}
console.log(flatter(_arr)) // [1,2,3,4,5,6]

AJAX

/**
 * tips: httpPut 发送 ajax 请求
 */
const httpPut = (url, data, callback, err = console.error) => {
  const request = new XMLHttpRequest()
  request.open("PUT", url, true)
  request.setRequestHeader("Content-type", "application/json; charset=utf-8")
  request.onload = () => callback(request)
  request.onerror = () => err(request)
  request.send(data)
}
const data = JSON.stringify({
  id: 1,
  title: "foo",
  body: "bar",
  userId: 1
})
httpPut("https://jsonplaceholder.typicode.com/posts/1", data, (request) => {
  console.log(request.responseText)
})

/*
 * tips: ajax get
 */
const httpGet = (url, callback, err = console.error) => {
  const request = new XMLHttpRequest()
  request.open("GET", url, true)
  request.onload = () => callback(request.responseText)
  request.onerror = () => err(request)
  request.send()
}
httpGet("https://jsonplaceholder.typicode.com/posts/1", console.log)

/*
 * tips: ajax delete
 */
const httpDelete = (url, callback, err = console.error) => {
  const request = new XMLHttpRequest()
  request.open("DELETE", url, true)
  request.onload = () => callback(request)
  request.onerror = () => err(request)
  request.send()
}
httpDelete("https://jsonplaceholder.typicode.com/posts/1", (request) => {
  console.log(request.responseText)
})

/**
 * tips: ajax post
 */
const httpPost = (url, data, callback, err = console.error) => {
  const request = new XMLHttpRequest()
  request.open("POST", url, true)
  request.setRequestHeader("Content-type", "application/json; charset=utf-8")
  request.onload = () => callback(request.responseText)
  request.onerror = () => err(request)
  request.send(data)
}
const newPost = {
  userId: 1,
  id: 1337,
  title: "Foo",
  body: "bar bar bar"
}
const data = JSON.stringify(newPost)
httpPost("https://jsonplaceholder.typicode.com/posts", data, console.log)

发布订阅

/**
 * tips: publish–subscribe
 */
const createEventHub = () => ({
  hub: Object.create(null),
  emit(event, data) {
    ;(this.hub[event] || []).forEach((handler) => handler(data))
  },
  on(event, handler) {
    if (!this.hub[event]) this.hub[event] = []
    this.hub[event].push(handler)
  },
  off(event, handler) {
    const i = (this.hub[event] || []).findIndex((h) => h === handler)
    if (i > -1) this.hub[event].splice(i, 1)
    if (this.hub[event].length === 0) delete this.hub[event]
  }
})
const handler = (data) => console.log(data)
const hub = createEventHub()
let increment = 0

// Subscribe: listen for different types of events
hub.on("message", handler)
hub.on("message", () => console.log("Message event fired"))
hub.on("increment", () => increment++)

// Publish: emit events to invoke all handlers subscribed to them, passing the data to them as an argument
hub.emit("message", "hello world") // logs 'hello world' and 'Message event fired'
hub.emit("message", { hello: "world" }) // logs the object and 'Message event fired'
hub.emit("increment") // `increment` variable is now 1

// Unsubscribe: stop a specific handler from listening to the 'message' event
hub.off("message", handler)

new 操作符

function myNew (fn, ...args) {
  var obj = Object.create(fn.prototype) // 复制原型链
  var res = fn.call(obj, ...args) // 获取作用域,修改原型链,绑定参数
  if (res && (typeof res === 'object' || typeof res === 'function')) {
    return res // 存在返回
  }
  return obj // 不存在返回对象复制品
}

function Person (name, age) {
  this.name = name
  this.age = age
}
Person.prototype.say = function () {
  console.log(this.age)
}
// wxh
// Person { name: 'wxh', age: 333 }
// 333
let p1 = myNew(Person, 'wxh', 333)
console.log(p1.name)
console.log(p1)
p1.say()

call apply bind

Function.prototype.myCall = function (context, ...args) {
  if (!context || context === null) {
    context = window;
  }
  // 创造唯一的key值  作为我们构造的context内部方法名
  let fn = Symbol();
  context[fn] = this; //this指向调用call的函数
  // 执行函数并返回结果 相当于把自身作为传入的context的方法进行调用了
  return context[fn](...args);
};

// apply原理一致  只是第二个参数是传入的数组
Function.prototype.myApply = function (context, args) {
  if (!context || context === null) {
    context = window;
  }
  // 创造唯一的key值  作为我们构造的context内部方法名
  let fn = Symbol();
  context[fn] = this;
  // 执行函数并返回结果
  return context[fn](...args);
};

//bind实现要复杂一点  因为他考虑的情况比较多 还要涉及到参数合并(类似函数柯里化)

Function.prototype.myBind = function (context, ...args) {
  if (!context || context === null) {
    context = window;
  }
  // 创造唯一的key值  作为我们构造的context内部方法名
  let fn = Symbol();
  context[fn] = this;
  let _this = this;
  //  bind情况要复杂一点
  const result = function (...innerArgs) {
    // 第一种情况 :若是将 bind 绑定之后的函数当作构造函数,通过 new 操作符使用,则不绑定传入的 this,而是将 this 指向实例化出来的对象
    // 此时由于new操作符作用  this指向result实例对象  而result又继承自传入的_this 根据原型链知识可得出以下结论
    // this.__proto__ === result.prototype   //this instanceof result =>true
    // this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype; //this instanceof _this =>true
    if (this instanceof _this === true) {
      // 此时this指向指向result的实例  这时候不需要改变this指向
      this[fn] = _this;
      this[fn](...[...args, ...innerArgs]); //这里使用es6的方法让bind支持参数合并
      delete this[fn];
    } else {
      // 如果只是作为普通函数调用  那就很简单了 直接改变this指向为传入的context
      context[fn](...[...args, ...innerArgs]);
      delete context[fn];
    }
  };
  // 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法
  // 实现继承的方式: 使用Object.create
  result.prototype = Object.create(this.prototype);
  return result;
};

//用法如下

// function Person(name, age) {
//   console.log(name); //'我是参数传进来的name'
//   console.log(age); //'我是参数传进来的age'
//   console.log(this); //构造函数this指向实例对象
// }
// // 构造函数原型的方法
// Person.prototype.say = function() {
//   console.log(123);
// }
// let obj = {
//   objName: '我是obj传进来的name',
//   objAge: '我是obj传进来的age'
// }
// // 普通函数
// function normalFun(name, age) {
//   console.log(name);   //'我是参数传进来的name'
//   console.log(age);   //'我是参数传进来的age'
//   console.log(this); //普通函数this指向绑定bind的第一个参数 也就是例子中的obj
//   console.log(this.objName); //'我是obj传进来的name'
//   console.log(this.objAge); //'我是obj传进来的age'
// }

// 先测试作为构造函数调用
// let bindFun = Person.myBind(obj, '我是参数传进来的name')
// let a = new bindFun('我是参数传进来的age')
// a.say() //123

// 再测试作为普通函数调用
// let bindFun = normalFun.myBind(obj, '我是参数传进来的name')
//  bindFun('我是参数传进来的age')

深拷贝

// 判断是否是对象
function isObject (val) {
  return typeof val === 'object' && val !== null
}

function deepClone (obj, hash = new WeakMap()) {
  if (!isObject(obj)) return obj
  if (hash.has(obj)) {
    return hash.get(obj)
  }
  let target = Array.isArray(obj) ? [] : {}
  hash.set(obj, target) // 存入 WeakMap 中
  // 递归调用
  Reflect.ownKeys(obj).forEach(item => {
    if (isObject(obj[item])) {
      target[item] = deepClone(obj[item], hash) // 递归调用
    } else {
      target[item] = obj[item]
    }
  })

  return target
}

var obj1 = {
  a: 1,
  b: { a: 2 }
}
var obj2 = deepClone(obj1)
console.log(obj1)

instance 操作符

function myInstanceof(left, right) {
  while (true) {
    if (left === null) {
      return false;
    }
    if (left.__proto__ === right.prototype) {
      return true;
    }
    left = left.__proto__;
  }
}

实现防抖与节流

// 防抖
function debounce(fn, delay = 300) {
  //默认300毫秒
  let timer;
  return function () {
    const args = arguments;
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, args); // 改变this指向为调用debounce所指的对象
    }, delay);
  };
}

window.addEventListener(
  "scroll",
  debounce(() => {
    console.log(111);
  }, 1000)
);

// 节流
// 设置一个标志
function throttle(fn, delay) {
  let flag = true;
  return () => {
    if (!flag) return;
    flag = false;
    timer = setTimeout(() => {
      fn();
      flag = true;
    }, delay);
  };
}

window.addEventListener(
  "scroll",
  throttle(() => {
    console.log(111);
  }, 1000)
);

将虚拟 Dom 转化为真实 Dom

const VDom = {
  tag: 'DIV',
  attrs: {
    id: 'app'
  },
  children: [
    {
      tag: 'SPAN',
      children: [{ tag: 'A', children: [], text: 'hello' }],
      text: 'hello'
    },
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [], text: 'hello' },
        { tag: 'A', children: [], text: 'hello' }
      ]
    }
  ],
  text: 'hello'
}

function _render (vnode) {
  if (typeof vnode === 'number') {
    vnode = String(vnode)
  }
  if (typeof vnode === 'string') {
    return document.createTextNode(vnode)
  }

  const dom = document.createElement(vnode.tag)
  if (vnode.attrs) {
    // 遍历属性
    Object.keys(vnode.attrs).forEach(key => {
      const value = vnode.attrs[key]
      dom.setAttribute(key, value)
    })
  }
  if (vnode.text) {
    dom.innerText = vnode.text
  }

  vnode.children.forEach(child => dom.appendChild(_render(child)))
  return dom
}

console.log(_render(VDom))

相关文章

  • 手写JS面试题-10道(一)

    是什么 通常面试的时候,面试官会提一些JS手写的面试题,主要是为了考察面试者JS 基础的掌握程度。 以下是整理收集...

  • 手写JS面试题-10道(二)

    是什么 通常面试的时候,面试官会提一些JS手写的面试题,主要是为了考察面试者JS 基础的掌握程度。 以下是整理收集...

  • 十道前端面试题第【03】篇

    摘要:本篇分享了10道面试题——Web性能优化方案、JS严格模式、五道算法题、自定义JS事件系统、输入URL到浏览...

  • 十道前端面试题第【04】篇

    摘要:本篇分享了10道面试题——四道算法题、判断两个对象是否相等、JS数组模拟队列、SSR服务端渲染、封装WebS...

  • 面试.md

    [TOC] 2017年第一波 JavaScript 面试题 一道常被人轻视的前端****JS面试题 目录 前言 第...

  • 由一道面试题到 valueOf 方法的原理

    一道面试题 最近看到一道面试题 add(4)(2, 3)(10) // 得到结果 20 本以为用 argument...

  • 多个异步请求同时发出,对其结果进行有序执行~

    这是一道面试时遇到的面试题,当时没手写出来,现在抽空敲出来了,补一下~原题是这样的: 有10个ajax请求,要求同...

  • js基础面试题

    面试题:延迟加载JS有哪些方式? 面试题:JS数据类型有哪些? 面试题:JS数据类型考题 考题一: 考题二: 面试...

  • js面试题

    一道腾讯js面试题 题目如下: 对作用域链(scope chain)、执行环境(execution context...

  • 那些年遇到的面试题

    腾讯面试题 据说是小米面试题 答案: 来自国外的一道题 一道经典面试题: 杂 1.JS数据类型有哪些?哪些是引用类...

网友评论

      本文标题:手写JS面试题-10道(一)

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