1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 风尚云网面试题系列—JS高频手写代码题

风尚云网面试题系列—JS高频手写代码题

时间:2022-01-22 13:09:05

相关推荐

风尚云网面试题系列—JS高频手写代码题

目录

实现 Promise

实现一个 call 函数

实现一个 apply 函数

实现一个 bind 函数

浅拷贝、深拷贝的实现

实现一个防抖函数

实现一个节流函数

柯里化函数的实现

Object.create 的基本实现原理

实现一个双向数据绑定

实现一个简单路由

实现懒加载

rem 基本设置

手写实现 AJAX

手写jsonp

实现数组的map方法

实现 Promise

// 未添加异步处理等其他边界情况// ①自动执行函数,②三个状态,③thenclass Promise {constructor (fn) {// 三个状态this.state = 'pending'this.value = undefinedthis.reason = undefinedlet resolve = value => {if (this.state === 'pending') {this.state = 'fulfilled'this.value = value}}let reject = value => {if (this.state === 'pending') {this.state = 'rejected'this.reason = value}}// 自动执行函数try {fn(resolve, reject)} catch (e) {reject(e)}}// thenthen(onFulfilled, onRejected) {switch (this.state) {case 'fulfilled':onFulfilled(this.value)breakcase 'rejected':onRejected(this.reason)breakdefault:}}}

实现一个 call 函数

// 思路:将要改变this指向的方法挂到目标this上执行并返回Function.prototype.mycall = function (context) {if (typeof this !== 'function') {throw new TypeError('not funciton')}context = context || windowcontext.fn = thislet arg = [...arguments].slice(1)let result = context.fn(...arg)delete context.fnreturn result}

实现一个 apply 函数

// 思路:将要改变this指向的方法挂到目标this上执行并返回Function.prototype.myapply = function (context) {if (typeof this !== 'function') {throw new TypeError('not funciton')}context = context || windowcontext.fn = thislet resultif (arguments[1]) {result = context.fn(...arguments[1])} else {result = context.fn()}delete context.fnreturn result}

实现一个 bind 函数

// 思路:类似call,但返回的是函数Function.prototype.mybind = function (context) {if (typeof this !== 'function') {throw new TypeError('Error')}let _this = thislet arg = [...arguments].slice(1)return function F() {// 处理函数使用new的情况if (this instanceof F) {return new _this(...arg, ...arguments)} else {return _this.apply(context, arg.concat(...arguments))}}}

浅拷贝、深拷贝的实现

浅拷贝:

// 1. ...实现let copy1 = {...{x:1}}// 2. Object.assign实现let copy2 = Object.assign({}, {x:1})

深拷贝:

// 1. JOSN.stringify()/JSON.parse() // 缺点:拷贝对象包含 正则表达式,函数,或者undefined等值会失败let obj = {a: 1, b: {x: 3}}JSON.parse(JSON.stringify(obj))// 2. 递归拷贝function deepClone(obj) {let copy = obj instanceof Array ? [] : {}for (let i in obj) {if (obj.hasOwnProperty(i)) {copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]}}return copy}

实现一个防抖函数

// 思路:在规定时间内未触发第二次,则执行function debounce (fn, delay) {// 利用闭包保存定时器let timer = nullreturn function () {let context = thislet arg = arguments// 在规定时间内再次触发会先清除定时器后再重设定时器clearTimeout(timer)timer = setTimeout(function () {fn.apply(context, arg)}, delay)}}function fn () {console.log('防抖')}addEventListener('scroll', debounce(fn, 1000))

实现一个节流函数

// 基础版1:时间戳(第一次触发会执行,但不排除不执行的可能,请思考一下哦)function throttle(fn, delay) {var prev = Date.now()return function(...args) {var dist = Date.now() - previf (dist >= delay) {fn.apply(this, args)prev = Date.now()}}}// 基础版2:定时器(最后一次也会执行)function throttle(fn, delay) {var timer = nullreturn function(...args) {var that = thisif(!timer) {timer = setTimeout(function() {fn.apply(this, args)timer = null}, delay)}}}// 进阶版:开始执行、结束执行function throttle(fn, delay) {var timer = nullvar prev = Date.now()return function(...args) {var that = thisvar remaining = delay - (Date.now() - prev) // 剩余时间if (remaining <= 0) { // 第 1 次触发fn.apply(that, args)prev = Date.now()} else { // 第 1 次之后触发timer && clearTimeout(timer)timer = setTimeout(function() {fn.apply(that, args)}, remaining)}}}function fn () {console.log('节流')}addEventListener('scroll', throttle(fn, 1000))

柯里化函数的实现

柯里化函数的定义:将多参数的函数转换成单参数的形式

柯里化函数实现的原理:利用闭包原理在执行可以形成一个不销毁的作用域,然后把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个最少参数函数。

第一种:固定传入参数,参数够了才执行

/*** 实现要点:柯里化函数接收到足够参数后,就会执行原函数,那么我们如何去确定何时达到足够的参数呢?* 柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组。* 接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行fn,没有的话则返回一个新的 curry 函数,将现有的参数塞给他。* */// 待柯里化处理的函数let sum = (a, b, c, d) => {return a + b + c + d}// 柯里化函数,返回一个被处理过的函数 let curry = (fn, ...arr) => { // arr 记录已有参数return (...args) => { // args 接收新参数if (fn.length <= (...arr,...args)) { // 参数够时,触发执行return fn(...arr, ...args)} else { // 继续添加参数return curry(fn, [...arr, ...args])}}}var sumPlus = curry(sum)sumPlus(1)(2)(3)(4)sumPlus(1, 2)(3)(4)sumPlus(1, 2, 3)(4)

