目录
JavaScript 必写
- 数组排序(多种方法)
- 数组去重(多种方法)
- 数组扁平化
- 将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组
- 深拷贝
- 函数防抖节流
- 实现函数原型方法 call apply bind
- 实现数组原型方法 forEach map filter some reduce
- 实现 Promise
- 实现 Promise.all
- 实现 Promise.allSettled
- 封装事件类 EventEmitter(发布订阅模式)
JavaScript 编码能力
- 函数柯里化
- 函数记忆
- 对象比较
- 计算字符串中出现最多的字母与出现次数
- 实现一个 trim 方法
- js 实现一个函数 获得 url 参数的值
- 驼峰转下划线:appleOrangePinkBoy => apple_orange_pink_boy
- 实现一个 get 方法通过
.
来取对象的值 - 封装一下 axios 或者手写封装 ajax
- 实现一个 once 函数,传入函数参数只执行一次
- 实现一个简单的模板字符串替换
- 合并对象 merge
- 求多个数组之间的交集
- 实现一个版本比较方法 compareVersion
JavaScript 必写
数组排序(多种方法)
// 冒泡排序
// 冒泡排序的原理如下:
// 从第一个元素开始,把当前元素和下一个元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素,那么此时(一轮结束后)最后一个元素就是该数组中最大的数
// 下一轮重复以上操作,但是此时最后一个元素已经是最大数了,所以不需要再比较最后一个元素,只需要比较到 length - 1 -i的位置
function bubble(array) {for (let i = 0; i < array.length - 1; i++) {console.log('第' + (i + 1) + '轮开始')let flag = true// 从 0 到 `length - 1` 遍历for (let j = 0; j < array.length - 1 - i; j++) {if (array[j] > array[j + 1]) {flag = false;[array[j], array[j + 1]] = [array[j + 1], array[j]]console.log('第' + (j + 1) + '次:' + array.toString(array))}}if (flag) {console.log('第' + (i + 1) + '轮后数据结束变化更新')break}}return array
}
console.log(bubble([3, 2, 1, 4, 8, 6, 7]))
// 第1轮开始
// 第1次:2,3,1,4,8,6,7
// 第2次:2,1,3,4,8,6,7
// 第5次:2,1,3,4,6,8,7
// 第6次:2,1,3,4,6,7,8
// 第2轮开始
// 第1次:1,2,3,4,6,7,8
// 第3轮开始
// 第3轮后数据结束变化更新
// 时间复杂度是 O(n * n)
// http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html
// 快排
// 快排的原理如下:
//第一步,选择中间的元素作为"基准"。(基准值可以任意选择,但是选择中间的值比较容易理解。)
//第二步,按照顺序,将每个元素与"基准"进行比较,形成两个子集,一个"小于基准",另一个"大于基准"。
//第三步,对两个子集不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
function quickSort(arr) {if (arr.length <= 1) {return arr //递归出⼝}var pivotIndex = Math.floor(arr.length / 2)// 取出基准值var pivot = arr.splice(pivotIndex, 1)var left = []var right = []for (var i = 0; i < arr.length; i++) {if (arr[i] < pivot) {left.push(arr[i])} else {right.push(arr[i])}}return quickSort(left).concat(pivot, quickSort(right))
}
// 该算法的复杂度和归并排序是相同的,但是额外空间复杂度比归并排序少,只需 O(logN),并且相比归并排序来说,所需的常数时间也更少。
console.log(quickSort([3, 2, 1, 4, 8, 6, 7]))
// 原地快排
function quickSort(arr, low = 0, high = arr.length - 1) {if (low >= high) returnlet left = lowlet right = highlet flag = arr[left]// 判断左右游标是否重合,如果重合,循环结束while (left < right) {// 右边大,继续向左比较if (flag <= arr[right]) {right--}// 交换下一个可能不合理的位置arr[left] = arr[right]// 左边大,继续向右比较if (flag >= arr[left]) {left++}// 交换下一个arr[right] = arr[left]}//重合之后,交换基准数arr[left] = flagquickSort(arr, low, left - 1)quickSort(arr, left + 1, high)return arr
}
console.log(quickSort([4, 3, 8, 1, 9, 6, 2]))
数组去重(多种方法)
//github.com/mqyqingfeng/Blog/issues/27
var array = [1, 2, 1, 1, '1']
;(() => {var unique = a => [...new Set(a)]console.log(unique(array)) // [1, 2, "1"]
})()
;(() => {function unique(array) {var res = []for (var i = 0, len = array.length; i < len; i++) {var current = array[i]if (res.indexOf(current) === -1) {res.push(current)}}return res}console.log(unique(array)) // [1, 2, "1"]
})()
;(() => {function unique(array) {var res = []for (var i = 0, len = array.length; i < len; i++) {var current = array[i]if (res.indexOf(current) === -1) {res.push(current)}}return res}console.log(unique(array)) // [1, 2, "1"]
})()
;(() => {function unique(array) {var res = array.filter(function(item, index, array) {//若当前元素所在的索引位置 === 在原始数组中出现该值的的第一个索引,则 true 返回当前元素return array.indexOf(item) === index})return res}console.log(unique(array))
})()
;(() => {function unique(array) {var o = new Map()// set 设置后返回新的 Map 对象return array.filter(key => !o.has(key) && o.set(key, true))}console.log(unique(array))
})()
数组扁平化
let entry = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10]
// 实现 Array.prototype.flat()
// https://github.com/mqyqingfeng/Blog/issues/36
;(() => {function flatten(arr) {let res = []for (let i = 0; i < arr.length; i++) {let item = arr[i]if (Array.isArray(item)) {res = res.concat(flatten(item))} else {res.concat(item)}}return res}console.log(flatten(arr)) // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
})()
;(() => {function flatten(arr) {return arr.reduce((prev, curr) => {return prev.concat(Array.isArray(curr) ? flatten(curr) : curr)}, [])}console.log(flatten(arr))
})()
;(() => {function flatten(arr) {while (arr.some(item => Array.isArray(item))) {arr = [].concat(...arr)}return arr}console.log(flatten(arr))
})()
ES6 增加了扩展运算符,用于取出参数对象的所有可遍历属性,拷贝到当前对象之中:
var arr = [1, [2, [3, 4]]]
console.log([].concat(...arr)) // [1, 2, [3, 4]]
将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组
Array.prototype.flat = function() {return [].concat(...this.map(item => (Array.isArray(item) ? item.flat() : [item])))
}
Array.prototype.unique = function() {return [...new Set(this)]
}
const sort = (a, b) => a - b
console.log(arr.flat().unique().sort(sort)
) // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ]
深拷贝
用 JSON,存在如下缺点:
- 不支持 Date、正则、undefined、函数等数据
- 不支持引用(即环状结构)
const deepClone = o => JSON.parse(JSON.stringify(o))
基础版(新增函数函数类型支持),推荐使用 lodash 的深拷贝函数
function deepCopy(target) {if (typeof target == 'object') {const result = Array.isArray(target) ? [] : {}for (const key in target) {if (typeof target[key] == 'object') {result[key] = deepCopy(target[key])} else {result[key] = target[key]}}return result} else if (typeof target == 'function') {return eval('(' + target.toString() + ')')// 也可以这样克隆函数// return new Function("return " + target.toString())();} else {return target}
}
递归完整版本
要点:
1.递归
2.判断类型
3.不拷贝原型上的属性
4.检查环
const deepClone = (o, cache) => {if (!cache) {cache = new Map()}if (o instanceof Object) {if (cache.get(o)) {return cache.get(o)}let resultif (o instanceof Function) {// 有 prototype 就是普通函数if (o.prototype) {result = function() {return o.apply(this, arguments)}} else {result = (...args) => {return o.call(undefined, ...args)}}} else if (o instanceof Array) {result = []} else if (o instanceof Date) {return +new Date(o)} else if (o instanceof RegExp) {result = new RegExp(o.source, o.flags)} else {// 最后是普通对象result = {}}// ! 只要拷贝过下次就不要拷贝了cache.set(o, result)for (const key in o) {if (o.hasOwnProperty(key)) {result[key] = deepClone(o[key], cache)}}return result} else {// string、number、boolean、undefined、null、symbol、bigintreturn o}
}
const a = {number: 1,bool: false,str: 'hi',empty1: undefined,empty2: null,array: [{ name: 'frank', age: 18 },{ name: 'jacky', age: 19 }],date: new Date(2000, 0, 1, 20, 30, 0),regex: /\.(j|t)sx/i,obj: { name: 'frank', age: 18 },f1: (a, b) => a + b,f2: function(a, b) {return a + b}
}
a.self = a
var b = deepClone(a)
console.log(b)
console.log(b.self === b)
函数防抖节流
防抖与节流函数是一种最常用的 高频触发优化方式,能对性能有较大的帮助。
- 节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms 执行一次即可。
function throttle(fn, wait = 500, immediate) {let timer = nulllet callNow = immediatereturn function() {let context = this,args = argumentsif (callNow) {fn.apply(context, args)callNow = false}if (!timer) {timer = setTimeout(() => {fn.apply(context, args)timer = null}, wait)}}
}
- 防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。
function debounce(fn, wait = 1500, immediate) {let timer = nullreturn function() {let args = argumentslet context = thisif (immediate && !timer) {fn.apply(context, args)}if (timer) clearTimeout(timer)timer = setTimeout(() => {fn.apply(context, args)}, wait)}
}
实现函数原型方法 call apply bind
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
// https://github.com/mqyqingfeng/Blog/issues/11
Function.prototype.call__fake = function(context) {context = context || windowlet fn = thislet exec = Symbol('fn')let args = [].slice.call(arguments, 1)// 将函数设为对象上的临时属性,函数内 this 保证正确context[exec] = fn// 执行函数,存储返回结果const result = context[exec](...args)// 删除临时变量delete context[exec]return result
}
var value = 2
var obj = {value: 1
}
function bar(name, age) {console.log(this.value)return {value: this.value,name: name,age: age}
}
bar.call__fake(null) // 2
console.log(bar.call__fake(obj, 'kevin', 18))
// 1
// {value: 1, name: 'kevin', age: 18}
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
// https://github.com/mqyqingfeng/Blog/issues/11
Function.prototype.apply__fake = function(context) {context = context || windowlet fn = thislet exec = Symbol('fn')let args = arguments[1] || []// 将函数设为对象的属性context[exec] = fnconst result = context[exec](...args)delete context[exec]return result
}
var value = 2
var obj = {value: 1
}
function bar(name, age) {console.log(this.value)return {value: this.value,name: name,age: age}
}
bar.apply__fake(null) // 2
console.log(bar.apply__fake(obj, ['kevin', 18]))
// 1
// {value: 1, name: 'kevin', age: 18}
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Function.prototype.bind__fake = function(context) {context = context || windowlet fn = thislet exec = Symbol('fn')let args = [].slice.call(arguments, 1)// 将函数设为对象上的临时属性,函数内 this 保证正确context[exec] = fnreturn function() {// 执行函数,存储返回结果const result = context[exec](...args)// 删除临时变量delete context[exec]return result}
}
var value = 2
var obj = {value: 1
}
function bar(name, age) {console.log(this.value)return {value: this.value,name: name,age: age}
}
bar.bind__fake(null)() // 2
console.log(bar.bind__fake(obj, 'kevin', 18)())
// 1
// {value: 1, name: 'kevin', age: 18}
实现数组原型方法 forEach map filter some reduce
// forEachhttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
;(() => {Array.prototype.forEach__fake = function(fn) {const array = thisfor (let index = 0; index < array.length; index++) {const element = array[index]fn.call(null, element, index, array)}}const array1 = ['a', 'b', 'c']array1.forEach__fake((element, i) => console.log(i, element))// expected output: 0 'a'// expected output: 1 'b'// expected output: 2 'c'
})()
// map https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map
;(() => {Array.prototype.map__fake = function(fn) {let result = []const array = thisfor (let index = 0; index < array.length; index++) {const element = array[index]result[index] = fn.call(null, element, index, array)}return result}const array1 = [1, 4, 9, 16]// pass a function to mapconst map1 = array1.map__fake(x => x * 2)console.log(map1)// expected output: Array [2, 8, 18, 32]
})()
// filter https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
;(() => {Array.prototype.filter__fake = function(fn) {let result = []const array = thisfor (let index = 0; index < array.length; index++) {const element = array[index]fn.call(null, element, index, array) ? result.push(element) : ''}return result}const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']const result = words.filter__fake(word => word.length > 6)console.log(result)// expected output: Array ["exuberant", "destruction", "present"]
})()
// somehttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/some
;(() => {Array.prototype.some__fake = function(fn) {let flag = falseconst array = thisfor (let index = 0; index < array.length; index++) {const element = array[index]if (fn.call(null, element, index, array)) {flag = truebreak}}return flag}const array = [1, 2, 3, 4, 5]// checks whether an element is evenconst even = element => element % 2 === 0console.log(array.some__fake(even))// expected output: true
})()
// reduce https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
;(() => {Array.prototype.reduce__fake = function(fn, initialValue) {let result = initialValue || 0const array = thisfor (let index = 0; index < array.length; index++) {const element = array[index]result = fn.call(null, result, element, index, array)}return result}const array1 = [1, 2, 3, 4]// 0 + 1 + 2 + 3 + 4const initialValue = 0const sumWithInitial = array1.reduce__fake((previousValue, currentValue) => previousValue + currentValue,initialValue)console.log(sumWithInitial)// expected output: 10
})()
实现 Promise
class Promise__fake {constructor(executor) {this.status = 'pending'this.handleFulfilled = [] // 存储成功后的回调this.handleRejection = [] // 存储失败后的回调// ! resolve 形参的实际参数在这儿const resolve = data => {// 状态变更只有一次if (this.status !== 'pending') {return}this.status = 'fulfilled'// ! 等一会,否则 handleFulfilled 为空setTimeout(() => {this.handleFulfilled.forEach(fn => fn(data))}, 0)}const reject = reason => {if (this.status !== 'pending') {return}this.status = 'rejected'setTimeout(() => {this.handleRejection.forEach(fn => fn(reason))}, 0)}try {executor(resolve, reject)} catch (e) {// 遇到错误时,捕获错误,执行 reject 函数reject(e)}}then(fulfilledFn, rejectedFn) {this.handleFulfilled.push(fulfilledFn)this.handleRejection.push(rejectedFn)return this}
}
//1. 链式测试
var p1 = new Promise__fake(function(resolve, reject) {console.log('init Promise')if (Math.random() > 0.5) {resolve('大')} else {reject('小')}
})
p1.then(data => console.log('success', data),reason => console.log('error', reason)
).then(() => console.log('success 2'),() => console.log('error 2')
)
// 2. 延时测试
var sleep = (time, data) =>new Promise__fake(function(resolve, reject) {setTimeout(resolve, time, data)})
sleep(3000, '时间到!').then(val => {console.log(val)
})
// 3. 状态变更后不可变
const p2 = new Promise__fake(function(resolve, reject) {resolve('失败了!')reject('还会成功吗!')
})
p2.then(data => console.log(data),reason => console.log(reason)
)
实现 Promise.all
;(() => {var promise1 = 41var promise2 = 42var promise3 = new Promise(function(resolve, reject) {setTimeout(resolve, 5000, 'foo')})var promise4 = new Promise(function(resolve, reject) {setTimeout(reject('[err]: 模拟错误'), 300)})function p1(time) {return new Promise(function(resolve, reject) {setTimeout(function() {resolve(time)}, time)})}// Promise 扩展Promise.all__fake = promiseAry => {return new Promise((resolve, reject) => {let resultAry = [],index = 0for (let i = 0; i < promiseAry.length; i++) {Promise.resolve(promiseAry[i]).then(result => {index++resultAry[i] = resultif (index === promiseAry.length) {resolve(resultAry)}}).catch(reason => {reject(reason)})}})}Promise.all__fake([promise1, promise2, promise3]).then(function(values) {console.log(values) // [41, 42, 'foo']})Promise.all__fake([promise4, promise2, promise3]).then(function(values) {console.log(values) // Uncaught (in promise) [err]: 模拟错误})Promise.all__fake([p1(5000), p1(1000)]).then(function(res) {console.log(res) //[5000,1000]})
})()
实现 Promise.allSettled
如果任意的 promise reject,则 Promise.all 整个将会 reject。当我们需要 所有 结果都成功时,它对这种“全有或全无”的情况很有用:Promise.allSettled 等待所有的 promise 都被 settle,无论结果如何。结果数组具有:
- {status:“fulfilled”, value:result} 对于成功的响应,
- {status:“rejected”, reason:error} 对于 error。
Promise.allSettled = function(promises) {const rejectHandler = reason => ({ status: 'rejected', reason })const resolveHandler = value => ({ status: 'fulfilled', value })const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler))return Promise.all(convertedPromises)
}
封装事件类 EventEmitter(发布订阅模式)
class EventEmitter {constructor() {this._cache = {}}$on(type, callback) {if (this._cache[type]) {this._cache[type].push(callback)} else {this._cache[type] = [callback]}return this}$emit(type, data) {let fns = this._cache[type]if (Array.isArray(fns)) {fns.forEach(fn => {fn(data)})}return this}$off(type, callback) {let fns = this._cache[type]if (Array.isArray(fns) && callback) {this._cache[type] = fns.filter(event => {return event !== callback})}return this}$once(type, callback) {let that = thisfunction func() {var args = Array.prototype.slice.call(arguments, 0)callback.apply(that, args)that.$off(type, func)}this.$on(type, func)}
}
var Event = new EventEmitter()
Event.$once('addAddress', function(address) {console.log(JSON.stringify(address, null, 2))
})
Event.$emit('addAddress', {location: '北京',longitude: '116°20′',latitude: '39°56′'
})
Event.$once('once', function(res) {console.log(JSON.stringify(res, null, 2))
})
Event.$emit('once', {content: '我希望只能执行一次'
})
Event.$emit('once', {content: '我希望只能执行一次'
})
Event.$emit('once', {content: '我希望只能执行一次'
})
function mitt(all) {return {all: (all = all || new Map()),on(eventName, callback) {let cbs = all.get(eventName);(cbs && cbs.push(callback)) || all.set(eventName, [callback])},emit(eventName, ...args) {let cbs = all.get(eventName)if (cbs.length === 0) {console.error(`no find ${eventName} function.`)return}cbs.forEach(cb => cb(args))},off(eventName, callback) {let cbs = all.get(eventName)cbs &&all.set(eventName,cbs.filter(cb => cb !== callback))}}
}
const eventHub = new mitt()
eventHub.on('click', console.log)
setTimeout(() => {eventHub.emit('click', 'yanyue404')eventHub.off('click', console.log)
}, 1500)
setTimeout(() => {eventHub.emit('click', '1024')
}, 1500)
JavaScript 编码能力
函数柯里化
const add = (x, y, z) => x + y + z
const curry = fn => {const fnLength = fn.lengthreturn function curried(...args) {if (args.length === fnLength) {return fn.apply(null, args)} else {return function(...reset) {return curried.apply(null, args.concat(reset))}}}
}
const curriedAdd = curry(add)
const result = curriedAdd(1)(2)(3)
const result2 = curriedAdd(1, 2, 3)
const result3 = curriedAdd(1, 2)(3)
console.log('result', result) // result 6
console.log('result2', result2) // result2 6
console.log('result3', result3) // result3 6
函数记忆
// github.com/mqyqingfeng/Blog/issues/46
https: var memoize = function(func, hasher) {var memoize = function(key) {var cache = memoize.cachevar address = '' + (hasher ? hasher.apply(this, arguments) : key)if (!cache[address]) {cache[address] = func.apply(this, arguments)}return cache[address]}memoize.cache = {}return memoize
}
var add = function(a, b, c) {return a + b + c
}
// 自定义存储 key
var memoizedAdd = memoize(add, function() {var args = Array.prototype.slice.call(arguments)return JSON.stringify(args)
})
console.log(memoizedAdd(1, 2, 3)) // 6
console.log(memoizedAdd(1, 2, 4)) // 7
// 适用场景:需要大量重复的计算,或者大量计算又依赖于之前的结果
;(() => {var count = 0var fibonacci = function(n) {count++return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2)}for (var i = 0; i <= 10; i++) {fibonacci(i)}console.log('优化前:' + count) // 453
})()
;(() => {var count = 0var fibonacci = function(n) {count++return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2)}fibonacci = memoize(fibonacci)for (var i = 0; i <= 10; i++) {fibonacci(i)}console.log('优化后:' + count) // 12
})()
函数组合
/**
* 执行从右到左的功能组合
*/
function compose(...funcs) {return function(result) {let list = funcs.slice()while (list.length > 0) {// 从列表中取第一个函数并执行result = list.pop()(result)}return result}
}
/**
* 执行从左到右的功能组合
*/
function pipe(initialValue, ...funcs) {return funcs.reduce((preValue, curFunc) => curFunc(preValue), initialValue)
}
对象比较
function isObject(value) {var type = typeof valuereturn value != null && (type == 'object' || type == 'function')
}
/**
* form vue
* Check if two values are loosely equal - that is,
* if they are plain objects, do they have the same shape?
*/
function looseEqual(a, b) {if (a === b) return trueconst isObjectA = isObject(a)const isObjectB = isObject(b)if (isObjectA && isObjectB) {try {const isArrayA = Array.isArray(a)const isArrayB = Array.isArray(b)if (isArrayA && isArrayB) {return (a.length === b.length &&a.every((e, i) => {return looseEqual(e, b[i])}))} else if (a instanceof Date && b instanceof Date) {return a.getTime() === b.getTime()} else if (!isArrayA && !isArrayB) {const keysA = Object.keys(a)const keysB = Object.keys(b)return (keysA.length === keysB.length &&keysA.every(key => {return looseEqual(a[key], b[key])}))} else {return false}} catch (e) {return false}} else if (!isObjectA && !isObjectB) {return String(a) === String(b)} else {return false}
}
计算字符串中出现最多的字母与出现次数
let str = 'configureyourdevicetousewhistleasitsHTTPandHTTPSproxyonIP'
let str2 = 'aabbcbc'
// o、e 出现了 5 次
function getMaxString(string) {const map = {}let max = 1let maxKeys = []for (let i = 0; i < string.length; i++) {let key = string[i]map[key] ? map[key]++ : (map[key] = 1)if (map[key] > max) {max = map[key]maxKeys = [key]} else if (map[key] === max) {maxKeys.push(key)}}console.log('最大值存在多个', maxKeys.join('、') + '出现了 ' + max + '次')return [max, maxKeys]
}
getMaxString(str) // 5, ['e','o']
getMaxString(str2) // 3, ['b']
实现一个 trim 方法
// 删除左右的空格
const trim = s => s.replace(/(^\s+)|(\s+$)/g, '')
// 删除所有的空格
const trimAll = s => s.replace(/\s/g, '')
const greeting = ' Hello world! '
console.log(trim(greeting)) // Hello world!
console.log(trimAll(greeting)) // Helloworld!
js 实现一个函数 获得 url 参数的值
1.将 http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&d&enabled
解析为如下格式:
{ user: 'anonymous', id: [123, 456], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型 city: '北京', // 中文 enabled: true, // 未指定值的 key 约定值为 true
}
let url = `http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&d&enabled`
function getQueryJson(url = '') {let json = {}url = decodeURIComponent(url || location.href)if (typeof url !== 'string') return jsonlet splits = url.split('?')if (splits && splits.length >= 2) {let paramsArr = splits[1].split('&')if (paramsArr && paramsArr.length > 0) {json = paramsArr.reduce((o, item) => {const [key, value] = item.split('=')if (key && !o[key]) {o[key] = value === undefined ? true : value} else {if (!Array.isArray(o[key])) {o[key] = [o[key]].concat(value)} else {o[key] = o[key].push(value)}}return o}, {})}}return json
}
console.log(JSON.stringify(getQueryJson(url), null, 2))
/*{user: "anonymous",id: ["123", "456"],city: "北京",d: true,enabled: true,}; */
驼峰转下划线:appleOrangePinkBoy => apple_orange_pink_boy
;(() => {let str = 'appleOrangePinkBoy'function underline(str) {// \B 非单词边界,左右占位的字符必须是 \w ([0-9a-zA-Z_])return str.replace(/\B([A-Z])/g, (m, p1) => `_${p1.toLowerCase()}`)}console.log(underline(str)) // apple_orange_pink_boy
})()
;(() => {let str = 'apple_orange_pink_boy'function decamelize(str) {return str.replace(/_(\w)/g, (m, p1) => p1.toUpperCase())}console.log(decamelize(str)) // appleOrangePinkBoy
})()
实现一个 get 方法通过.
来取对象的值
const obj = { a: [{ b: { c: 3 } }] }
function get(obj, path, def) {// https://jex.im/regulex/#!flags=&re=%5B%5C.%5C%5B%5C%5D%5D%2Blet chain = Array.isArray(path) ? path : path.split(/[\.\[\]]+/)let val = chain.reduce((prev, curr) => {if (prev) {return (prev = prev[curr])} else {return prev}}, obj)return val === undefined ? def : val
}
console.log(get(obj, 'a.b', false)) // false
console.log(get(obj, 'pop_act.pic.aaaaaaaaa')) // undefined
console.log(get(obj, 'pop_act.pic.aaaaaaaaa', false)) // false
console.log(get(obj, ['a', 'b', 'c'])) // undefined
console.log(get(obj, ['a', '0', 'b', 'c'])) // 3
console.log(get(obj, 'a[0].b.c')) // 3
封装一下 axios 或者手写封装 ajax
/**
* 将参数对象转换为a=1&b=2字符串格式
* @param {object} params 待转换的参数对象
*/
function stringify(params = {}) {let str = ''for (const key of Object.keys(params)) {str += `&${key}=${params[key]}`}return str.substring(1)
}
/**
* ajax请求
* @param {string} 请求链接
* @param {string} 方法名,post|get
* @param {object} 请求头
* @param {params} 参数
*/
function ajax({ url = '', method = 'GET', headers = {}, params = {} } = {}) {return new Promise((resolve, reject) => {try {let xhr = new XMLHttpRequest()if (method === 'GET' && JSON.stringify(params) !== '{}') {url = url + '?' + stringify(params)} else if (method === 'POST') {xhr.open('POST', url, true)xhr.setRequestHeader('Content-type', 'application/json')}// true 为一个异步的请求xhr.open(method, url, true)for (const key of Object.keys(headers)) {xhr.setRequestHeader(key, headers[key])}xhr.send(method == 'POST' ? JSON.stringify(params) : '')xhr.onreadystatechange = function() {if (xhr.readyState == 4 && xhr.status == 200) {let ret = xhr.responsetry {ret = JSON.parse(ret)} catch (err) {console.log(err)}resolve(ret)} else if (xhr.readyState == 4 && xhr.status != 200) {reject(xhr.response)}}} catch (err) {reject(err)}})
}
实现一个 once 函数,传入函数参数只执行一次
function once(func) {var flag = truereturn function() {if (flag == true) {func.apply(null, arguments)flag = false}return undefined}
}
实现一个简单的模板字符串替换
const render = function(tpl, data) {// m 参数为 匹配的子串// p1 参数为 (.*?)的匹配结果return tpl.replace(/\{\{(.*?)\}\}/g, function(match, p1) {return data[p1.trim()]})
}
const text = render('我是{{ name}},年龄{{age}},性别{{sex}}', {name: 'yanyue404',age: 18,sex: '男'
})
console.log(text)
const tpl = "Hei, my name is <% name%>, and I'm <%age%> years old."
const render = (tpl, context) =>tpl.replace(/<%(.*?)%>/g, function(_, m) {return data[m.trim()]})
console.log(render(tpl, {name: 'Rainbow',age: '20'})
)
合并对象 merge
const merge = (obj, target = {}) => {Object.keys(obj).forEach(key => {if (isObject(obj[key])) {if (isObject(target[key])) {// 都是对象Object.keys(obj[key]).forEach(prop => {target[key][prop] = obj[key][prop]})} else {// target不是对象 直接重写if (target[key]) {target[key] = {[key]: target[key],...obj[key]}} else {target[key] = obj[key]}}} else {if (isObject(target[key])) {target[key] = {...target[key],[key]: obj[key]}} else {target[key] = obj[key]}}})return target
}
const obj1 = {pid: 1,signup: '注册',menu: '菜单',headers: {common: 'common'}
}
const obj2 = {pid: 2,signup: {sin: 2},menu: {id: 1},headers: {test: 123}
}
const result = merge(obj1, obj2)
// {
// pid: 2,
// signup: { signup: '注册', sin: 2 },
// menu: { menu: '菜单', id: 1 },
// headers: { common: 'common', test: 123 }
// }
console.log(result)
求多个数组之间的交集
let array1 = [1, 2, 3, 4, 5, 6, 7, 7]
let array2 = [2, 3, 4, 5, 6, 7, 7, 8, 9]
let array3 = [4, 5, 6, 7, 7, 8, 9]
let array4 = []
function intersection(...args) {return Array.from(new Set(args.reduce((prev, curr) => {return prev.filter(item => curr.includes(item))})))
}
console.log(intersection(array1, array3, array3)) // [4,5,6,7]
console.log(intersection(array4)) // []
实现一个版本比较方法 compareVersion
// compareVersion("1.11.0", "1.9.9"); // 1
function compareVersion(currVersion, comparedVersion) {if (currVersion && comparedVersion) {let v1 = currVersion.split('.')let v2 = comparedVersion.split('.')const len = Math.max(v1.length, v2.length)while (v1.length < len) {v1.push('0')}while (v2.length < len) {v2.push('0')}for (let i = 0; i < len; i++) {const num1 = parseInt(v1[i])const num2 = parseInt(v2[i])if (num1 > num2) {return 1} else if (num1 < num2) {return -1}}return 0}return -1
}
最后
最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。
有需要的小伙伴,可以点击下方卡片领取,无偿分享