文章目录

  • 实现一下观察者模式
  • 实现一下工厂模式
  • 实现一下单例模式
  • 设计一个lazyMan,实现以下功能:
  • 实现一个简单的EventEmitter
  • 手撕实现原生js的一些方法(call、apply、bind、map、reduce、filter、splice、pop、push、unshift、shift)
  • 实现对象的迭代器,使得对象可以使用for of方式循环
  • 手撕实现一下前端路由
  • 简单实现一下实现事件委托(ul中很多个li标签)
  • 用setTimeOut实现一下setInterval
  • 实现一下curry函数(柯里化函数)
  • 实现类似 add(1)(2) add(1, 2, 3)(10) add(1)(2)(3)(4)(5)的函数
  • 实现一下es5的几种继承方法
  • 代码实现一下new方法过程
  • 简单实现promise方法
  • 实现promise.all和promise.race
  • 实现promise.allsettled 和 promise.any
  • 实现一个promise失败重传函数(重传限定多少次)
  • 实现一个串行执行的promise
  • 实现一个并发控制数量的promise
  • 实现一个基于promise的ajax函数
  • 实现一个深拷贝函数
  • 用函数实现一下instanceof和typeof的功能
  • 实现防抖和节流函数
  • 实现数组扁平化函数flatten
  • 创建形如a.b.c.d嵌套对象
  • 二叉树的前中后序(深度优先)及层序(广度优先)遍历
  • 实现以下lodash的_get()函数
  • 实现将url中?后的参数转化为js对象
  • 实现大数相加
  • 字符串的全排列
  • 多维数组的全排列
  • 判断是否为平衡二叉树
  • 两数之和、三数之和
