收集整理自用,方便平时翻看记忆。题目和答案来源请看文章末尾,欢迎各位大佬补充,批评指正一般来说,把下面基础中的高频题写熟练就差不多了。当然去面大厂这些远远不够,还要再刷一些算法题。 基础 高频 1.手写 instanceof // 原理:验证当前类的原型prototype是否会出现在实例的原型链proto上,只要在它的原型链上,则结果都为truefunction myinstanceOf_(obj, class_name) {      // let proto = obj.__proto__;    let proto = Object.getPrototypeOf(obj)    let prototype = class_name.prototype    while (true) {        if (proto == null) return false        if (proto == prototype) return true          // proto = proto.__proto__;        proto = Object.getPrototypeOf(proto)    }} 2.手写 new 操作符 function myNew(){    //1.创建一个新的对象    let obj = {};    //获得构造函数    let con = [].shift.call(arguments);     //2.新对象的隐式原型__proto__链接到构造函数的显式原型prototype    obj.__proto__ = con.prototype;    //3.构造函数内部的 this 绑定到这个新创建的对象 执行构造函数    let result = con.apply(obj, arguments)    //4.如果构造函数没有返回非空对象,则返回创建的新对象    return result instanceof Object ? result:obj;}// test/*function Car(name,price){    this.name = name     this.price = price}Car.prototype.run = function() {    console.log(this.price);};var test_create = myNew(Car, 'a', 100000);console.log(test_create)// comparelet obj = new Car( 'a', 100000)console.log(obj)*/ 3.手写 call、apply、bind 函数 call(thisArg, ...args) // 给函数的原型添加 _call 方法,使得所有函数都能调用 _call// thisArg 就是要绑定的那个this;...args 扩展操作符传参,适合不定长参数,args是一个数组Function.prototype._call = function(thisArg,...args){    // 1.获取需要执行的函数    let func = this    // 2.将 thisArg 转成对象类型(防止它传入的是非对象类型,例如123数字)    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window    // 3.使用 thisArg 调用函数,绑定 this    // 评论区大佬提出的改进方法,用 Symbol 创建一个独一无二的属性,避免属性覆盖或冲突的情况    let fn = Symbol()    thisArg[fn] = this    // thisArg.fn = fn    let result = thisArg[fn](...args)    delete thisArg[fn]    // 4.返回结果    return result} apply(thisArg, argsArray) Function.prototype._apply = function(thisArg,argArray){    // 1.获取需要执行的函数    let fn = this    // 2.将 thisArg 转成对象类型(防止它传入的是非对象类型,例如123数字)    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window    // 判断一些边界情况    argArray = argArray || []    // 3.使用 thisArg 调用函数,绑定 this    thisArg.fn = fn    // 将传递过来的数组(可迭代对象)拆分,传给函数    let result = thisArg.fn(...argArray)    delete thisArg.fn    // 4.返回结果    return result} bind(thisArg, ...args) Function.prototype._call = function(thisArg,...args){    let fn = this    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window    thisArg.fn = fn    let result = thisArg.fn(...args)    delete thisArg.fn    return result}// 利用 call 模拟 bindFunction.prototype._bind = function(thisArg,...args){    let fn = this // 需要调用的那个函数的引用    // bind 需要返回一个函数    return function(){        return fn._call(thisArg, ...args)    }} 4.手写深拷贝 PS:浅拷贝也可以用一样的模板,当然深拷贝考得多 function deepCopy(object) {  if (!object || typeof object !== "object") return object;  let newObject = Array.isArray(object) ? [] : {};  for (let key in object) {    if (object.hasOwnProperty(key)) {      newObject[key] = deepCopy(object[key]);    }  }  return newObject;} 进阶:解决循环引用的深拷贝 function deepClone(obj, hash = new WeakMap()) {  if (!object || typeof object !== "object") return object;  // 是对象的话就要进行深拷贝,遇到循环引用,将引用存储起来,如果存在就不再拷贝  if (hash.get(obj)) return hash.get(obj);  let cloneObj = Array.isArray(object) ? [] : {};  hash.set(obj, cloneObj);  for (let key in obj) {    if (obj.hasOwnProperty(key)) {      // 实现一个递归拷贝      cloneObj[key] = deepClone(obj[key], hash);    }  }  return cloneObj;} 5.手写防抖节流 function debounce(func, delay) {  // 这里使用了闭包,所以 timer 不会轻易被销毁  let timer = null  // 生成一个新的函数并返回  return function (...args) {    // 清空定时器    if (timer) {      clearTimeout(timer)    }    // 重新启动定时器    timer = setTimeout(() => {      func.call(this, ...args)    }, delay)  }}function throttle(func, delay) {  let timer = null  // 在 delay 时间内,最多执行一次 func  return function (...args) {    if (!timer) {      timer = setTimeout(() => {        func.call(this, ...args)        // 完成一次计时,清空,待下一次触发        timer = null      }, delay)    }  }} 6.手写Ajax请求 function ajax(url) {    // 创建一个 XHR 对象    return new Promise((resolve,reject) => {        const xhr = new XMLHttpRequest()        // 指定请求类型,请求URL,和是否异步        xhr.open('GET', url, true)        xhr.onreadystatechange = funtion() {          // 表明数据已就绪            if(xhr.readyState === 4) {                if(xhr.status === 200){                    // 回调                    resolve(JSON.stringify(xhr.responseText))                }                else{                    reject('error')                }            }        }        // 发送定义好的请求        xhr.send(null)    })} 7.手写数组去重 // 1.Set + 数组复制fuction unique1(array){    // Array.from(),对一个可迭代对象进行浅拷贝    return Array.from(new Set(array))}// 2.Set + 扩展运算符浅拷贝function unique2(array){    // ... 扩展运算符    return [...new Set(array)]}// 3.filter,判断是不是首次出现,如果不是就过滤掉function unique3(array){    return array.filter((item,index) => {        return array.indexOf(item) === index    })}// 4.创建一个新数组,如果之前没加入就加入function unique4(array){    let res = []    array.forEach(item => {        if(res.indexOf(item) === -1){            res.push(item)        }    })    return res} 进阶:如果数组内有数组和对象,应该怎么去重(此时对象的地址不同,用Set去不了重) 8.手写数组扁平 // 方法1-3:递归function flat1(array){    // reduce(): 对数组的每一项执行归并函数,这个归并函数的返回值会作为下一次调用时的参数,即 preValue    // concat(): 合并两个数组,并返回一个新数组    return array.reduce((preValue,curItem) => {        return preValue.concat(Array.isArray(curItem) ? flat1(curItem) : curItem)    },[])}function flat2(array){    let res = []    array.forEach(item => {        if(Array.isArray(item)){            // res.push(...flat2(item))        // 如果遇到一个数组,递归            res = res.concat(flat2(item))        }        else{            res.push(item)        }    })    return res}function flat3(array){    // some(): 对数组的每一项都运行传入的函数,如果有一项返回 TRUE,则这个方法返回 TRUE    while(array.some(item => Array.isArray(item))){    // ES6 增加了扩展运算符,用于取出参数对象的所有可遍历属性,拷贝到当前对象之中:        array = [].concat(...array)        console.log(...array)    }    return array}// 方法4、5:先转成字符串,再变回数组function flat4(array){    //[1,[2,3]].toString()  =>  1,2,3    return array.toString().split(',').map(item => parseInt(item))}function flat5(array){    return array.join(',').split(',').map(item => Number(item))} 9.手写数组乱序 // 方法1: sort + Math.random()function shuffle1(arr){    return arr.sort(() => Math.random() - 0.5);// }// 方法2:时间复杂度 O(n^2)// 随机拿出一个数(并在原数组中删除),放到新数组中function randomSortArray(arr) {    let backArr = [];    while (arr.length) {        let index = parseInt(Math.random() * arr.length);        backArr.push(arr[index]);        arr.splice(index, 1);    }    return backArr;}// 方法3:时间复杂度 O(n)// 随机选一个放在最后,交换function randomSortArray2(arr) {    let lenNum = arr.length - 1;    for (let i = 0; i < lenNum; i++) {        let index = parseInt(Math.random() * (lenNum + 1 - i));        [a[index],a[lenNum - i]] = [a[lenNum - i],a[index]]    }    return arr;} 10.手写 Promise.all()、Promise.race() PS: 有能力的可以去写下 Promise 和其他的 Promise 方法 function myAll(promises){    // 问题关键:什么时候要执行resolve,什么时候要执行 reject    return new Promise((resolve,reject) => {        results = []        // 迭代数组中的 Promise,将每个 promise 的结果保存到一个数组里        let counter = 0        for (let i = 0; i < promises.length; i++) {            // 如果不是 Promise 类型要先包装一下            // 调用 then 得到结果            Promise.resolve(promises[i]).then(res => {                // 这里不能用 push(),因为需要保证结果的顺序。感谢评论区大佬的批评指正                results[i] = res                counter++                // 如果全部成功,状态变为 fulfilled                if(counter === promises.length){                    resolve(results)                }                },err => { // 如果出现了 rejected 状态,则调用 reject() 返回结果                reject(err)            })        }    })}// testlet p1 = new Promise(function (resolve, reject) {    setTimeout(function () {        resolve(1)    }, 1000)})let p2 = new Promise(function (resolve, reject) {    setTimeout(function () {        resolve(2)    }, 2000)})let p3 = new Promise(function (resolve, reject) {    setTimeout(function () {        resolve(3)    }, 3000)})myAll([p3, p1, p2]).then(res => {    console.log(res) // [3, 1, 2]}) // 哪个 promise 状态先确定,就返回它的结果function myRace(promises) {    return new Promise((resolve, reject) => {        promises.forEach(promise => {            Promise.resolve(promise).then(res => {                resolve(res)            }, err => {                reject(err)            })        })    })} 11.手撕快排 PS: 常见的排序算法,像冒泡,选择,插入排序这些最好也背一下,堆排序归并排序能写则写。万一考到了呢,要是写不出就直接回去等通知了 const _quickSort = array => {    // 补全代码    quickSort(array, 0, array.length - 1)    // 别忘了返回数组    return array}const quickSort = (array, start, end) => {    // 注意递归边界条件    if(end - start < 1)    return    // 取第一个数作为基准    const base = array[start]    let left = start    let right = end    while(left < right){        // 从右往左找小于基准元素的数,并赋值给右指针 array[right]        while(left < right &&  array[right] >= base)    right--        array[left] = array[right]        // 从左往右找大于基准元素的数,并赋值给左指针 array[left]        while(left < right && array[left] <= base)    left++        array[right] = array[left]    }    // 双指针重合处,将基准元素填到这个位置。基准元素已经事先保存下来了,因此不用担心上面的赋值操作会覆盖掉基准元素的值    // array[left] 位置已经确定,左边的都比它小,右边的都比它大    array[left] = base    quickSort(array, start, left - 1)    quickSort(array, left + 1, end)    return array} 12.手写 JSONP // 动态的加载js文件function addScript(src) {  const script = document.createElement('script');  script.src = src;  script.type = "text/javascript";  document.body.appendChild(script);}addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");// 设置一个全局的callback函数来接收回调结果function handleRes(res) {  console.log(res);}// 接口返回的数据格式,加载完js脚本后会自动执行回调函数handleRes({a: 1, b: 2}); 13.手写寄生组合继承 PS: 组合继承也要能写出来 function Parent(name) {  this.name = name;  this.say = () => {    console.log(111);  };}Parent.prototype.play = () => {  console.log(222);};function Children(name,age) {  Parent.call(this,name);  this.age = age}Children.prototype = Object.create(Parent.prototype);Children.prototype.constructor = Children; 14.数组/字符串操作题 可以自己找些基础的练一下,就不一一列举了 15.手写二分查找 //  迭代版function search(nums, target) {  // write code here    if(nums.length === 0)    return -1    let left = 0,right = nums.length - 1        // 注意这里的边界,有等号    while(left <= right){        let mid = Math.floor((left + right) / 2)        if(nums[mid] < target)    left = mid + 1        else if(nums[mid] > target)    right = mid - 1        else    return mid    }    return -1}// 递归版function binary_search(arr, low, high, key) {    if (low > high) {        return -1;    }    var mid = parseInt((high + low) / 2);    if (arr[mid] == key) {        return mid;    } else if (arr[mid] > key) {        high = mid - 1;        return binary_search(arr, low, high, key);    } else if (arr[mid] < key) {        low = mid + 1;        return binary_search(arr, low, high, key);    }}; 16.手写函数柯里化 function sum(x,y,z) {    return x + y + z}function hyCurrying(fn) {    // 判断当前已经接收的参数的个数,和函数本身需要接收的参数是否一致    function curried(...args) {        // 1.当已经传入的参数 大于等于 需要的参数时,就执行函数        if(args.length >= fn.length){            // 如果调用函数时指定了this,要将其绑定上去            return fn.apply(this, args)        }        else{            // 没有达到个数时,需要返回一个新的函数,继续来接收参数            return function(...args2) {                //return curried.apply(this, [...args, ...args2])                // 接收到参数后,需要递归调用 curried 来检查函数的个数是否达到                return curried.apply(this, args.concat(args2))            }        }    }    return curried}var curryAdd = hyCurry(sum)curryAdd(10,20,30)curryAdd(10,20)(30)curryAdd(10)(20)(30) 17.CSS水平垂直居中 18.CSS画三角形 19.CSS实现两栏和三栏布局 20.手写发布-订阅模式 || Event Bus 中频 1.手写事件委托 2.DOM操作相关题目 3.手写对象扁平化 4.手写数组常见方法 Array.filter/map/fill/reduce 5.手写列表转树 6.日期时间格式化 7.数字千分位 8.URL参数解析 9.手写观察者模式 10.手写 Promise(进阶) 低频 1.手写组合函数 2.手写Object.create() 3.手写Object.is() 4.手写Object.freeze() 5.手写JSON.stringify() 6.大数相加 7.下划线转驼峰 8.CSS清除浮动 场景模拟题 高频 1.实现 sleep 函数 async function test() {    console.log('开始')    await sleep(4000)    console.log('结束')}function sleep(ms) {    return new Promise(resolve => {        setTimeout(() => {            resolve()        }, ms)    })}test() 2.setTimeout 实现 setInterval function setInterval(fn, time){    var interval = function(){    // time时间过去,这个异步被执行,而内部执行的函数正是interval,就相当于进了一个循环        setTimeout(interval, time);    // 同步代码        fn();    }    //interval被延迟time时间执行    setTimeout(interval,time); } 3.异步循环打印 1,2,3 var sleep = function (time, i) {  return new Promise(function (resolve, reject) {    setTimeout(function () {      resolve(i);    }, time);  })};var start = async function () {  for (let i = 1; i <= 3; i++) {    let result = await sleep(1000, i);    console.log(result);  }};start(); 4.循环打印红、黄、绿 function red() {    console.log('red');}function green() {    console.log('green');}function yellow() {    console.log('yellow');}const task = (timer, light) => {    new Promise((resolve, reject) => {        setTimeout(() => {            if (light === 'red') {                red()            }            else if (light === 'green') {                green()            }            else if (light === 'yellow') {                yellow()            }            resolve()        }, timer)    })}const taskRunner =  async () => {    await task(3000, 'red')    await task(2000, 'green')    await task(2100, 'yellow')    taskRunner()}taskRunner() 进阶 Promise 并发控制、串行执行相关的题目 更多 Promise 的面试题在这里:要就来45道Promise面试题一次爽到底,面大厂的兄弟可以看看 参考资料 牛客职导校招冲刺集训营-前端.牛客 前端面试手撕题.牛客网JS手撕题库 前端秋招面经.bbin.牛客 前端开发-个人面经高频知识点整理(校招) .求求来一份offer趴!!.牛客 「2021」高频前端面试题汇总之手写代码篇.CUGGZ.掘金 最全的手写JS面试题.Big shark@LX.掘金 2021年前端各大公司都考了那些手写题(附带代码).战场小包.掘金 awesome-coding-js 用 JavaScript 实现的算法和数据结构.Conardli.GitHub 深入JavaScript高级语法.coderwhy 「一劳永逸」送你21道高频JavaScript手写面试题.TianTianUp.掘金 更多题目可见 https://fe.ecool.fun/topic-list?pageNumber=6&orderBy=updateTime&order=asc&tagId=26
点赞 134
评论 19
全部评论

