总结
-
框架原理真的深入某一部分具体的代码和实现方式时,要多注意到细节,不要只能写出一个框架。
-
算法方面很薄弱的,最好多刷一刷,不然影响你的工资和成功率😯
-
在投递简历之前,最好通过各种渠道找到公司内部的人,先提前了解业务,也可以帮助后期优秀 offer 的决策。
-
要勇于说不,对于某些 offer 待遇不满意、业务不喜欢,应该相信自己,不要因为当下没有更好的 offer 而投降,一份工作短则一年长则 N 年,为了幸福生活要慎重选择!!!
喜欢这篇文章文章的小伙伴们点赞+转发支持,你们的支持是我最大的动力!
if (hasKey) {
this.cache.delete(key)
}
this.cache.set(key, val)
if (this.cache.size > this.size) {
this.cache.delete(this.cache.keys().next().value)
}
}
}
12、简单实现 发布订阅模式
题目描述:实现一个发布订阅模式拥有
on emit once off
方法
class EventEmitter {
constructor() {
this.cache = {}
}
on(name, fn) {
const tasks = this.cache[name]
if (tasks) {
this.cache[name].push(fn)
} else {
this.cache[name] = [fn]
}
}
off(name, fn) {
const tasks = this.cache[name]
if (task) {
const index = tasks.findIndex(item => item === fn)
if (index >= 0) {
this.cache[name].splice(index, 1)
}
}
}
emit(name, …args) {
// 复制一份。防止回调里继续on,导致死循环
const tasks = this.cache[name].slice()
if (tasks) {
for (let fn of tasks) {
fn(…args)
}
}
}
once(name, cb) {
function fn(…args) {
cb(args)
this.off(name, fn)
}
this.on(name, fn)
}
}
13、实现JSON.parse
题目描述:实现
JSON.parse
function parse (json) {
return eval(“(” + json + “)”);
}
14、将DOM转化成树结构对象
题目描述:
将上方的DOM转化为下面的树结构对象
{
tag: ‘DIV’,
children: [
{ tag: ‘SPAN’, children: [] },
{
tag: ‘UL’,
children: [
{ tag: ‘LI’, children: [] },
{ tag: ‘LI’, children: [] }
]
}
]
}
实现如下:
function dom2tree(dom) {
const obj = {}
obj.tag = dom.tagName
obj.children = []
dom.childNodes.forEach(child => obj.children.push(dom2tree(child)))
return obj
}
15、将树结构转换为DOM
题目描述:
{
tag: ‘DIV’,
children: [
{ tag: ‘SPAN’, children: [] },
{
tag: ‘UL’,
children: [
{ tag: ‘LI’, children: [] },
{ tag: ‘LI’, children: [] }
]
}
]
}
将上方的树结构对象转化为下面的DOM
实现如下:
// 真正的渲染函数
function _render(vnode) {
// 如果是数字类型转化为字符串
if (typeof vnode === “number”) {
vnode = String(vnode);
}
// 字符串类型直接就是文本节点
if (typeof vnode === “string”) {
return document.createTextNode(vnode);
}
// 普通DOM
const dom = document.createElement(vnode.tag);
if (vnode.attrs) {
// 遍历属性
Object.keys(vnode.attrs).forEach((key) => {
const value = vnode.attrs[key];
dom.setAttribute(key, value);
});
}
// 子数组进行递归操作
vnode.children.forEach((child) => dom.appendChild(_render(child)));
return dom;
}
16、判断一个对象有环引用
题目描述:验证一个对象有无环引用
var obj = {
a: {
c: [
1, 2
]
},
b: 1
}
obj.a.c.d = obj
console.log(cycleDetector(obj)) // true
实现思路:用一个数组存储每一个遍历过的对象,下次找到数组中存在,则说明环引用
function cycleDetector(obj) {
const arr = [obj]
let flag = false
function cycle(o) {
const keys = Object.keys(o)
for (const key of keys) {
const temp = o[key]
if (typeof temp === ‘object’ && temp !== null) {
if (arr.indexOf(temp) >= 0) {
flag = true
return
}
arr.push(temp)
cycle(temp)
}
}
}
cycle(obj)
return flag
}
17、计算一个对象的层数
题目描述:给你一个对象,统计一下它的层数
const obj = {
a: { b: [1] },
c: { d: { e: { f: 1 } } }
}
console.log(loopGetLevel(obj)) // 4
实现如下:
function loopGetLevel(obj) {
var res = 1;
function computedLevel(obj, level) {
var level = level ? level : 0;
if (typeof obj === ‘object’) {
for (var key in obj) {
if (typeof obj[key] === ‘object’) {
computedLevel(obj[key], level + 1);
} else {
res = level + 1 > res ? level + 1 : res;
}
}
} else {
res = level > res ? level : res;
}
}
computedLevel(obj)
return res
}
18、对象的扁平化
题目描述:
const obj = {
a: {
b: 1,
c: 2,
d: {e: 5}
},
b: [1, 3, {a: 2, b: 3}],
c: 3
}
flatten(obj) 结果返回如下
// {
// ‘a.b’: 1,
// ‘a.c’: 2,
// ‘a.d.e’: 5,
// ‘b[0]’: 1,
// ‘b[1]’: 3,
// ‘b[2].a’: 2,
// ‘b[2].b’: 3
// c: 3
// }
实现如下:
const isObject = (val) => typeof val === “object” && val !== null
function flatten(obj) {
if (!isObject(obj)) return
const res = {}
const dfs = (cur, prefix) => {
if (isObject(cur)) {
if (Array.isArray(cur)) {
cur.forEach((item, index) => {
dfs(item,
${prefix}[${index}]
)})
} else {
for(let key in cur) {
dfs(cur[key],
${prefix}${prefix ? '.' : ''}${key}
)}
}
} else {
res[prefix] = cur
}
}
dfs(obj, ‘’)
return res
}
// 测试
console.log(flatten(obj))
19、实现(a == 1 && a == 2 && a == 3)为true
题目描述:
实现(a == 1 && a == 2 && a == 3)为true
// 第一种方法
var a = {
i: 1,
toString: function () {
return a.i++;
}
}
console.log(a == 1 && a == 2 && a == 3) // true
// 第二种方法
var a = [1, 2, 3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3); // true
// 第三种方法
var val = 0;
Object.defineProperty(window, ‘a’, {
get: function () {
return ++val;
}
});
console.log(a == 1 && a == 2 && a == 3) // true
20、实现限制并发的Promise调度器
题目描述:JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
addTask(1000,“1”);
addTask(500,“2”);
addTask(300,“3”);
addTask(400,“4”);
的输出顺序是:2 3 1 4
整个的完整执行流程:
一开始1、2两个任务开始执行
500ms时,2任务执行完毕,输出2,任务3开始执行
800ms时,3任务执行完毕,输出3,任务4开始执行
1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行
1200ms时,4任务执行完毕,输出4
实现如下:
class Scheduler {
constructor(limit) {
this.queue = []
this.limit = limit
this.count = 0
}
add(time, order) {
const promiseCreator = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order)
resolve()
}, time)
})
}
this.queue.push(promiseCreator)
}
taskStart() {
for(let i = 0; i < this.limit; i++) {
this.request()
}
}
request() {
if (!this.queue.length || this.count >= this.limit) return
this.count++
this.queue.shift()().then(() => {
this.count–
this.request()
})
}
}
// 测试
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(time, order);
};
addTask(1000, “1”);
addTask(500, “2”);
addTask(300, “3”);
addTask(400, “4”);
scheduler.taskStart();
21、实现lazyMan函数
题目描述:
实现一个LazyMan,可以按照以下方式调用:
LazyMan(“Hank”)输出:
Hi! This is Hank!
LazyMan(“Hank”).sleep(10).eat(“dinner”)输出
Hi! This is Hank!
//等待10秒…
Wake up after 10
Eat dinner~
LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出
Hi This is Hank!
Eat dinner~
Eat supper~
LazyMan(“Hank”).eat(“supper”).sleepFirst(5)输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
实现如下:
class _LazyMan {
constructor(name) {
this.tasks = []
const task = () => {
console.log(
Hi! This is ${name}
)this.next()
}
this.tasks.push(task)
setTimeout(() => {
this.next()
}, 0)
}
next() {
const task = this.tasks.shift()
task && task()
}
sleep(time) {
this.sleepWrapper(time, false)
return this
}
sleepFirst(time) {
this.sleepWrapper(time, true)
return this
}
sleepWrapper(time, first) {
const task = () => {
setTimeout(() => {
console.log(
Wake up after ${time}
)this.next()
}, time * 1000)
}
if (first) {
this.tasks.unshift(task)
} else {
this.tasks.push(task)
}
}
eat(food) {
const task = () => {
console.log(
Eat ${food}
);this.next();
};
this.tasks.push(task);
return this;
}
}
// 测试
const lazyMan = (name) => new _LazyMan(name)
lazyMan(‘Hank’).sleep(1).eat(‘dinner’)
lazyMan(‘Hank’).eat(‘dinner’).eat(‘supper’)
lazyMan(‘Hank’).eat(‘supper’).sleepFirst(5)
22、实现add函数
题目描述:实现一个 add 方法 使计算结果能够满足如下预期:
- add(1)(2)(3)()=6
- add(1,2,3)(4)()=10
function add(…args1) {
let allArgs = […args1]
function fn(…args2) {
if (!args2.length) return fn.toString()
allArgs = […allArgs, …args2]
return fn
}
fn.toString = function () {
return allArgs.reduce((pre, next) => pre + next)
}
return fn
}
// 测试
console.log(add(1)(2)(3)())
console.log(add(1, 2)(3)())
23、实现一个合格的深拷贝
24、实现 Promise
25、实现 async/await
Array篇
定义一个测试数组
const players = [
{ name: ‘科比’, num: 24 },
{ name: ‘詹姆斯’, num: 23 },
{ name: ‘保罗’, num: 3 },
{ name: ‘威少’, num: 0 },
{ name: ‘杜兰特’, num: 35 }
]
26、forEach
参数代表含义
-
item:遍历项
-
index:遍历项的索引
-
arr:数组本身
Array.prototype.sx_forEach = function (callback) {
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this)
}
}
players.sx_forEach((item, index, arr) => {
console.log(item, index)
})
// { name: ‘科比’, num: 24 } 0
// { name: ‘詹姆斯’, num: 23 } 1
// { name: ‘保罗’, num: 3 } 2
// { name: ‘威少’, num: 0 } 3
// { name: ‘杜兰特’, num: 35 } 4
27、map
参数代表含义
-
item:遍历项
-
index:遍历项的索引
-
arr:数组本身
Array.prototype.sx_map = function (callback) {
const res = []
for (let i = 0; i < this.length; i++) {
res.push(callback(this[i], i, this))
}
return res
}
console.log(players.sx_map((item, index) =>
${item.name}--${item.num}--${index}
))// [ ‘科比–24–0’, ‘詹姆斯–23–1’, ‘保罗–3–2’, ‘威少–0–3’, ‘杜兰特–35–4’ ]
28、filter
参数代表含义
-
item:遍历项
-
index:遍历项的索引
-
arr:数组本身
Array.prototype.sx_filter = function (callback) {
const res = []
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this) && res.push(this[i])
}
return res
}
console.log(players.sx_filter(item => item.num >= 23))
// [
// { name: ‘科比’, num: 24 },
// { name: ‘詹姆斯’, num: 23 },
// { name: ‘杜兰特’, num: 35 }
// ]
29、every
参数代表含义
-
item:遍历项
-
index:遍历项的索引
-
arr:数组本身
Array.prototype.sx_every = function (callback) {
let flag = true
for (let i = 0; i < this.length; i++) {
flag = callback(this[i], i, this)
if (!flag) break
}
return flag
}
console.log(players.sx_every(item => item.num >= 23)) // false
console.log(players.sx_every(item => item.num >= 0)) // true
30、some
参数代表含义
-
item:遍历项
-
index:遍历项的索引
-
arr:数组本身
Array.prototype.sx_some = function (callback) {
let flag = false
for (let i = 0; i < this.length; i++) {
flag = callback(this[i], i, this)
if (flag) break
}
return flag
}
console.log(players.sx_some(item => item.num >= 23)) // true
console.log(players.sx_some(item => item.num >= 50)) // false
31、reduce
参数代表含义
-
pre:前一项
-
next:下一项
-
index:当前索引
-
arr:数组本身
Array.prototype.sx_reduce = function (callback, …args) {
let start = 0, pre
if (args.length) {
pre = args[0]
} else {
pre = this[0]
start = 1
}
for (let i = start; i < this.length; i++) {
pre = callback(pre, this[i], i, this)
}
return pre
}
// 计算所有num相加
const sum = players.sx_reduce((pre, next) => {
return pre + next.num
}, 0)
console.log(sum) // 85
32、findIndex
参数代表含义
-
item:遍历项
-
index:遍历项的索引
-
arr:数组本身
Array.prototype.sx_findIndex = function (callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return i
}
}
return -1
}
console.log(players.sx_findIndex(item => item.name === ‘科比’)) // 0
console.log(players.sx_findIndex(item => item.name === ‘安东尼’)) // -1
33、find
参数代表含义
-
item:遍历项
-
index:遍历项的索引
-
arr:数组本身
Array.prototype.sx_find = function (callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return this[i]
}
}
return undefined
}
console.log(players.sx_find(item => item.name === ‘科比’)) // { name: ‘科比’, num: 24 }
console.log(players.sx_find(item => item.name === ‘安东尼’)) // undefined
34、fill
用处:填充数组
参数代表含义
-
initValue:填充的值
-
start:开始填充索引,默认0
-
end:结束填充索引,默认length
Array.prototype.sx_fill = function (value, start = 0, end) {
end = end || this.length
for (let i = start; i < end; i++) {
this[i] = value
}
return this
}
console.log(players.sx_fill(‘林三心’, 1, 3))
// [
// { name: ‘科比’, num: 24 },
// ‘林三心’,
// ‘林三心’,
// ‘林三心’,
// { name: ‘杜兰特’, num: 35 }
// ]
35、includes
用处:查找元素,查到返回
true
,反之返回false
,可查找NaN
Array.prototype.sx_includes = function (value, start = 0) {
if (start < 0) start = this.length + start
const isNaN = Number.isNaN(value)
for (let i = start; i < this.length; i++) {
if (this[i] === value || (isNaN && Number.isNaN(this[i])) {
return true
}
}
return false
}
console.log([1, 2, 3].sx_includes(2)) // true
console.log([1, 2, 3, NaN].sx_includes(NaN)) // true
console.log([1, 2, 3].sx_includes(1, 1)) // false
36、join
用处:将数组用分隔符拼成字符串,分隔符默认为
,
Array.prototype.sx_join = function (s = ‘,’) {
let str = ‘’
for(let i = 0; i < this.length; i++) {
str = i === 0 ?
${str}${this[i]}
:${str}${s}${this[i]}
}
return str
}
console.log([1, 2, 3].sx_join()) // 1,2,3
console.log([1, 2, 3].sx_join('')) // 12*3
37、flat
Array.prototype.sx_flat = function (num = Infinity) {
let arr = this
let i = 0
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(…arr)
i++
if (i >= num) break
}
return arr
}
const testArr = [1, [2, 3, [4, 5]], [8, 9]]
console.log(testArr.sx_flat(1))
// [1, 2, 3, 4, 5, 8, 9]
38、splice
难点
- 截取长度和替换长度的比较,不同情况
Array.prototype.sx_splice = function (start, length, …values) {
if (length === 0) return []
length = start + length > this.length - 1 ? this.length - start : length
console.log(length)
const res = [], tempArr = […this]
for (let i = start; i < start + values.length; i++) {
this[i] = values[i - start]
}
this.length = start + values.length
if (values.length < length) {
const cha = length - values.length
console.log(cha)
for (let i = start + values.length; i < tempArr.length; i++) {
this[i] = tempArr[i + cha]
}
this.length = this.length - cha
}
if (values.length > length) {
for (let i = start + length; i < tempArr.length; i++) {
this.push(tempArr[i])
}
}
for (let i = start; i < start + length; i++) {
res.push(tempArr[i])
}
return res
}
Object篇
定义一个测试对象
const obj = {
name: ‘林三心’,
age: 22,
gender: ‘男’
}
39、entries
用处:将对象转成键值对数组
Object.prototype.sx_entries = function (obj) {
const res = []
for (let key in obj) {
obj.hasOwnProperty(key) && res.push([key, obj[key]])
}
return res
}
console.log(Object.sx_entries(obj))
// [ [ ‘name’, ‘林三心’ ], [ ‘age’, 22 ], [ ‘gender’, ‘男’ ] ]
40、fromEntries
用处:跟
entries
相反,将键值对数组转成对象Object.prototype.sx_fromEntries = function (arr) {
const obj = {}
for (let i = 0; i < arr.length; i++) {
const [key, value] = arr[i]
obj[key] = value
}
return obj
}
console.log(Object.sx_fromEntries([[‘name’, ‘林三心’], [‘age’, 22], [‘gender’, ‘男’]]))
// { name: ‘林三心’, age: 22, gender: ‘男’ }
41、keys
用处:将对象的key转成一个数组合集
Object.prototype.sx_keys = function (obj) {
const keys = []
for (let key in obj) {
obj.hasOwnProperty(key) && res.push(key)
}
return keys
}
console.log(Object.keys(obj))
// [ ‘name’, ‘age’, ‘gender’ ]
42、values
用处:将对象的所有值转成数组合集
Object.prototype.sx_values = function (obj) {
const values = []
for (let key in obj) {
obj.hasOwnProperty(key) && values.push(obj[key])
}
return values
}
console.log(Object.sx_values(obj))
// [ ‘林三心’, 22, ‘男’ ]
43、instanceOf
用处:A instanceOf B,判断A是否经过B的原型链
function instanceOf(father, child) {
const fp = father.prototype
var cp = child.proto
while (cp) {
if (cp === fp) {
return true
}
cp = cp.proto
}
return false
}
function Person(name) {
this.name = name
}
总结
-
框架原理真的深入某一部分具体的代码和实现方式时,要多注意到细节,不要只能写出一个框架。
-
算法方面很薄弱的,最好多刷一刷,不然影响你的工资和成功率😯
-
在投递简历之前,最好通过各种渠道找到公司内部的人,先提前了解业务,也可以帮助后期优秀 offer 的决策。
-
要勇于说不,对于某些 offer 待遇不满意、业务不喜欢,应该相信自己,不要因为当下没有更好的 offer 而投降,一份工作短则一年长则 N 年,为了幸福生活要慎重选择!!!
喜欢这篇文章文章的小伙伴们点赞+转发支持,你们的支持是我最大的动力!