实现一下观察者模式
class Subject {constructor () {this.state = 0this.observe = []}getState () {return this.state}setState (val) {this.state = valthis.observe.forEach((item) => {item.update()})}attach(ob) {this.observe.push(ob)}
}class Observe {constructor (name, sub) {this.name = namethis.sub = subthis.sub.attach(this)}update () {console.log(`${this.name} update, state: ${this.sub.getState()}`)}
}
实现一下工厂模式
// 工厂模式
class Role {constructor(options) {this.role = options.role;this.permissions = options.permissions;}show() {const str = `是一个${this.role}, 权限:${this.permissions.join(', ')}`;console.log(str);}
}class SimpleFactory {constructor(role) {if(typeof this[role] !== 'function') {throw new Error('参数只能为 admin 或 developer');}return this[role]();}admin() {return new Role({role: '管理员',permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论']});}developer() {return new Role({role: '开发者',permissions: ['开发', '推送', '提问', '评论']});}
}
实现一下单例模式
class LoginForm {constructor () {this.state = 'hide'}show () {if (this.state === 'show') {console.log('已经show')}this.state = 'show'console.log('登录框show成功')}hide () {if (this.state === 'hide') {console.log('已经hide')}this.state = 'hide'console.log('登录框hide成功')}
}LoginForm.getInstance = (function () {let instance = nullreturn function () {if (!instance) {instance = new LoginForm()}return instance}
})()
设计一个lazyMan,实现以下功能:

LazyMan(‘Tony’);
// Hi I am Tony

LazyMan(‘Tony’).sleep(10).eat(‘lunch’);
// Hi I am Tony
// 等待了10秒…
// I am eating lunch
LazyMan(‘Tony’).eat(‘lunch’).sleep(10).eat(‘dinner’);
// Hi I am Tony
// I am eating lunch
// 等待了10秒…
// I am eating diner
LazyMan(‘Tony’).eat(‘lunch’).eat(‘dinner’).sleepFirst(5).sleep(10).eat(‘junk food’);
// Hi I am Tony
// 等待了5秒…
// I am eating lunch
// I am eating dinner
// 等待了10秒…
// I am eating junk food

考察点:运用发布订阅者模式,js事件执行机制

function lazyMan(name) {// 任务清单队列this.taskList = []this.name = nameconsole.log(`Hi I am ${name}`)setTimeout(() => this.next())
}
lazyMan.prototype = {// 订阅方法eat (food) {const fn = () => {console.log(`i am eating ${food}`);this.next()}this.taskList.push(fn)return this},sleepFirst (time) {const fn = () => {setTimeout(() => {console.log(`等待了${time}秒`);this.next()}, time*1000)}this.taskList.unshift(fn)return this},sleep (time) {const fn = () => {setTimeout(() => {console.log(`等待了${time}秒`);this.next()}, time*1000)}this.taskList.push(fn)return this},// 事件发布next () {const fn = this.taskList.shift()fn && fn()}
}
function LazyMan (name) {return new lazyMan(name)
}LazyMan('Tony')
LazyMan('Tony').sleep(10).eat('lunch')
LazyMan('Tony').eat('lunch').sleep(10).eat('dinner')
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')
实现一个简单的EventEmitter

就是事件的触发机制,发布订阅者的一个实现,和上题思路一样

function eventEmitter () {// 定义事件池this.eventpool = {}// 事件绑定this.on = function (event, callback) {this.eventpool[event] ? this.eventpool[event].push(callback) : this.eventpool[event]}// 事件分发this.emit = function (event, ...args) {this.eventpool[event] && this.eventpool[event].forEach((cb) => cb(...args))}// 事件解绑this.off (event) {if (this.eventpool[event]) {delete this.eventpool[event]}}// 事件只绑定执行一次this.once(event, callback) {this.on(event, (...args) => {callback(...args)this.off(event)})}
}

考察点:事件触发与事件监听器功能的封装

以上两个手撕题涉及的
发布 + 订阅
DOM 的事件机制就是发布订阅模式最常见的实现,这大概是前端最常用的编程模型了,监听某事件,当该事件发生时,监听该事件的监听函数被调用。
————————————————————————————————————
发布订阅模式,阮一峰在《Javascript 异步编程的 4 种方法》,中:

我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe
pattern),又称"观察者模式"(observer pattern)。

手撕实现原生js的一些方法(call、apply、bind、map、reduce、filter、splice、pop、push、unshift、shift)
  • 实现call方法
// call的实现(实现call)
Function.prototype.call = function (context) {context = context ? Object(context) : windowcontext.fn = thislet args = [...arguments].slice(1)let res = context.fn(...args)delete context.fnreturn res
}
  • 实现apply方法
// apply的实现
Function.prototype.apply = function (context, arr) {context = context ? Object(context) : windowcontext.fn = thislet resif (!arr) {res = context.fn()} else {res = context.fn(...arr)}delete context.fnreturn res
}
  • 实现bind方法
// 函数实现
function bind(fn, context) {let args = Array.prototype.slice.call(arguments, 2)return function () {return fn.apply(context, args.concat(Array.prototype.slice.call(arguments)))}
}
// 原型链修改
Function.prototype.bind = function (context) {let that = thislet args = Array.prototype.slice.call(arguments, 1)return function () {return that.apply(context, args.concat(Array.prototype.slice.call(arguments)))}
}// 更为完整的方法
Function.prototype.bind2 = function (context) {if (typeof this !== "function") {throw new Error("Function.prototype.bind - what is trying to be bound is not callable");}var self = this;var args = Array.prototype.slice.call(arguments, 1);var fNOP = function () {};var fBound = function () {var bindArgs = Array.prototype.slice.call(arguments);return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));}fNOP.prototype = this.prototype;fBound.prototype = new fNOP();return fBound;
}
  • 实现map方法
// 不使用其他js函数实现map
Array.prototype.map = function (fn) {let arr = []for (let i = 0; i < this.length; i++) {arr. push(fn(this[i], i, this))}return arr
}
// 使用reduce实现map
Array.prototype.map = function (fn) {return this.reduce((arr, cur, index) => {arr.push(fn(cur, index, this))return arr}, [])
}
  • 实现reduce方法
// 实现reduce
Array.prototype.reduce = function (fn, initVal) {let res = initVal ? initVal : 0for (let i = initVal ? 1 : 0; i < this.length; i++) {res = fn(res, this[i], i, this)}return res
}
  • 实现filter方法
Array.prototype.filter = function (fn) {let arr = []for (let i = 0; i < this.length; i++) {if (fn(this[i], i, this)) {arr.push(this[i])}}return arr
}
  • 实现splice方法
