高频

一. 柯里化函数(Currying)和反柯里化

简介

柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

个人理解偏函数是一种特殊柯里化,偏函数

核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。

按照Stoyan Stefanov --《JavaScript Pattern》作者 的说法,所谓“柯里化”就是使函数理解并处理部分应用

柯里化有3个常见作用:

  1. 参数复用
  2. 提前返回
  3. 延迟计算/运行

简单实现

function currying(fn, ...rest1) {return function(...rest2) {return fn.apply(null, rest1.concat(rest2))}
}

例:将一个sayHello函数柯里化

function sayHello(name, age, fruit) {console.log(console.log(`我叫 ${name},我 ${age} 岁了, 我喜欢吃 ${fruit}`))
}const curryingShowMsg1 = currying(sayHello, '小明')
curryingShowMsg1(22, '苹果')            // 我叫 小明,我 22 岁了, 我喜欢吃 苹果const curryingShowMsg2 = currying(sayHello, '小衰', 20)
curryingShowMsg2('西瓜')               // 我叫 小衰,我 20 岁了, 我喜欢吃 西瓜

高阶(递归)柯里化函数

function curryingHelper(fn, len) {const length = len || fn.length  // 第一遍运行length是函数fn一共需要的参数个数,以后是剩余所需要的参数个数return function(...rest) {return rest.length >= length    // 检查是否传入了fn所需足够的参数? fn.apply(this, rest): curryingHelper(currying.apply(this, [fn].concat(rest)), length - rest.length)        // 在通用currying函数基础上}
}

例:

function sayHello(name, age, fruit) { console.log(`我叫 ${name},我 ${age} 岁了, 我喜欢吃 ${fruit}`) }    const betterShowMsg = curryingHelper(sayHello)
betterShowMsg('小衰', 20, '西瓜')      // 我叫 小衰,我 20 岁了, 我喜欢吃 西瓜
betterShowMsg('小猪')(25, '南瓜')      // 我叫 小猪,我 25 岁了, 我喜欢吃 南瓜
betterShowMsg('小明', 22)('倭瓜')      // 我叫 小明,我 22 岁了, 我喜欢吃 倭瓜
betterShowMsg('小拽')(28)('冬瓜')      // 我叫 小拽,我 28 岁了, 我喜欢吃 冬瓜

二. 排序

// 冒泡排序 选出最大
var paixuArr = [34,45,2,4,77,13,0,67,89,3,795,24]
function mao(arr){
let len = arr.lengthfor(let i=0; i < len - 1; i++) {for(let j=0; j < len - i - 1; j++) {if(arr[j] > arr[j+1]){let temp = arr[j]arr[j] = arr[j+1]arr[j+1] = temp}}}return arr
}// 选择排序
function xuanze(arr) {let len = arr.lengthfor(let i=0; i < len - 1; i++) {for(let j=i + 1; j < len; j++) {if(arr[i] > arr[j]){let temp = arr[i]arr[i] = arr[j]arr[j] = temp}}}return arr
}// 快速排序
function kuaisu(arr){let len = arr.lengthif(len <= 1) {return arr}let firstValue = arr[0]let leftList = []let rightList = []for(let i = 1; i < len; i ++){if(arr[i] >= arr[0]){rightList.push(arr[i])} else {leftList.push(arr[i])}}return kuaisu(leftList).concat([firstValue], kuaisu(rightList))
}

三. setTimeout 实现 setInterval

使用key 标识当前执行的 setInterval, 存储在 timeWorker 中(对象是为了兼容多个 interval 并存)

值为 当前执行的 setTimeout,

当需要清除时,先找到要清除的 setInterval key,然后 clear 对应的 setTimeout

var timeWorker = {}
var mySetInterval= function(fn, time) {// 定义一个key,来标识此定时器var key = Symbol();// 定义一个递归函数,持续调用定时器var execute = function(fn, time) {timeWorker[key] = setTimeout(function(){fn();execute(fn, time);}, time)}execute(fn, time);// 返回keyreturn key;
}
var myClearInterval = function(key) {if (key in timeWorker) {clearTimeout(timeWorker[key]);delete timeWorker[key];}
}

