一、实现Object.create()
理解
- 将传入对象作为新建对象的原型
let obj = {}
let newObj = Object.create(obj)
console.log(newObj.__proto__ == obj); // true
实现
- 创建一个构造函数 让构造函数的原型指向传入的对象
- 返回构造函数的实例 f
f.__proto__ == F.prototype == obj
function create(obj) {
function F() {}
F.prototype = obj
F.prototype.constructor = F
return new F()
}
let obj = {}
let newObj = create(obj)
console.log(newObj.__proto__ == obj) // true
二、实现instanceof
理解
instanceof可以判断引用数据类型 但是不能判断基本数据类型- 本质是在判断在对象的原型链中能否找到对应类型的原型
Array.prototype.__proto__ == Object.prototypeFunction.Prototype.__proto__ == Object.prototype
let a = {}
let b = 123
let c = function() {}
let d = []
console.log(a instanceof Object) // true
console.log(b instanceof Number) // false
console.log(c instanceof Function) // true
console.loh(d instanceof Array) // true
console.log(c instanceof Object) // true
console.loh(d instanceof Object) // true
实现
function myInstanceof(left, right) {
if(typeof left !== 'object' && typeof left !== 'function' || left === null) return false
let proto = Object.getPrototypeOf(left)
let prototype = right.prototype
while(true) {
if(!proto) return false
if(proto === prototype) return true
proto = Object.getPrototypeOf(proto)
}
}
let a = {}
let b = 123
let c = function() {}
let d = []
console.log(myInstanceof(a, Object)) // true
console.log(myInstanceof(b, Number)) // false
console.log(myInstanceof(c, Object)) // true
console.log(myInstanceof(d, Object)) // true
三、实现new操作符
- 创建一个新对象,这个对象的__proto__要指向构造函数的原型对象
- 执行构造函数
- 返回值为object类型则作为new方法的返回值返回,否则返回上述全新对象
function _new() {
const [constructor, ...args] = [...arguments]
if(typeof constructor != 'function') return
let obj = {}
obj.__proto__ = constructor.prototype
let result = constructor.apply(obj, args)
if(result && (typeof result == 'object' || typeof result == 'function')) {
return result
}
return obj
}
四、实现防抖函数
- 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
- 应用场景:浏览器窗口大小resize避免次数过于频繁。登录等按钮避免发送多次请求
function debounce(fn, delay) {
let timer = null
return function(...args) {
let context = this
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, args)
}, delay)
}
}
五、实现节流函数
- 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效
- 节流重在加锁
flag = false
function throttle(fn, delay) {
let flag = true
return function(...args) {
let context = this
if(flag) {
flag = false
setTimeout(() => {
flag = true
fn.apply(context, args)
}, delay)
}
}
}
六、判断类型函数
- 如果为
null就返回'null' - 如果为引用数据类型 就通过
Object.prototype.toString.call方法判断 - 基本数据类型就通过
typeof判断
function getType(value) {
if(value === null) return 'null'
if(typeof value === 'object') {
let valueClass = Object.prototype.toString.call(value)
let type = valueClass.split(' ')[1].split('')
type.pop()
return type.join('').toLowerCase()
}
return typeof value
}
七、手写call函数
Function.prototype.mycall = function() {
let [context, ...args] = [...arguments]
if(typeof this !== 'function') return
context = context || window
context.fn = this
let result = context.fn(...args)
delete context.fn
return result
}
let obj = {
a: 1
}
function output(b,c) {
console.log(this.a,b,c);
}
output.mycall(obj,2,3) // 1 2 3
八、手写apply函数
Function.prototype.myapply = function() {
if(typeof this !== 'function') return
let context = arguments[0] || window
context.fn = this
let result = null
if(arguments[1]) {
result = context.fn(arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
let obj = {
a: 1
}
function output(arr) {
console.log(this.a, arr);
}
output.myapply(obj,[2,3]) // 1 [ 2, 3 ]
九、手写bind函数
bind的普通实现
Function.prototype.mybind = function(context, ...args){
return (...newArgs) => {
return this.call(context,...args, ...newArgs)
}
}
let obj = {
z: 3
}
function fn(x, y) {
console.log(x, y, this.z);
}
var returnFunc = fn.mybind(obj, 1)
returnFunc(2) // 1 2 3
new对this的优先级高于bind,所以需要区分调用bind返回函数时是普通调用还是new调用
Function.prototype.mybind = function(context) {
if(typeof this !== 'function') return
var args = [...arguments].slice(1), fn = this
return function Fn() {
return fn.apply(
this instanceof Fn ? this : context, // new对this的的优先级更高
args.concat(...arguments)
)
}
}
let obj = {
z: 3
}
function fn(x, y, z) {
this.x = x
this.y = y
z && (this.z = z)
console.log(this.x, this.y, this.z);
}
fn.prototype.age = 25
var returnFunc = fn.mybind(obj, 1)
var person = new returnFunc(2, 4) // 1 2 4
// 因为new对this的优先级更高
console.log(person.age) // undefined
// 无法调用原型属性
- 用
new调用bind返回函数,并可以使用原型上的方法和属性。借助一个空的构造函数F实现。
Function.prototype.mybind = function(context) {
if(typeof this !== 'function') return
var args = [...arguments].slice(1), fn = this
var returnFunc = function () {
return fn.apply(
this instanceof returnFunc ? this : context,
args.concat(...arguments)
)
}
var F = function() {}
F.prototype = this.prototype
returnFunc.prototype = new F()
return returnFunc
}
let obj = {}
function fn() {}
fn.prototype.age = 26
var returnFunc = fn.mybind(obj)
var person = new returnFunc()
console.log(person.age) // 26
十、函数柯里化
- 简单实现一个
add的柯里化
function add() {
let args = [...arguments]
let inner = function() {
args.push(...arguments)
return inner
}
inner.toString = function() {
return args.reduce((prev, curt) => prev + curt)
}
return inner
}
console.log(add(1)(2)(3)(4) + '') // 10
- 已知道需要进行柯里化的
addSum函数的入参个数是确定的,3
// 方法一
let currying = (fn, ...args) =>
fn.length > args.length ?
(...arguments) => currying(fn, ...args, ...arguments) :
fn(...args)
let addSum = (a, b ,c) => a + b + c
let add = currying(addSum)
console.log(add(1)(2)(3)) // 6
// 方法二
function currying(fn, ...args) {
let allArgs = [...args]
return function inner() {
allArgs = [...allArgs, ...arguments]
if(fn.length > allArgs.length) {
return inner
} else {
return fn(...allArgs)
}
}
}
let addSum = (a, b ,c) => a + b + c // 10
let add = currying(addSum)
console.log(add(1)(2)(3))
- 已知道需要进行柯里化的
addSum函数的入参个数是不确定的
function currying(fn, ...args) {
let allArgs = [...args]
return function inner() {
if(arguments.length) {
allArgs = [...allArgs, ...arguments]
return inner
} else {
return fn(...allArgs)
}
}
}
let addSum = (...args) => args.reduce((prev, curt) => prev + curt)
let add = currying(addSum)
console.log(add(1)(2)(3)()) // 6
十一、浅拷贝
Object.assign()Object.assign(target,source1,source2,....)
- 扩展运算符
let obj2 = {...obj}
Array.prototype.slicearr.slice()
Array.prototype.concatarr.concat()
- 代码实现浅拷贝
function shallowCopy(obj) {
if(typeof obj !== 'object' || !obj) return
let newObj = Array.isArray(obj) ? [] : {}
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = obj[key]
}
}
return newObj
}
let obj = { a: 1, b: { c: 2 } }
let newObj = shallowCopy(obj)
obj.a = 2
obj.b.c = 1
console.log(obj) // { a: 2, b: { c: 1 } }
console.log(newObj) // { a: 1, b: { c: 1 } }
十二、深拷贝
function deepCopy(obj) {
if(typeof obj !== 'object' || !obj) return
let newObj = Array.isArray(obj) ? [] : {}
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]
}
}
return newObj
}
let obj = { a: 1, b: { c: 2 } }
let newObj = deepCopy(obj)
obj.a = 2
obj.b.c = 1
console.log(obj) // { a: 2, b: { c: 1 } }
console.log(newObj) // { a: 1, b: { c: 2 } }
十三、实现简单的AJAX
- 原始版
const SERVER_URL = '/server'
const xml = new XMLHttpRequest()
xml.open('GET', SERVER_URL, true) // open(方法,路径,是否异步加载,用户授权)
xml.onreadystatechange = function() {
if(this.readyState !== 4) return
if(this.status === 200) {
handle(this.response);
} else {
console.error(this.statusText);
}
}
xml.onerror = function() {
console.error(this.statusText);
}
xml.setRequestHeader('Accept', 'application/json')
xml.responseType = 'json'
xml.send()
promise封装版
function AJAX(url) {
return new Promise((resolve, reject) => {
const xml = new XMLHttpRequest()
xml.open('GET', url, true) // open(方法,路径,是否异步加载,用户授权)
xml.onreadystatechange = function() {
if(this.readyState !== 4) return
if(this.status === 200) {
resolve(this.response);
} else {
reject(this.statusText);
}
}
xml.onerror = function() {
reject(this.statusText);
}
xml.setRequestHeader('Accept', 'application/json')
xml.responseType = 'json'
xml.send()
})
}
十四、控制并发
- 每完成一个请求就会有一个新的请求加入,保证请求的最大数为
maxCount
function limitRunTask(tasks, n) {
return new Promise((resolve, reject) => {
let index = 0, finish = 0, start = 0, res = [];
function run() {
if (finish == tasks.length) {
resolve(res)
return
}
while (start < n && index < tasks.length) {
start++
let cur = index
tasks[index++].then(v => {
res[cur] = v
}).finally(() => {
start--
finish++
run()
})
}
}
run()
})
}
function fetch(url) {
return new Promise((resolve,reject) => {
resolve(url)
})
}
const urlList = []
for(let i = 0; i < 100; i++) {
urlList.push('url' + i)
}
const promises = urlList.map(item => fetch(item))
const maxCount = 10
limitRunTask(promises, maxCount).then(res => {
console.log(res)
})
- 将数组分成多个数目相等的小数组,每次最多只开启
maxConcurrentNum个并发请求,以此来控制并发数量。每当一组请求完成后再发送新的一批请求
const urls = []
for(let i = 0; i < 100; i++) {
urls.push('url' + i)
}
const maxConcurrentNum = 10; // 最大并发数
// 数组分块,chunk表示每批次数量,返回数组二维数组
function chunk(arr, chunk) {
let result = [];
for (let i = 0, len = arr.length; i < len; i += chunk) {
result.push(arr.slice(i, i + chunk));
}
return result;
}
// 异步请求方法
function fetchUrl(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
})
})
}
// 对url数组进行分块处理
const chunkedUrls = chunk(urls, maxConcurrentNum);
(async function () {
try {
for (let urls of chunkedUrls) {
const promises = urls.map(url => fetchUrl(url));
// 等待所有promises完成执行,并将结果存入results数组中
const results = await Promise.all(promises);
console.log('results:', results);
}
} catch (err) {
console.error(err);
}
})()
十五、实现一个 sleep 函数
function sleep(delay) {
let start = (new Date()).getTime()
while((new Date()).getTime() - start < delay) continue
}
function test() {
console.log('111');
sleep(2000);
console.log('222');
}
test()
十六、实现(5).add(3).sub(2)
Number.prototype.add = function(value) {
if(typeof value != 'number') throw new Error('请输入数字~')
return this.valueOf() + value
}
Number.prototype.sub = function(value) {
if(typeof value != 'number') throw new Error('请输入数字~')
return this.valueOf() - value
}
console.log((5).add(3).sub(2))
十七、实现一个字符串匹配算法,从长度为 slen 的字符串 S 中,查找长度为 tlen 的字符串 T,若存在返回所在位置
function getStrIndex(S, T) {
let slen = S.length
let tlen = T.length
if(!slen || !tlen || slen < tlen) return -1
for(let i = 0; i < slen; i++) {
let j = 0,k = i
if(S[k] == T[j]) {
k++
j++
while(j < tlen && k < slen) {
if(S[k] != T[j]) break
else {
j++
k++
}
}
if(j == tlen) return i
}
}
return -1
}
console.log(getStrIndex("Hello World", "rl")) // 8
十八、使用递归实现累加
function add(x, y) {
if(x === y) return x
else {
return y + add(x, y - 1)
}
}
console.log(add(1,100)) // 5050
十九、async await的实现
const getData = () => new Promise(resolve => setTimeout(() => resolve("data"), 1000))
function asyncToGenerator(generatorFunc) {
return function() {
const gen = generatorFunc.apply(this, arguments)
return new Promise((resolve, reject) => {
function step(key, arg) {
let generatorResult
try {
generatorResult = gen[key](arg)
} catch (error) {
return reject(error)
}
const { value, done } = generatorResult
if (done) {
return resolve(value)
} else {
return Promise.resolve(value).then(val => step('next', val), err => step('throw', err))
}
}
step("next")
})
}
}
var test = asyncToGenerator(
function* testG() {
// await被编译成了yield
const data = yield getData()
console.log('data: ', data);
const data2 = yield getData()
console.log('data2: ', data2);
return 'success'
}
)
test().then(res => console.log(res))
参考地址:手写async await的最简实现(20行) - 掘金 (juejin.cn)
二十、实现一个模块 math ,支持链式调用
class Math {
constructor(value) {
let hasInitValue = true;
if (value === undefined) {
value = NaN;
hasInitValue = false;
}
Object.defineProperties(this, {
value: {
enumerable: true,
value: value,
},
hasInitValue: {
enumerable: false,
value: hasInitValue,
},
});
}
add(...args) {
const init = this.hasInitValue ? this.value : args.shift();
const value = args.reduce((pv, cv) => pv + cv, init);
return new Math(value);
}
minus(...args) {
const init = this.hasInitValue ? this.value : args.shift();
const value = args.reduce((pv, cv) => pv - cv, init);
return new Math(value);
}
times(...args) {
const init = this.hasInitValue ? this.value : args.shift();
const value = args.reduce((pv, cv) => pv * cv, init);
return new Math(value);
}
divide(...args) {
const init = this.hasInitValue ? this.value : args.shift();
const value = args.reduce((pv, cv) => pv / cv, init);
return new Math(value);
}
toJSON() {
return this.valueOf();
}
toString() {
return String(this.valueOf());
}
valueOf() {
return this.value;
}
}
let math = new Math()
console.log(math.add(2,4).minus(3).times(2)) // Math { value: 6 }
21. 给定一个字符串,求不含有重复字符的最长子串的长度
var lengthOfLongestSubstring = function (s) {
var temp = [];
var maxs = 0;
if (s == "") {
return 0;
}
if (s.length == 1) {
return 1;
}
let i = 0
while (i < s.length) {
if(!temp.includes(s[i])) {
temp.push(s[i])
maxs++
}
i++
}
return maxs;
}
console.log(lengthOfLongestSubstring('abcabcbb')); // 3
console.log(lengthOfLongestSubstring('bbbbb')); // 1
console.log(lengthOfLongestSubstring('pwwkewrxyz')); // 8
22. 用ES6proxy实现 arr[-1] 的访问
function proxyArray(arr) {
const length = arr.length
return new Proxy(arr, {
get(target, key) {
key = +key // 将string转换为int
while(key < 0) {
key += length
}
return target[key]
}
})
}
let a = proxyArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
console.log(a[1]); // 2
console.log(a[-10]); // 9
console.log(a[-20]); // 8
23. 将数组扁平转换为树结构
递归方式
const flatArr = [
{ id: '01', parentId: 0, name: '节点1' },
{ id: '011', parentId: '01', name: '节点1-1' },
{ id: '0111', parentId: '011', name: '节点1-1-1' },
{ id: '02', parentId: 0, name: '节点2' },
{ id: '022', parentId: '02', name: '节点2-2' },
{ id: '023', parentId: '02', name: '节点2-3' },
{ id: '0222', parentId: '022', name: '节点2-2-2' },
{ id: '03', parentId: 0, name: '节点3' },
]
function getTreeData (arr, parentId) {
function loop(parentId) {
return arr.reduce((pre,cur) => {
if(cur.parentId == parentId) {
cur.children = loop(cur.id)
pre.push(cur)
}
return pre
}, [])
}
return loop(parentId)
}
let result = getTreeData(flatArr, 0)
console.log(result);
非递归方式
const flatArr = [
{ id: '01', parentId: 0, name: '节点1' },
{ id: '011', parentId: '01', name: '节点1-1' },
{ id: '0111', parentId: '011', name: '节点1-1-1' },
{ id: '02', parentId: 0, name: '节点2' },
{ id: '022', parentId: '02', name: '节点2-2' },
{ id: '023', parentId: '02', name: '节点2-3' },
{ id: '0222', parentId: '022', name: '节点2-2-2' },
{ id: '03', parentId: 0, name: '节点3' },
]
function getTreeData (arr) {
let data = arr.filter(item => {
item.children = arr.filter(e => {
return item.id == e.parentId
})
return !item.parentId
})
return data
}
let result = getTreeData(flatArr)
console.log(result);
24. 去除字符串中出现次数最少的字符,不改变原字符串的顺序
function f(str){
let obj = {};
for(let i=0;i<str.length;i++){
if (obj[str[i]]) {
obj[str[i]] ++;
} else {
obj[str[i]] = 1;
}
}
let countArr = [];
Object.keys(obj).forEach((item)=>{
countArr.push(obj[item]);
});
let min = Math.min(...countArr);
for(let key in obj) {
if(obj[key] == min) {
str = str.replaceAll(key, '')
}
}
return str
}
let str = 'aaabbbcceeff'
let result = f(str)
console.log(result); // aaabbb