// 实现原生splice
Array.prototype.splice = function (again, num) {let frontArr = []let afterArr = []let args = Array.prototype.slice.call(arguments, 2)for (let i = 0; i < again; i++) {frontArr[i] = this[i]}if (args.length) {for (let i = 0; i < args.length; i++) {frontArr[again + i] = this[i]}}for (let i = again + num, j = 0; i < this.length; i++, j++) {afterArr[j] = this[i]}let deleteArr = []for (let i = 0; i < num; i++) {deleteArr[i] = this[again + i]}let a = frontArr.concat(afterArr)this.length = a.lengthfor (let i = 0; i < a.length; i++) {this[i] = a[i]}return deleteArr
}
  • 实现push方法
Array.prototype.push = function () {let args = argumentsfor (let i = 0; i < args.length; i++) {this[this.length] = args[i]}return this.length
}
  • 实现pop方法
Array.prototype.pop = function () {if(this.length === 0) returnlet val = this[this.length - 1]this.length -= 1return val
}
  • 实现unshift方法
Array.prototype.unshift = function () {let args = [...arguments]let len = args.lengthfor (let i = this.length - 1; i >= 0; i--) {this[i + len] = this[i]}for (let i = 0; i < len; i++) {this[i] = args[i]}return this.length
}
  • 实现shift方法