四. 二叉树广度、深度(前序、中序、后序)遍历

二叉树的遍历有深度优先和广度优先两种。 前序、中序、后序遍历都属于深度优先遍历。层次遍历属于广度优先遍历,从上到下,从左到右或从右到左一层一层的遍历。

1、二叉树的深度优先遍历-递归

// 前/先序:根左右
var DLR = function(root, res) {if(!root)   return ;res.push(root.val);root.left && DLR(root.left, res);root.right && DLR(root.right, res);
}// 中序:左根右
var LDR = function(root, res) {if(!root)   return ;root.left && LDR(root.left, res);res.push(root.val);if(root.right)    LDR(root.right, res);
}// 后序:左右根
var LRD = function(root, res) {if(!root)   return ;root.left && LRD(root.left, res);root.right && LRD(root.right, res);res.push(root.val);
}

2、二叉树的深度优先遍历-非递归-迭代-采用栈(先进后出)

// 前序:根左右
var DLR = function(root) {if(!root)   return [];let res = [];let s = [root];while(s.length > 0) {let p = s.shift();    // 取第一个res.push(p.val);p.right && s.unshift(p.right);p.left && s.unshift(p.left);}return res;
}// 中序:左根右
// 先将根节点入栈,找到所有左节点入栈,直到没有左节点为止
// 然后出栈存入结果数组,每出一个,对比该根节点的右子节点是否有左节点,若有则入栈,否则继续出栈
var LDR = function(root) {let res = [];let s = [];     // 栈  let p = root;   // 指针while(p || s.length > 0) { // 直至左节点为空,即没有左节点为止while (p) {s.push(p);p = p.left;}// 出栈,存放根节点p = s.pop();res.push(p.val);p = p.right;}return res;
}// 后序:左右根
// 后序遍历比较复杂,但是看见网上有个比较好记住的办法:
// 按照与前序相似的方法(前序压栈的顺序是先右后左,这里是先左后右),先得到一个结果,然后对结果倒序一下。
var LRD = function(root) {if(!root)   return [];let res = [];let s = [root];while(s.length > 0) {let p = s.shift();    // 取第一个res.push(p.val);p.left && s.unshift(p.left);p.right && s.unshift(p.right);}return res.reverse();
}

3、二叉树的广度优先优先-非递归-采用队列

// 二叉树的遍历-层次遍历
// 从上往下,从左到右/从右到左
var levelOrder = function(root) {if(!root)   return [];let res = [];let q = [root];while(q.length > 0){let tmp = [];let temp = [];for(let i in q){let p = q[i];temp.push(p.val);p.left && tmp.push(p.left);p.right && tmp.push(p.right);}q = tmp;res.push(temp);}return res;
};

五. 继承

1. 重写原型对象

业务中较少使用,缺点:
(1). 父类属性直接赋值给子类原型,子类会共享属性
(2). 无法向父类传参

缺点:所有对于原型链上的修改,都会变成通用调整。

function Game () { this.name = 'LOL' }
Game.prototype.getName = function () { return this.name }function LOL () {}
LOL.prototype = new Game()
LOL.prototype.constructor = LOL

2. 构造函数继承

(1)经典继承

优点:解决共享属性 + 向父类传参问题
缺点:原型链上的共享方法无法直接读取继承(getName)

function Game (arg) {this.name = 'LOL'}
Game.prototype.getName = function () { return this.name }function LOL (arg) {Game.call(this, arg)
}

(2)组合继承

解决原型链上的共享方法无法直接读取继承(getName)问题
缺点:性能(两次执行this.name = ‘LOL’)

function Game (arg) {this.name = 'LOL'}
Game.prototype.getName = function () { return this.name }function LOL (arg) {Game.call(this, arg)
}
LOL.prototype = new Game()
LOL.prototype.constructor = LOL

3. 寄生组合继承

function Game (arg) {this.name = 'LOL'}
Game.prototype.getName = function () { return this.name }function LOL (arg) {Game.call(this, arg)
}
LOL.prototype = Object.create(Game.prototype)
LOL.prototype.constructor = LOL

new Object() 和 Object.create() 区别?
后者可以继承传入的 arg 参数

4. 多重继承

function Game (arg) {this.name = 'LOL'}
Game.prototype.getName = function () { return this.name }function Store () { this.shop = 'Steam' }
Store.prototype.getPlatform = function () { return this.shop }function LOL (arg) {Game.call(this, arg)
}
LOL.prototype = Object.create(Game.prototype)
Objcet.assign(LOL.prototype, Store.prototype)
LOL.prototype.constructor = LOL

六. 经典闭包

函数外部可以获取到函数作用域内的变量值

function mail() {let content = '这是内容'return function () {console.log(content)}
}
const getMail = mail()
getMail() // 可以输出

如何实现私有属性? 采用闭包。

Class Mail {constructor () {let _content = '这是内容'this.getContent = () => {console.log(_content)}}
}

箭头函数无 constructor,无法实例化

七. 发布订阅者

class Publisher {constructor() {this.subList = []}// 以电话号码作为订阅的标识add(phoneNumber,cb) {this.subList.push({phoneNumber,cb})}// 取消订阅remove(phoneNumber) {const index = this.subList.findIndex(item => item.phoneNumber === phoneNumber)this.subList.splice(index, 1)}// 通知publish(msg) {this.subList.forEach(item => {item.cb(msg)})}getSubscriberList() {console.log(this.subList)}
}const publisher = new Publisher()publisher.add(12345678 ,function (msg) {console.log('第一个订阅者收到了消息:' + msg)
})let i = 1// 通知
setInterval(() => {publisher.publish(i++)publisher.getSubscriberList()
}, 1000)setTimeout(() => {publisher.add(999888, function (msg) {console.log('第二个订阅者收到:' + msg)})
}, 1000 * 5)

八. 移动端1px边框

  • 用图片
  • 用阴影模拟
.box-shadow-1px {box-shadow: inset 0px -1px 1px -1px #c8c7cc;
}
  • 用伪类
.border-1px:before {content: " ";position: absolute;left: 0;top: 0;width: 100%;height: 1px;border-top: 1px solid #D9D9D9;color: #D9D9D9;-webkit-transform-origin: 0 0;transform-origin: 0 0;-webkit-transform: scaleY(0.5);transform: scaleY(0.5);
}

九. 合并二维有序数组成一维有序数组,归并排序的思路

var arr = [1, 2,  5, 6, [3, 4]]
var arr1 = arr.join().split(',')
var arr2 = arr.flat(Infinity)
function flatten(arr, deep = 1) {return deep > 0 ? arr.reduce((acc, val)=> acc.concat(Array.isArray(val) ? flatten(val, deep-1) : val), []): arr.slice()
}
// console.log(flatten(arr, Infinity));

十. 斐波那契数列 1 1 2 3 5 8 13 , arr[n] = arr[n-1] + arr[n-2]

// 循环
function feibona1(n) {let n1 = 1;let n2 = 1;let sum = 1;for (let i = 1; i < n; n++){sum = n1 + n2n1 = n2n2 = sum}return sum
}
// 普通递归
function feibona2(n){if(n === 1 || n ===2){return 1 }return feibona2(n-2) + feibona2(n-1)
}
// 改进递归-把前两位数字做成参数避免重复计算
function feibona3(n){function fei(m, v1, v2){if(m === 1) return v1if(m === 2) return v2return fei(m-1, v2, v1+v2)}fei(n, 1, 1)
}

十一. 字符串出现的不重复最长长度

var lengthOfLongestSubstring = function(s) {var res = 0; // 用于存放当前最长无重复子串的长度var str = ""; // 用于存放无重复子串var len = s.length;for(var i = 0; i < len; i++) {var char = s.charAt(i);var index = str.indexOf(char);if(index === -1) {str += char;res = res < str.length ? str.length : res;} else {str = str.substr(index + 1) + char;}}return res;
};
// console.log(longStr('abcdaouritbjhlfcldfkmdf'));

十二. 简单实现loadsh的_get

function get(obj, keys, det){return keys.split(/\./).reduce((o, v) => {return (o || {})[v]}, obj) || det
}
get({ 'a': [{ 'b': { 'c': 3 } }] }, 'a.0.b.c', '1111')

十三. 实现add(1)(2)(3)

function add(...args){return args.reduce((a,b)=> a+b)
}function currying(fn){let args=[]return function temp(...newArgs){if(newArgs.length){args = [...args, newArgs]return temp} else {let val = fn.apply(this, args)args = []return val}}
}
let addCurry = currying(add)
// console.log(addCurry(1)(2)(3)(4, 5)())
// console.log(addCurry(1)(2)(3, 4, 5)())
// console.log(addCurry(1)(2, 3, 4, 5)())
// console.log(add(1)(2)(3).toString())

十四. 手写数组转树 & 树转数组

数组->树

const arrtree = [{id:1, parentId: null, name: 'a'},{id:2, parentId: null, name: 'b'},{id:6, parentId: 3, name: 'f'},{id:7, parentId: 4, name: 'g'},{id:8, parentId: 7, name: 'h'},{id:3, parentId: 1, name: 'c'},{id:4, parentId: 2, name: 'd'},{id:5, parentId: 1, name: 'e'}
]function arrayTree(arr){if(!Array.isArray(arr) || !arr.length) returnlet map = {}arr.forEach(v => map[v.id] = v)let list = []arr.forEach(v => {const item = map[v.parentId]if(item){( item.children || (item.children = [])).push(v)} else {list.push(v)}})return list
}// console.log(arrayTree(arrtree))

树->数组

// 将树数据转化为平铺数据
flatTreeData(treeData: any[], childKey = 'children') {const arr: any[] = [];const expanded = (data: any) => {if (data && data.length > 0) {data.filter((d: any) => d).forEach((e: any) => {arr.push(e);expanded(e[childKey] || []);});}};expanded(treeData);return arr;
}

十五. 手写一个数据双向绑定

let input = document.getElementById('value')
let text = document.getElementById('text')let data = { value: '' }
input.value = data.value
text.innerHTML = data.value
// 数据劫持
Object.defineProperty(data, 'value', {set: function (val) {input.value = valtext.innerHTML = val},get: function () {return input.value}
})
input.addEventListener('keyup', function (){data.value = input.valueconsole.log(data);
})

十六. 手写 new

const customNew = (Func, ...args) => {// 创建一个空的简单JavaScript对象const obj = Object.create({})// 链接该对象到prototypeobj.__proto__ = Func.prototype// 将创建的对象作为this的上下文 const res = Func.apply(obj, args)// 如果该函数没有返回对象,则返回thisreturn res instanceof Object ? res : obj
}

十七. 判断一个单词是否回文

function checkPalindrom(str) {return str.split('').reverse().join() === str
}

十八. 实现call、apply、bind

Function.prototype.customCall = function (context, ...args) {if (this === Function.prototype) {return undefined; // 用于防止 Function.prototype.customCall() 直接调用}const _this = context || window;_this.fn = thisconst res = _this.fn(...args)delete _this.fnreturn res
}
Function.prototype.customApply = function (context, args) {if (this === Function.prototype) {return undefined; // 用于防止 Function.prototype.customApply() 直接调用}const _this = context || window;_this.fn = thislet resif (Array.isArray(args)) {res = _this.fn(...args)} else {res = _this.fn()}delete _this.fnreturn res
}
Function.prototype.customBind = function (context) {if (this === Function.prototype) {throw new TypeError('Error')}const _this = thisreturn function F(...args) {if (this instanceof F) {return new _this(...args)} else {return _this.apply(context, args)}}
}

十九. 手写 instanceof

右边变量的原型在左边变量的原型链上

const customInstanceof = (target, origin) => {let proto = target.__proto__, index = 0while (proto !== origin.prototype) {proto = proto.__proto__if (!proto) {return false}index += 1}console.log(index);return true
}

二十. 数组去重

// ES6
Array.from(new Set(arr))// 遍历
var arrreset = [1,2,3,4,3,4,5,2,7,4]
var arrreset1 = Array.from(new Set(arrreset))let arrreset2 = []
arrreset.forEach(v => {if(arrreset2.indexOf(v) === -1){arrreset2.push(v)}
})

二十一. 手写object.create

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

二十二. 深拷贝deepclone

export function deepClone(source: any) {if (!source && typeof source !== 'object') {throw new Error('error arguments');}const targetObj: any = source.constructor === Array ? [] : {};Object.keys(source).forEach((keys: any) => {if (source[keys] && typeof source[keys] === 'object') {targetObj[keys] = deepClone(source[keys]);} else {targetObj[keys] = source[keys];}});return targetObj;
}

JSON.stringify() 缺点

  • 对象中有时间类型的时候,序列化之后会变成字符串类型。
  • 对象中有undefined和Function类型数据的时候,序列化之后会直接丢失。
  • 对象中有NaN、Infinity和-Infinity的时候,序列化之后会显示 null。
  • 对象循环引用的时候,会直接报错。
const obj = {nan:NaN,infinityMax:1.7976931348623157E+10308,infinityMin:-1.7976931348623157E+10308,undef: undefined,fun: () => { console.log('叽里呱啦,阿巴阿巴') },date:new Date,
}

二十三. 防抖、节流+装饰器

/*** 节流* @param fn  函数* @param delay 延迟时间(毫秒单位),默认200毫秒* @returns {Function}*/
export function throttle(fn: any, delay = 200) {let lastCall = 0return function (...args: any[]) {const now = new Date().getTime()if (now - lastCall < delay) returnlastCall = nowfn(...args)}
}/*** 防抖* @param fn {Function}   实际要执行的函数* @param delay {Number}  延迟时间,也就是阈值,单位是毫秒(ms)* @return {Function}     返回一个“去弹跳”了的函数*/function debounce(fn, delay) {let timer = nullreturn function(...arg) {clearTimeout(timer)timer = setTimeout(() => {fn(...arg)}, delay)}}/*** 装饰器防抖* @param {number} delay* @return {*}*/
export const Debounce = (delay = 500) => {let timeoutDeb = null;return (target, propertyKey, descriptor) => {const original = descriptor.value;descriptor.value = function(...args) {clearTimeout(timeoutDeb);timeoutDeb = setTimeout(() => {original.call(this, ...args);}, delay);};};
};

低频

一. rgb转16进制颜色

利用正则 match 方法取出r、g、b 数值,然后分别转换16进制(不足两位补0),最后拼接加 # 转大写

function rgb2hex(rgb) {const rgb = rgb.match(/\d+/g); // 分别取出连续数字即为 r/g/bconst hex = (n) => {return ("0" + Number(n).toString(16)).slice(-2);}return rgb.reduce((acc, cur) => acc + hex, '#').toUpperCase()
}

获取 rgb 还可以通过 split(‘,’)

const rgb = sRGB.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')

二. 千位符

function toTousands(num){let n = num.toString().split('.')[0],decal = num.toString().indexOf('.') !== 1 ? '.' + num.toString().split('.')[1] : '';let result = ''while(n.length > 3) {result = ',' + n.slice(-3) + resultn = n.silce(0, n.length-3)}return (n || '') + result + decal
}

三. JS手写最大并发控制实现方式

class LimitResquest {constructor(limit){this.limit = limitthis.currentSum = 0this.task = []}request(reqFn) {if(!reqFn || !(reqFn instanceof Function)){console.error('当前请求不是一个function', reqFn)return}this.task.push(reqFn)if(this.currentSum < this.limit){this.run()}}async run() {try{++this.currentSumconst fn = this.task.shift()await fn()} catch(e) {console.error(e)} finally{--this.currentSumif(this.task.length > 0){this.run()}}}
}let a = () => new Promise((resolve) => {setTimeout(() => {resolve(1)}, 1000)
}).then((data) => console.log(data))let b = () => new Promise((resolve) => {setTimeout(() => {resolve(2)}, 1000)
}).then((data) => console.log(data))let c = () => new Promise((resolve) => {setTimeout(() => {resolve(3)}, 1000)
}).then((data) => console.log(data))let d = () => new Promise((resolve) => {setTimeout(() => {resolve(4)}, 1000)
}).then((data) => console.log(data))let limitResquest = new LimitResquest(2)
limitResquest.request(a)
limitResquest.request(b)
limitResquest.request(c)
limitResquest.request(d)
limitResquest.request(a)
limitResquest.request(b)
limitResquest.request(c)
limitResquest.request(d)

四. 实现一个简单的路由

class Route{constructor(){this.route = {} // 整个路由对象this.currtHash = '' // 当前路由this.freshRoute = this.freshRoute.bind(this)window.addEventListener('load', this.freshRoute, false)window.addEventListener('hashchange', this.freshRoute, false)}storeRoute(path, cb){this.route[path] = cb || function() {}}freshRoute(){this.currtHash = location.hash.slice(1) || '/'this.route[this.currtHash]()}
}

五. rem 自适应

function resetFontSize(){var baseFontsize = 100var designWidth = 750var width = window.innerWidthvar currFonrsize = (width/designWidth) * baseFontsizedocument.getElementsByTagName('html')[0].fontSzie = currFonrsize + 'px'
}window.onresize = function (){resetFontSize()
}resetFontSize()

六. 懒加载

// 获取所有图片的标签
const imgs = document.getElementsByTagName('img')
// 获取可视区域的高度
const viewHeight = window.innerHeight || document.documentElement.clientHeight
// num 计算当前显示到哪一张图片,避免每次都从第一张图片开始检查是否需要
let num = 0, len = imgs.lengthfunction lazyLoad() {for(let i = num; i < len; i ++) {// 用可视区域的高度减去匀速顶部距离可视区域的高度let distance = viewHeight - migs[i].getBoundingClientRect().topif(distance > 20){imgs[i].src = imgs[i].getAttribute('data-src')}}
}// 最好添加节流
window.addEventListener('sroll', lazyLoad)

七. 手写fetch拦截器

(function() {let interceptor_req = [], interceptor_res = []function c_fetch(url, init = {}) {init.method = init.method || 'GET'interceptor_req.forEach(interceptor => {init = interceptor(init)})return new Promise((resolve, reject) => {fetch(url, init).then(res => {interceptor_res.forEach(interceptor => {res = interceptor(res)})resolve(res)}).catch(err => {console.error(err)})})}c_fetch.interceptor = {request: {use: function(callback){interceptor_req.push(callback)}},response: {use: function(callback){interceptor_res.push(callback)}}}// export default c_fetch})()

码字不易,觉得有帮助的小伙伴点个赞~

前端面试手写题汇总大全(含答案)-- 持续更新相关推荐

  1. 2022必会的前端面试手写题

    前端面试题视频讲解 将数字每千分位用逗号隔开 数字有小数版本: let format = n => {let num = n.toString() // 转成字符串let decimals = ...

  2. 2021 年大厂面试高频架构题汇总(附答案详解)

    金九银十到了,有很多即将面试的朋友咨询怎么做准备.这里必须要和大家再强调一下要准备的7大方面!总结起来包括:1至2门你最熟悉的编程语言+数据结构和算法题+计网+操作系统+设计模式+数据库+开发框架. ...

  3. 2021 年架构技术面试大厂高频题汇总(附答案详解)

    很多面试架构师的朋友跟我吐槽面试太难,接二连三的做了Offer陪跑人.事实确实如此,做一个架构师需要具备的技术栈是真的庞杂:开发框架.数据库.设计模式.数据结构.算法...... 这也就导致了大多数人 ...

  4. 西北师范大学地理与环境科学学院考研真题汇总(高等数学)持续更新。。。

    西北师范大学地理与环境学科学院研究生入学考试的所有专业(地图学与地理信息系统.自然地理学.人文地理学.环境科学.环境工程)的数学均为自主命题,复习参考教材为同济大学第五版.

  5. 【面试】970- 一文帮你搞定90%的JS手写题

    还在害怕手写题吗,本文可以帮你扩展并巩固自己的JS基础,顺便搞定90%的手写题.在工作中还可以对常用的需求进行手写实现,比如深拷贝.防抖节流等可以直接用于往后的项目中,提高项目开发效率.不说废话了,下 ...

  6. java面笔试_java笔试手写算法面试题大全含答案

    java笔试手写算法面试题大全含答案 1.统计一篇英文文章单词个数. public class WordCounting { public static void main(String[] args ...

  7. 一文帮你搞定90%的JS手写题

    还在害怕手写题吗,本文可以帮你扩展并巩固自己的JS基础,顺便搞定90%的手写题.在工作中还可以对常用的需求进行手写实现,比如深拷贝.防抖节流等可以直接用于往后的项目中,提高项目开发效率.不说废话了,下 ...

  8. 手写bind_一次搞定前端“四大手写”

    本文首发于个人GitHub博客 要问程序员最心虚的面试题,如果要投票选择,手撕代码一定是前三位的.其中在前端领域,以手写 bind.手写深拷贝.手写 EventHub(发布-订阅).手写 Promis ...

  9. 前端关于html的面试题,关于java:前端面试HTML面试题汇总

    前端面试-HTML面试题汇总 博客阐明 文章所波及的材料来自互联网整顿和集体总结,意在于集体学习和教训汇总,如有什么中央侵权,请分割自己删除,谢谢! 1.语义化 为什么要语义化? a. 为了在没有CS ...

最新文章

  1. OpenCV学习笔记大集锦
  2. c语言中包含math.h的时用gcc编译要加-lm参数
  3. linux基础学习(十)
  4. java web 数据库操作_Java Web----Java Web的数据库操作(二)
  5. [Vue.js]实战 -- 电商项目(二)
  6. 基于Token的身份验证——JWT
  7. java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/Str
  8. 外测要做好测试记录,并照相保留证据
  9. 我的知识管理工具列表
  10. seaweedFS基本使用
  11. 计算机蓝屏代码0x0000007b,蓝屏代码0X0000007B各机型解决方法及原因
  12. Android信任Https自签名证书详细教程
  13. 用银行卡号查相应的归属银行,卡种类
  14. 解决C#读取文本文件乱码
  15. 松本行弘(Ruby发明者):Emacs怎样改变了我的人生
  16. 企业邮箱注册购买优惠有哪些,企业工作邮箱怎么注册购买?
  17. 困兽之斗--写给基地培训的同学们
  18. 通过形状先验引导的3D目标检测方法(Disp R-CNN解读)
  19. speedoffice表格如何设置文本框的透明度?
  20. oracle中的用户详解 【转】

热门文章

  1. 惠新宸:PHP在百度的应用现状及展望
  2. 目标检测SSD算法笔记
  3. mysql2006错误
  4. C++ JSON 库 jsoncpp 新API的使用方法(CharReaderBuilder / StreamWriterBuilder)
  5. 防抖(debounce)和节流(throttle)
  6. 生病卖了2套房、花费130万才明白:不要轻易买保险
  7. RSTP协议原理与配置整——RSTP配置实例
  8. 北京各品牌的折扣店汇总
  9. 绿色红色荧光标记PLGA,PLA,PVP,PVA,明胶纳米纤维膜定制
  10. unity 自由落体运动的物理学知识