相关推荐

有趣的牛油果开挂了:最近这个阶段收到些杂七杂八的短信是真的烦
点赞 评论 收藏
分享
10-09 00:50
已编辑
长江大学 算法工程师
不期而遇的夏天:1.同学你面试评价不错,概率很大,请耐心等待;2.你的排名比较靠前,不要担心,耐心等待;3.问题不大,正在审批,不要着急签其他公司,等等我们!4.预计9月中下旬,安心过节;5.下周会有结果,请耐心等待下;6.可能国庆节前后,一有结果我马上通知你;7.预计10月中旬,再坚持一下;8.正在走流程,就这两天了;9.同学,结果我也不知道,你如果查到了也告诉我一声;10.同学你出线不明朗,建议签其他公司保底!11.同学你找了哪些公司,我也在找工作。
点赞 评论 收藏
分享
最近又搬回宿舍了,在工位坐不住,写一写秋招起伏不断的心态变化,也算对自己心态的一些思考表演式学习从开始为实习准备的时候就特别焦虑,楼主一开始选择的是cpp后端,但是24届这个方向已经炸了,同时自己又因为本科非92且非科班,所以感到机会更加迷茫。在某天晚上用java写出hello world并失眠一整晚后选择老本行干嵌入式。理想是美好的,现实情况是每天忙但又没有实质性进展,总是在配环境,调工具,顺带还要推科研。而这时候才发现自己一直在表演式学习,徘徊在设想如何展开工作的循环里,导致没有实质性进展。现在看来当时如果把精力专注在动手写而不是两只手端着看教程,基本功或许不会那么差。实习的焦虑5月,楼主...
耶比:哲学上有一个问题,玛丽的房间:玛丽知道眼睛识别色彩的原理知道各种颜色,但是她生活在黑白的房间里,直到有一天玛丽的房门打开了她亲眼看到了颜色,才知道什么是色彩。我现在最大可能的减少对非工作事情的思考,如果有一件事困扰了我, 能解决的我就直接做(去哪里或者和谁吵架等等……),解决不了的我就不想了,每一天都是最年轻的一天,珍惜今天吧
投递比亚迪等公司10个岗位 > 秋招被确诊为…… 牛客创作赏金赛
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务