Array.prototype.shift = function () {let removeVal = this[0]for (let i = 0; i < this.length; i++) {if (i !== this.length - 1) {this[i] = this[i + 1]}}this.length -= 1return removeVal
}
实现对象的迭代器,使得对象可以使用for of方式循环
// 用generator函数,返回[key,value]
Object.prototype[Symbol.iterator] = function* iterEntries() {let keys = Object.keys(this);for (let i = 0; i < keys.length; i++) {let key = keys[i];yield [key, obj[key]];}
}// 普通函数, ,返回[key,value]
Object.prototype[Symbol.iterator] = function () {const keys = Object.keys(this);let index = 0;return {next: () => {return {value: [keys[index], this[keys[index++]]], // 每次迭代的结果done: index > keys.length // 迭代结束标识 false停止迭代,true继续迭代};}}
}
手撕实现一下前端路由

考察点:
前端路由的两种实现方式
1、location.hash+hashchange事件。
2、history.pushState()+popstate事件。

// 这里用hash的路由方法实现
function Router() {this.routes = {}this.curUrl = ''this.init()
}Router.prototype.route = function (path, cb) {this.routes[path] = cb || function () {}
}Router.prototype.refresh = function () {this.curUrl = location.hash.slice(1) || '/'this.routes[this.curUrl] && this.routes[this.curUrl]()
}Router.prototype.init = function () {window.addEventListener('load', this.refresh.bind(this))window.addEventListener('hashchange', this.refresh.bind(this))
}// 用个例子试用一下
var router = new Router()
router.route('/', function () {var body = document.getElementById('page-type')body.innerHTML = '首页耶耶耶'
})
router.route('/news', function () {var body = document.getElementById('page-type')body.innerHTML = '新闻耶耶耶'
})
简单实现一下实现事件委托(ul中很多个li标签)
window.onload = function () {let ul1 =  document.getElementById('ul1')let li = ul1.getElementsByTagName('li')// 方法一(挨个li标签加方法)---不推荐// for (let i = 0; i < li.length; i++) {//     li[i].addEventListener('click', function (e) {//         alert(li[i].innerHTML)//     })// }// 方法二:(事件委托)ul1.onclick = function (e) {var ev = e || window.eventif (ev.target.nodeName.toLowerCase() === 'li') {alert(ev.target.innerHTML)} }
}
用setTimeOut实现一下setInterval
function mySetInterval (cb, time) {const fn = () => {cb()setTimeout(() => {fn()}, time)}setTimeout(fn, time)
}
实现一下curry函数(柯里化函数)
// 函数柯里化
// 方式一:
function curry(func) {// 存储已传入参数let _args = [];// 做一个闭包function _curry(...args) {// 把参数合并_args = _args.concat(args);// 如果参数够了就执行if (_args.length >= func.length) {const result = func(..._args);_args = [];return result;}// 继续返回此函数else {return _curry;}}return _curry;
}
// 测试代码
function add1(a, b, c) {return a + b + c;
}
let testAdd = curry(add1);
console.log(testAdd(1)(2)(3));
console.log(testAdd(1, 2)(3));
console.log(testAdd(1)(2, 3));
// 方式二:
let curry = (fn) =>judge = (...args) =>args.length >= fn.length ?fn(...args): (...arg) => judge(...args, ...arg)
// 例子
function add (a, b, c) {return a + b + c
}
var res = curry(add(1))
var res1 = res(2)
console.log(res1(3))
实现类似 add(1)(2) add(1, 2, 3)(10) add(1)(2)(3)(4)(5)的函数
function add(...rest) {let args = rest;if (args.length === 0) return 0;return function partialSum(...rest) {args = [...args, ...rest]if (rest. length === 0) {return args.reduce((pre, cur) => pre + cur)} else {return partialSum}}
}
add(1)(2,3,5)(3)(3,4)(5)()
实现一下es5的几种继承方法
// 1:原型链继承
function Cat1 () {}
Cat1.prototype = new Animal()
// 2:构造函数继承
function Cat2 (name) {Animal.call(this)this.name = name || 'Cat'
}
// 3: 实例继承
function Cat3 (name) {let instance = new Animal()instance.name = name || 'Cat'return instance
}
// 4:拷贝继承
function Cat4 (name) {var animal = new Animal()for (let p in animal) {Cat4.prototype[p] = animal[p]}Cat4.prototype.name = name || 'Cat'
}
// 5: 组合继承(常用)
function Cat5 () {Animal.call(this)this.name = name || 'Cat'
}
Cat5.prototype = new Animal()
Cat5.prototype.constructor = Cat5
// 6:寄生组合继承
function Cat5 () {Animal.call(this)this.name = name || 'Cat'
}
(function () {var Super = function () {}Super.prototype = Animal.prototypeCat5.prototype = new Super()
})()
Cat5.prototype.constructor = Cat5
代码实现一下new方法过程

考察点:
(new的过程:
• 创建一个空对象,将它的引用赋给 this,继承函数的原型。
• 通过 this 将属性和方法添加至这个对象
• 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)

// new的过程
function parent (name, age) {this.name = namethis.age = age
}
parent.prototype.getName = function () {console.log(this.name);
}let newObj = function (par) {let args = Array.prototype.slice.call(arguments, 1)let child = Object.create(par.prototype)par.apply(child, args)return child
}let children = newObj(parent, 'haha', 22)children.getName()
console.log(children.hasOwnProperty('say'));

用函数实现new:

function creat () {// 获得父类构造函数。(也就是传入的arguments中的第一个参数)var Con = [].shift.call(arguments)// 创建一个空对象并将其链接到原型,使得可以访问到构造函数的原型属性var obj = Object.create(Con.prototype)// 绑定this实现继承,obj可以访问构造函数中的属性var result = Con.apply(obj, arguments)// 优先返回构造函数返回的对象return result instanceof Object ? result : obj
}
简单实现promise方法

考察点:
promise的原理及各种状态

function myPromise (fn) {let val = null, succcallbacks = [], failcallbacks = []let data = null, reason = null, status = 'pending'this.then = function (fulfilled, rejected) {if (status === 'pending') {succcallbacks.push(fulfilled)failcallbacks.push(rejected)return this} else if (status === 'resolve') {fulfilled(data)} else {rejected(reason)}}function resolve (val) {setTimeout(() => {data = valstatus = 'fulfilled'succcallbacks.forEach((callback) => {callback(val)})}, 0)}function reject (val) {setTimeout(() => {reason = valstatus = 'rejected'failcallbacks.forEach((callback) => {callback(val)})}, 0)}fn(resolve, reject)
}
// 测试
function test(num) {return new myPromise((resolve, reject) => {setTimeout(() => {resolve(num)}, 1000)})
}
实现promise.all和promise.race
function promiseAll (arr) {const res = [];let count = 0;const len = arr.length;return new Promise((resolve, reject) => {for (let i = 0; i < len; i++) {arr[i] && arr[i].then(res => {res.push(res);count++if (count === len) return resolve(res);})}})
}function promiseRace (arr) {return new Promise((resolve, reject) => {for (let i = 0; i < len; i++) {arr[i] && arr[i].then(res => {return resolve(res)})}})
}
实现promise.allsettled 和 promise.any
// promise.allsettled实现
MyPromise.allSettled = function(values) {let promises = [].slice.call(values)return new MyPromise((resolve, reject) => {let result = [], count = 0promises.forEach(promise => {MyPromise.resolve(promise).then(value=>{result.push({status: 'fulfilled', value})}).catch(err=>{result.push({status: 'rejected', value: err})}).finally(()=>{if(++count === promise.length) {resolve(result)}})})})
}// promise.any实现
MyPromise.any = function(promises){return new Promise((resolve,reject)=>{promises = Array.isArray(promises) ? promises : []let len = promises.length// 用于收集所有 reject let errs = []// 如果传入的是一个空数组,那么就直接返回 AggregateErrorif(len === 0) return reject(new AggregateError('All promises were rejected'))promises.forEach((promise)=>{promise.then(value=>{resolve(value)},err=>{len--errs.push(err)if(len === 0){reject(new AggregateError(errs))}})})})
}
实现一个promise失败重传函数(重传限定多少次)
// promise重传
function tryPromise (fn, count) {fn().then((result) => {return result}, (err) => {if (err) {if (count) {tryPromise(fn, count--)}}})
}
实现一个串行执行的promise
function serpromise(arr) {arr.reduce((pre, next, index, carr)=>{return pre.then(next)}, Promise.resolve())
}// arr里的元素为返回的promise对象
serpromise(arr)
实现一个并发控制数量的promise
// 模拟请求
function requestUrl(url) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(url, new Date());}, 1000 * Math.random());})
}
// 异步队列实现
function limitQueue(urls, limit) {// 完成任务数let i = 0;// 填充满执行队列for (let excuteCount = 0; excuteCount < limit; excuteCount++) {run();}// 执行一个任务function run() {// 构造待执行任务 当该任务完成后 如果还有待完成的任务 继续执行任务new Promise((resolve, reject) => {const url = urls[i];i++;resolve(requestUrl(url))}).then(() => {if (i < urls.length) run()})}
};// async + promise 解决方案
async function asyncProcess(urls, limit) {const task = [];const ans = [];for (let i = 0; i < urls.length; i++) {const p = requestUrl(urls[i]).then(res => {ans.push(res);task.splice(task.indexOf(p), 1);});task.push(p);if (task.length === limit) {await Promise.race(task);}}await Promise.allSettled(task);return ans;
}// 更易懂
function multiRequest(urls, maxNum) {const ret = [];let i = 0;let resolve;const promise = new Promise(r => resolve = r);const addTask = () => {if (i >= arr.length) {return resolve();}const task = request(urls[i++]).finally(() => {addTask();});ret.push(task);}while (i < maxNum) {addTask();}return promise.then(() => Promise.all(ret));
}
实现一个基于promise的ajax函数
// 使用promise封装ajax函数
var xmlhttp = new XMLHttpRequest()
function ajax (type, api, data) {return new Promise((resolve, reject) => {xmlhttp.onreadystatechange = () => {if (xmlhttp.readyState === 4) {if (xmlhttp.status === 200) {resolve(xmlhttp.responseText)} else {reject(xmlhttp.status)}}}xmlhttp.open(type, api, data)xmlhttp.send})
}
实现一个深拷贝函数
function deepCopy (obj) {if (!obj instanceof Object) {throw new Error('Not a Object')}var newObj = Array.isArray(obj) ? [] : {}for (let key in obj) {newObj[key] = obj[key] instanceof Object ? deepCopy(obj[key]) : obj[key]}return newObj
}
用函数实现一下instanceof和typeof的功能
function instance_of(L, R){ // 表示表达式左边R和右边LR = R.prototype // 取 R 的显示原型L = L.__proto__ // 取 L 的隐式原型while (true) {if (L === null) return false // Object.prototype.__proto__ === nullif (R === L) return true // 这里重点:当 O 严格等于 L 时,返回 true(即实例在原型链上)L = L.__proto__}
}function type_of(obj) {    return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();
}
实现防抖和节流函数
// 防抖
function debounce (fn) {let instance = nullreturn function () {clearTimeout(instance)instance = setTimeout(fn, 2000)}
}
// 节流
function throttle (fn, wait, maxTime) {let instance = nulllet startTime = new Date()return function () {let endTime = new Date()let args = argumentslet that = thisclearTimeout(instance)if (endTime - startTime >= maxTime) {fn.apply(that, args)startTime = endTime} else {instance = setTimeout(fn, wait)}}
}
实现数组扁平化函数flatten
// 数组扁平化
let flatArr123 = [1, 3, [2, [5, 6]], [4], [[6, 7, [8]]]]
// 1:
flatArr123.flat(Infinity)
// 2:
function flatten2(arr) {return arr.reduce((result, item) => {return result.concat(Array.isArray(item) ? flatten2(item) : item)}, [])
}
// 3:
function flatten3(arr) {return arr.toString().split(',').map(x => +x)
}
// 4:
function flatten4(arr) {while (arr.some(item => Array.isArray(item))) {arr = [].concat(...arr)}return arr
}
// 5:
function flatten5(arr) {var res = []arr.map(item => {if (Array.isArray(item)) {res = res.concat(flatten5(item))} else {res.push(item)}})return res
}
// 指定层数
Array.prototype.myFlat = function (dep = 1) {return this.reduce((acc, val) => {return  acc.concat(Array.isArray(val) && dep > 0 ? // 这里的三目就是防止这个现象:[3].concat([4]) // 结果为[3, 4]val.myFlat(--dep) : Array.isArray(val) ? [val] : val);}, [])
创建形如a.b.c.d嵌套对象
function buildObj(dataObjStr) {var array = dataObjStr.split(".")var result = {}var temp = resultfor (var i = 0; i < array.length; i++) {temp = temp[array[i]] = {}}return result
}
二叉树的前中后序(深度优先)及层序(广度优先)遍历

递归:

// 前序遍历
function preorderTraversal(root, arr = []) {if (root === null) returnarr.push(root.val)preorderTraversal(root.left, arr)preorderTraversal(root.right, arr)return arr
}
// 中序遍历
function inorderTraversal(root, arr = []) {if (root === null) returninorderTraversal(root.left, arr)arr.push(root.val)inorderTraversal(root.right, arr)return arr
}
// 后序遍历
function postorderTraversal(root, arr = []) {if (root === null) returnpostorderTraversal(root.left, arr)postorderTraversal(root.right, arr)arr.push(root.val)return arr
}

非递归:

// 前序遍历
function preorderTraversal(root) {var stack = []var res = []var p = rootif (root === null) return []while (p || stack.length) {while (p) {stack.push(p)res.push(p.val)p = p.left}p = stack.pop()p = p.right}return res
}
// 中序遍历
function inorderTraversal(root) {var stack = []var res = []var p = rootif (root === null) return []while (p || stack.length) {while (p) {stack.push(p)p = p.left}p = stack.pop()res.push(p.val)p = p.right}return res
}
// 后序遍历
function postorderTraversal(root, arr = []) {var stack = []var res = []var p = rootwhile (p || stack.length) {if (p) {stack.push(p)res.unshift(p.val)p = p.right} else {var node = stack.pop()p = node.left}}return res
}

层序遍历:

function isCompleteTree (tree) {let queue = []queue.push(tree)while (queue.length) {let p = queue.shift()if (p) {queue.push(p.left)queue.push(p.right)} else {while (queue.length) {if (queue.shift()) {return false}}}}return true
}

建立完全二叉树测试:

// 这里用个简单的递归方法简历完全二叉树来测试
// 树的节点
function bitTreeNode(val) {this.val = valthis.right = nullthis.left = null
}
// 递归建立
function creatTree(n) {if (n === 1) return new bitTreeNode(1)let root = new bitTreeNode(n)root.left = creatTree(n - 1)root.right = creatTree(n - 1)return root
}
var tree = creatTree(5)
console.log(tree)
实现以下lodash的_get()函数

知识点:
主要实现对象的嵌套调用情况,如 a.b.c.d.e,因为这么写很容易抛出异常。(正常写法得 a && a.b && a.b.c && a.b.c.d && a.b.c.d.e)

function _get (source, path, defaultValue = undefined) {const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.')let res = sourcefor (let val of paths) {res = Object(res)[val]if (res === undefined) {return defaultValue}}return res
}
实现将url中?后的参数转化为js对象
function urlToObject () {let url = window.location.hrefurl = url.indexOf('?') === -1 ? 'https://www.nowcoder.com/search?type=post&order=time&query=前端&subType=0&page=2' : url.split('?')[1].join('')let r = new RegExp(/([^?&=]+)=([^?&=]+)/g)var obj = {}url.replace(r, function (res, $1, $2) {console.log(res, $1, $2)obj[$1] = $2})console.log(obj)
}
实现大数相加

知识点:

因为JavaScript的Number类型是遵循IEEE 754规范表示的,这就意味着JavaScript能精确表示的数字是有限的,JavaScript可以精确到个位的最大整数是9007199254740992,也就是2的53次方,超过这个范围就会精度丢失,造成JavaScript无法判断大小。

function maxSum (str1, str2) {if (typeof str1 !== 'string' || typeof str2 !== 'string') {throw new Error('传入参数不是字符串')}let arr1 = str1.split('').reverse()let arr2 = str2.split('').reverse()let Len = Math.max(arr1.length, arr2.length)let flag = 0let res = []for (let i = 0; i < Len; i++) {arr1[i] = Number(arr1[i]) || 0arr2[i] = Number(arr2[i]) || 0let sum = arr1[i] + arr2[i] + flagif (sum >= 10) {flag = 1sum = sum % 10} else {flag = 0}res.push(sum)if (i === Len - 1 && flag) {res.push(flag)}}return res.reverse().join('')
}
字符串的全排列
function permutation (str) {let res = [str[0]]for (let i = 1; i < str.length; i++) {res = fp(res, str[i])}return [...new Set(res)].sort()
}function fp (arr, ch) {let tmp = []for (let i = 0; i <= arr[0].length; i++) {tmp = tmp.concat(arr.map((x) => x.slice(0, i) + ch + x.slice(i)))}return tmp
}
// console.log(permutation('abc'));
多维数组的全排列
//输入:[['A', 'B'], ['a', 'b'], ['1', '2']];
//输出:['Aa1','Aa2','Ab1','Ab2','Ba1','Ba2','Bb1','Bb2']// reduce版本
function permutaion( arrs) {return arrs. reduce(( pre, cur) =>{const result = [];for (let i = 0; i < pre. length; i++) {for (let j = 0; j < cur. length; j++) {result.push( pre[ i] + cur[ j])}}return result})
}// 递归版本
function pailie (arr) {let res = []const dfs = (path,index) => {if(path.length == arr.length) {res.push(path)return}arr.forEach((n,i) => {n.forEach(k => {if(i == index) {dfs(path + k,index+1)}})})}dfs("",0)return res
}
判断是否为平衡二叉树

知识点:
运用层序遍历的知识点来判断是否为平衡二叉树

function isCompleteTree (tree) {let queue = []queue.push(tree)while (queue.length) {let p = queue.shift()if (p) {queue.push(p.left)queue.push(p.right)} else {while (queue.length) {if (queue.shift()) {return false}}}}return true
}
两数之和、三数之和

ps:具体题目百度(面试常考算法)

// 两数之和
function twoSum (arr, sum) {let flag = {}for (let i = 0; i < arr.length; i++) {if (!flag[arr[i]]) {flag[sum - arr[i]] = arr[i]} else {return [arr[i], flag[arr[i]]]}}
}
// 三数之和
function threeSum (arr, sum) {let res = []if (!arr.length) return resarr.sort((a, b) => a - b)for (let i = 0; i < arr.length - 1; i++) {if (arr[i] >= sum) return resif (arr[i] === arr[i + 1]) continuelet L = i + 1let R = arr.length - 1while (L < R) {if (arr[i] + arr[L] + arr[R] === sum) {res.push([arr[i], arr[L], arr[R]])while (L < R && arr[L] === arr[L + 1]) L++while (L < R && arr[R] === arr[R - 1]) R--L++R--}else if (arr[i] + arr[L] + arr[R] < sum) L++else if (arr[i] + arr[L] + arr[R] > sum) R--}}return res
}

常见js手撕题及算法总结相关推荐

  1. LeetCode912. 排序数组(JavaScript手撕各种排序算法)

    题目链接:https://leetcode-cn.com/problems/sort-an-array/submissions/ 这是一道好题目,可以用来练习手撕各种排序算法,别直接调用api错过了这 ...

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

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

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

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

  4. js手撕——bind的模拟实现

    在前端面试中,可能会出现一些手撕模拟实现的题目,这里进行一些记录. 原生api js中中提供了 call, apply, bind 用于手动指定this的指向 概念 调用 call 和 apply 的 ...

  5. 那些前端用js手搓出来的算法与数据结构(一)链表篇

    首先,一句话,前端学算法有啥用 低情商:好跟后端battle.顺便互相吹吹牛 高情商:提高业务能力.提炼基本功 实际:大厂大公司面试要问,没办法. 工作中:应用场景不多,基本都是后端玩的 那到底学不学 ...

  6. 2021年的几次面试让我死磕了17道JS手写题!

    1.浅拷贝.深拷贝的实现 浅拷贝 // 1. ...实现 let copy1 = {...{x:1}}// 2. Object.assign实现 let copy2 = Object.assign({ ...

  7. 手撕Twitter推荐算法

    Twitter近期开源了其推荐系统源码[1,2,3],截止现在已经接近36k star.但网上公开的文章都是blog[1]直译,很拗口,因此特地开个系列系统分享下.系列涵盖: Twitter整体推荐系 ...

  8. 手撕自动驾驶算法——多目标追踪:imm交互式多模型

    文章目录 基本原理 流程图 公式推导 Mixing Prediction Updates Combination 基本原理 状态估计问题依赖于对象运动模型来预测和更新对象状态.然而,在城市情况下,被跟 ...

  9. 【神策数据面试】手撕题

    翻转字符串 例如 sensorsdata@cn 翻转后变为nc@atadsrosnes function reverseWords(source){ let result = "; //TO ...

最新文章

  1. jBPM4.4 no jBPM DB schema:
  2. onrsd.exe应用程序错误
  3. 第一次经历黑客攻击服务器系统
  4. Python CNN风格迁移
  5. tomcat默认连接数_Tomcat的默认连接器
  6. 在使用angularjs过程,ng-repeat中track by的作用
  7. 重设MYSQL数据库ROOT用户的密码
  8. C++实现md5加密或计算文件的唯一性识别
  9. HDU1210 Eddy's 洗牌问题【递推函数+模拟】
  10. README.md怎么写比较好
  11. 数字图像处理原理与实践:基于Visual C++开发
  12. mac终端支持git
  13. python2000个5除以84的余数_python中负数除法的求商和取余的问题
  14. 红米AirDots无线蓝牙耳机连接win10笔记本
  15. 计算机新生导论感言,新生入学感言范文精选
  16. cf一直连接服务器,玩穿越火线显示连接服务器超时怎么回事?原因分析及解决方法...
  17. IDEA运行项目时停不下来
  18. windows消息分类PostMessage、SendMessage
  19. 火水未濟 (易經大意 韓長庚)
  20. 【web学习之Mysql】数据库-----查询操作------大全

热门文章

  1. 177本名著浓缩成了177句话!经典收藏!太有道理了!
  2. 计算机基础科学悖论,细思极恐的科学悖论,盘点科学史上最难解释的10大悖论...
  3. node-red教程3.3 file控件介绍
  4. 总结 nginx access.log 太大如何清理
  5. multisim常用d触发器_请问这个符号的d触发器在multisim中的芯片代号是什么
  6. 美容,美甲,美妆等美业如何接入小程序
  7. 罗素问题 ——来自知乎大神
  8. 《C语言小程序篇---1》——实现一个“富婆通讯录“(超详细)
  9. 127Echarts - 关系图(Graph Life Expectancy)
  10. 程序员成长的四个简单技巧,你 get 了吗?