第二种:不固定传入参数,随时执行

/*** 当然了,柯里化函数的主要作用还是延迟执行,执行的触发条件不一定是参数个数相等,也可以是其他的条件。* 例如参数个为0的情况,那么我们需要对上面curry函数稍微做修改*/// 待柯里化处理的函数let sum = arr => {return arr.reduce((a, b) => {return a + b})}let curry = (fn, ...arr) => { // arr 记录已有参数return (...args) => { // args 接收新参数if (args.length === 0) { // 参数为空时,触发执行return fn(...arr, ...args)} else { // 继续添加参数return curry(fn, ...arr, ...args)}}}var sumPlus = curry(sum)sumPlus(1)(2)(3)(4)()sumPlus(1, 2)(3)(4)()sumPlus(1, 2, 3)(4)()

Object.create 的基本实现原理

// 思路:将传入的对象作为原型function create(obj) {function F() {}F.prototype = objreturn new F()}

实现一个双向数据绑定

let obj = {}let input = document.getElementById('input')let span = document.getElementById('span')// 数据劫持Object.defineProperty(obj, 'text', {configurable: true,enumerable: true,get() {console.log('获取数据了')},set(newVal) {console.log('数据更新了')input.value = newValspan.innerHTML = newVal}})// 输入监听input.addEventListener('keyup', function(e) {obj.text = e.target.value})

实现一个简单路由

// hash路由class Route{constructor(){// 路由存储对象this.routes = {}// 当前hashthis.currentHash = ''// 绑定this,避免监听时this指向改变this.freshRoute = this.freshRoute.bind(this)// 监听window.addEventListener('load', this.freshRoute, false)window.addEventListener('hashchange', this.freshRoute, false)}// 存储storeRoute (path, cb) {this.routes[path] = cb || function () {}}// 更新freshRoute () {this.currentHash = location.hash.slice(1) || '/'this.routes[this.currentHash]()}}

实现懒加载

<ul><li><img src="./imgs/default.png" data="./imgs/1.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/2.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/3.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/4.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/5.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/6.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/7.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/8.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/9.png" alt=""></li><li><img src="./imgs/default.png" data="./imgs/10.png" alt=""></li></ul>

let imgs = document.querySelectorAll('img')// 可视区高度let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeightfunction lazyLoad () {// 滚动卷去的高度let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTopfor (let i = 0; i < imgs.length; i ++) {// 图片在可视区冒出的高度let x = clientHeight + scrollTop - imgs[i].offsetTop// 图片在可视区内if (x > 0 && x < clientHeight+imgs[i].height) {imgs[i].src = imgs[i].getAttribute('data')}}}// addEventListener('scroll', lazyLoad) or setInterval(lazyLoad, 1000)

rem 基本设置

// 提前执行,初始化 resize 事件不会执行setRem()// 原始配置function setRem () {let doc = document.documentElementlet width = doc.getBoundingClientRect().widthlet rem = width / 75doc.style.fontSize = rem + 'px'}// 监听窗口变化addEventListener("resize", setRem)

手写实现 AJAX

// 1. 简单流程// 实例化let xhr = new XMLHttpRequest()// 初始化xhr.open(method, url, async)// 发送请求xhr.send(data)// 设置状态变化回调处理请求结果xhr.onreadystatechange = () => {if (xhr.readyStatus === 4 && xhr.status === 200) {console.log(xhr.responseText)}}// 2. 基于promise实现function ajax (options) {// 请求地址const url = options.url// 请求方法const method = options.method.toLocaleLowerCase() || 'get'// 默认为异步trueconst async = options.async// 请求参数const data = options.data// 实例化const xhr = new XMLHttpRequest()// 请求超时if (options.timeout && options.timeout > 0) {xhr.timeout = options.timeout}// 返回一个Promise实例return new Promise ((resolve, reject) => {xhr.ontimeout = () => reject && reject('请求超时')// 监听状态变化回调xhr.onreadystatechange = () => {if (xhr.readyState == 4) {// 200-300 之间表示请求成功,304资源未变,取缓存if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {resolve && resolve(xhr.responseText)} else {reject && reject()}}}// 错误回调xhr.onerror = err => reject && reject(err)let paramArr = []let encodeData// 处理请求参数if (data instanceof Object) {for (let key in data) {// 参数拼接需要通过 encodeURIComponent 进行编码paramArr.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))}encodeData = paramArr.join('&')}// get请求拼接参数if (method === 'get') {// 检测url中是否已存在 ? 及其位置const index = url.indexOf('?')if (index === -1) url += '?'else if (index !== url.length -1) url += '&'// 拼接urlurl += encodeData}// 初始化xhr.open(method, url, async)// 发送请求if (method === 'get') xhr.send(null)else {// post 方式需要设置请求头xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8')xhr.send(encodeData)}})}

手写jsonp

$.ajax({url: 'xxxxx', //一个跨域的urltype: 'get',dataType: 'jsonp', //设置服务器返回的数据类型jsonp: 'onJsonPLoad', //这个值用来配置前面提过的callback,它会拼接到url的后面jsonpCallback: 'handleRes', //用来设置回调函数名称success: function (res){ //这里的success回调就相当于之前写到的handleRes方法。console.log(res);}})

实现数组的map方法

Array.prototype.myMap = function(fn, thisValue) {let res = []thisValue = thisValue||[]let arr = thisfor(let i in arr) {res.push(fn(arr[i]))}return res}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。