call
- 语法: call(fn, obj, …args)
功能: 执行fn, 使this为obj, 并将后面的n个参数传给fn(功能等同于函数对象的call方法) ```javascript function call(fn,obj,…args){
// 如果obj是undefined/null, this指定为window if (obj===undefined || obj===null) { // return fn(…args) obj = GlobalThis;// 全局对象 }
// 给obj添加一个临时方法, 方法指向的函数就是fn obj.tempFn = fn // 通过obj来调用这个方法 ==> 也就会执行fn函数 ==> 此时fn中的this肯定为obj const result = obj.tempFn(…args) // 删除obj上的临时方法 delete obj.tempFn // 返回fn执行的结果 return result
}
<a name="ogrWV"></a>
# apply
- 语法: apply(fn, obj, args)
- 功能: 执行fn, 使this为obj, 并将args数组中的元素传给fn(功能等同于函数对象的apply方法)
```javascript
function apply(fn, obj, args) {
// 如果obj是undefined/null, this指定为window
if (obj===undefined || obj===null) {
// return fn(...args)
obj = GlobalThis
}
// 给obj添加一个临时方法, 方法指向的函数就是fn
obj.tempFn = fn
// 通过obj来调用这个方法 ==> 也就会执行fn函数 ==> 此时fn中的this肯定为obj
const result = obj.tempFn(...args)
// 删除obj上的临时方法
delete obj.tempFn
// 返回fn执行的结果
return result
}
bind
- 语法: bind(fn, obj, …args)
功能: 给fn绑定this为obj, 并指定参数为后面的n个参数 (功能等同于函数对象的bind方法)
import {call} from './call'
/*
自定义函数对象的bind方法
*/
function bind(fn, obj, ...args) {
// 返回一个新函数
return (... args2) => {
// 通过call调用原函数, 并指定this为obj, 实参为args与args2
return call(fn, obj, ...args, ...args2)
}
throttle节流
语法: throttle(callback, wait)
功能: 创建一个节流函数,在 wait 毫秒内最多执行 callback 一次
function throttle(callback, wait) {
let start = 0
// 返回一个事件监听函数(也就是节流函数)
return function (event) {
console.log('throttle event')
// 只有当距离上次处理的时间间隔超过了wait时, 才执行处理事件的函数
const current = Date.now()
if ( current - start > wait) {
callback.call(this, event) // 需要指定this和参数
start = current
}
}
}
debounce防抖
语法: debounce(callback, wait)
- 功能: 创建一个防抖动函数,该函数会从上一次被调用后,延迟 wait 毫秒后调用 callback
function debounce (callback, wait) {
// 用来保存定时器任务的标识id
let timeoutId = -1
// 返回一个事件监听函数(也就是防抖函数)
return function (event) {
console.log('debounce event')
// 清除未执行的定时器任务
if (timeoutId!==-1) {
clearTimeout(timeoutId)
}
// 启动延迟 await 时间后执行的定时器任务
timeoutId = setTimeout(() => {
// 调用 callback 处理事件
callback.call(this, event)
// 处理完后重置标识
timeoutId = -1
}, wait)
}
}
数组方法
- map(): 返回一个由回调函数的返回值组成的新数组
- reduce(): 从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值
- filter(): 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回
- find(): 找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined。
- findIndex(): 找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1。
- every(): 如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。
- some(): 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。 ```javascript function map (array, callback) { const arr = [] for (let index = 0; index < array.length; index++) { // 将callback的执行结果添加到结果数组中 arr.push(callback(array[index], index)) } return arr }
function reduce (array, callback, initValue) { let result = initValue for (let index = 0; index < array.length; index++) { // 调用回调函数将返回的结果赋值给result result = callback(result, array[index], index) } return result }
function filter(array, callback) {
const arr = [] for (let index = 0; index < array.length; index++) { if (callback(array[index], index)) { arr.push(array[index]) } } return arr }
function find (array, callback) { for (let index = 0; index < array.length; index++) { if (callback(array[index], index)) { return array[index] } } return undefined }
function findIndex (array, callback) { for (let index = 0; index < array.length; index++) { if (callback(array[index], index)) { return index } } return -1 }
export function every (array, callback) { for (let index = 0; index < array.length; index++) { if (!callback(array[index], index)) { // 只有一个结果为false, 直接返回false return false } } return true }
function some (array, callback) { for (let index = 0; index < array.length; index++) { if (callback(array[index], index)) { // 只有一个结果为true, 直接返回true return true } } return false }
<a name="O1EQE"></a>
## 数组去重
- 方法1: 利用forEach()和indexOf()
- 说明: 本质是双重遍历, 效率差些
- 方法2: 利用forEach() + 对象容器
- 说明: 只需一重遍历, 效率高些
- 方法3: 利用ES6语法: from + Set 或者 ... + Set
- 说明: 编码简洁
```javascript
function unique1 (array) {
const arr = []
array.forEach(item => {
if (arr.indexOf(item)===-1) {
arr.push(item)
}
})
return arr
}
function unique2 (array) {
const arr = []
const obj = {}
array.forEach(item => {
if (!obj.hasOwnProperty(item)) {
obj[item] = true
arr.push(item)
}
})
return arr
}
export function unique3 (array) {
// return Array.from(new Set(array))
return [...new Set(array)]
}
数组合并与切片
concat(): 合并
- 语法: var new_array = concat(array, value1[, value2[, …[, valueN]]])
- 功能: 将n个数组或值与当前数组合并生成一个新数组, 原始数组不会被改变
export function concat (array, ...values) {
const arr = [...array]
values.forEach(value => {
if (Array.isArray(value)) {
arr.push(...value)
} else {
arr.push(value)
}
})
return arr
}
slice(): 切片
- 语法: var new_array = slice(array, [begin[, end]])
- 功能: 返回一个由 begin 和 end 决定的原数组的浅拷贝, 原始数组不会被改变 ```javascript function slice (array, begin, end) { // 如果当前数组是[], 直接返回[] if (array.length === 0) { return [] }
// 如果begin超过最大下标, 直接返回[] begin = begin || 0 if (begin >= array.length) { return [] }
// 如果end不大于begin, 直接返回[] end = end || array.length if (end > array.length) { end = array.length } if (end <= begin) { return [] }
// 取出下标在[begin, end)区间的元素, 并保存到最终的数组中 const arr = [] for (let index = begin; index < end; index++) { arr.push(array[index]) }
数组扁平化
语法: flatten(array)
- 取出嵌套数组(多维)中的所有元素放到一个新数组(一维)中
如:[1, [3, [2, 4]]] ==> [1, 3, 2, 4]
方法一: 递归 + reduce() + concat()
- 方法二: … + some() + concat()
```javascript
/
方法一: 递归 + reduce() + concat()
/
export function flatten1 (array) {
return array.reduce((pre, item) => {
if (Array.isArray(item) && item.some(cItem => Array.isArray(cItem))) {
} else {return pre.concat(flatten1(item))
} }, []) }return pre.concat(item)
/ 方法二: … + some() + concat() / export function flatten2 (array) { let arr = [].concat(…array) while (arr.some(item => Array.isArray(item))) { arr = [].concat(…arr) } return arr }
<a name="qjXLv"></a>
## 数组分块
- 语法: chunk(array, size)
- 功能: 将数组拆分成多个 size 长度的区块,每个区块组成小数组,整体组成一个二维数组
- 如: [1, 3, 5, 6, 7, 8] 调用chunk(arr, 4) ==> [[1, 3, 5, 6], [7,8]]
<a name="AAp73"></a>
## 数组取差异
- 语法: difference(arr1, arr2)
- 功能: 得到当前数组中所有不在arr中的元素组成的数组(不改变原数组)
- 例子: difference([1,3,5,7], [5, 8]) ==> [1, 3, 7]
```javascript
export function difference (arr1, arr2) {
if (arr1.length===0) {
return []
} else if (arr2.length===0) {
return arr1.slice()
}
return arr1.filter(item => arr2.indexOf(item)===-1)
}
删除数组中部分元素
- pull(array, …values):
- 删除原数组中与value相同的元素, 返回所有删除元素的数组
- 说明: 原数组发生了改变
- 如: pull([1,3,5,3,7], 2, 7, 3, 7) ===> 原数组变为[1, 5], 返回值为[3,3,7]
pullAll(array, values):
- 功能与pull一致, 只是参数变为数组
- 如: pullAll([1,3,5,3,7], [2, 7, 3, 7]) ===> 数组1变为[1, 5], 返回值为[3,3,7] ```javascript export function pull (array, …values) { if (array.length===0 || values.length===0) { return [] }
var result = [] for (let index = 0; index < array.length; index++) { const item = array[index] if (values.indexOf(item)!==-1) {
array.splice(index, 1)
result.push(item)
index--
} }
return result }
export function pullAll (array, values) { if (!values || !Array.isArray(values)) { return [] } return pull(array, …values) }
<a name="QYfgf"></a>
## 得到数组的部分元素
- drop(array, count)
- 得到当前数组过滤掉左边count个后剩余元素组成的数组
- 说明: 不改变当前数组, count默认是1
- 如: drop([1,3,5,7], 2) ===> [5, 7]
- dropRight(array, count)
- 得到当前数组过滤掉右边count个后剩余元素组成的数组
- 说明: 不改变当前数组, count默认是1
- 如: dropRight([1,3,5,7], 2) ===> [1, 3]
```javascript
export function drop (array, count=1) {
if (array.length === 0 || count >= array.length) {
return []
}
return array.filter((item, index) => index>=count)
}
export function dropRight (array, count=1) {
if (array.length === 0 || count >= array.length) {
return []
}
return array.filter((item, index) => index < array.length-count)
字符串相关
- 字符串倒序
- 语法: reverseString(str)
- 功能: 生成一个倒序的字符串
- 字符串是否是回文
- 语法: palindrome(str)
- 功能: 如果给定的字符串是回文,则返回 true ;否则返回 false
- 截取字符串
- 语法: truncate(str, num)
- 功能: 如果字符串的长度超过了num, 截取前面num长度部分, 并以…结束 ```javascript function reverserString(str){ return Array.from(str).reverse().join(‘’) }
function palindrome(str){ return str === reverserString(str) }
function truncate(str,num){ return str.length > num ? str.slice(0,num) + ‘…’: str }
<a name="vODh5"></a>
# 对象相关
- newInstance()
- myInstanceOf()
- mergeObject()
- clone1() / clone2()
- deepClone1() / deepClone2() / deepClone3() / deepClone4()
<a name="FaJmi"></a>
## 自定义new
- 语法: newInstance(Fn, ...args)
- 功能: 创建Fn构造函数的实例对象
```javascript
function newInstance (Fn, ...args) {
// 创建一个空的object实例对象obj, 作为Fn的实例对象
const obj = {}
// 将Fn的prototype属性值赋值给obj的__proto__属性值
obj.__proto__ = Fn.prototype
// 调用Fn, 指定this为obj, 参数为args列表
const result = Fn.call(obj, ...args)
// 如果Fn返回的是一个对象类型, 那返回的就不再是obj, 而是Fn返回的对象
// 否则返回obj
return result instanceof Object ? result : obj
}
自定义instanceof
- 语法: myInstanceOf(obj, Type)
- 功能: 判断obj是否是Type类型的实例
实现: Type的原型对象是否是obj的原型链上的某个对象, 如果是返回true, 否则返回false
function myInstanceOf(obj, Type) {
// 得到原型对象
let protoObj = obj.__proto__
// 只要原型对象存在
while(protoObj) {
// 如果原型对象是Type的原型对象, 返回true
if (protoObj === Type.prototype) {
return true
}
// 指定原型对象的原型对象
protoObj = protoObj.__proto__
}
return false
}
合并多个对象
语法: object mergeObject(…objs)
- 功能: 合并多个对象, 返回一个合并后对象(不改变原对象)
- 例子:
{ a: [{ x: 2 }, { y: 4 }], b: 1}
{ a: { z: 3}, b: [2, 3], c: 'foo'}
- 合并后:
{ a: [ { x: 2 }, { y: 4 }, { z: 3 } ], b: [ 1, 2, 3 ], c: 'foo' }
function mergeObject(...objs) {
const result = {}
// 遍历objs
objs.forEach(obj => {
Object.keys(obj).forEach(key => {
// 如果result还没有key值属性
if (!result.hasOwnProperty(key)) {
result[key] = obj[key]
} else { // 否则 合并属性
result[key] = [].concat(result[key], obj[key])
}
})
})
// 可以使用reduce来代替forEach手动添加
return result
}
对象/数组拷贝
- 纯语言表达:
- 浅拷贝: 只是复制了对象属性或数组元素本身(只是引用地址值)
- 深拷贝: 不仅复制了对象属性或数组元素本身, 还复制了指向的对象(使用递归)
举例说明: 拷贝persons数组(多个人对象的数组)
- 浅拷贝: 只是拷贝了每个person对象的引用地址值, 每个person对象只有一份
- 深拷贝: 每个person对象也被复制了一份新的\
浅拷贝 ```javascript / 实现浅拷贝 方法一: 利用ES6语法 方法二: 利用ES5语法: for…in / / 方法一: 利用ES6语法/ export function clone1(target) { // 如果是对象(不是函数, 也就是可能是object对象或者数组) if (target!=null && typeof target===’object’) { if (target instanceof Array) {
// return target.slice()
// return target.filter(() => true)
// return target.map(item => item)
return [...target]
} else {
// return Object.assign({}, target)
return {...target}
} } // 基本类型或者函数, 直接返回 return target }
/ 方法二: 利用ES5语法: for…in / export function clone2(target) { if (target!=null && typeof target===’object’) { const cloneTarget = Array.isArray(target) ? [] : {} for (let key in target) { if (target.hasOwnProperty(key)) { cloneTarget[key] = target[key] } } return cloneTarget } else { return target } }
**深拷贝**
- 实现一: 大众乞丐版
- 问题1: 函数属性会丢失
- 问题2: 循环引用会出错
- 实现二: 面试基础版
- 解决问题1: 函数属性还没丢失
- 实现三: 面试加强版本
- 解决问题2: 循环引用正常
- 实现四: 面试加强版本2(优化遍历性能)
- 数组: while | for | forEach() 优于 for-in | keys()&forEach()
- 对象: for-in 与 keys()&forEach() 差不多
```javascript
1). 大众乞丐版
问题1: 函数属性会丢失
问题2: 循环引用会出错
*/
export function deepClone1(target) {
return JSON.parse(JSON.stringify(target))
}
/*
2). 面试基础版本
解决问题1: 函数属性还没丢失
*/
export function deepClone2 (target) {
if (target!==null && typeof target==='object') {
const cloneTarget = target instanceof Array ? [] : {}
for (const key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone2(target[key])
}
}
return cloneTarget
}
return target
}
/*
3). 面试加强版本
解决问题2: 循环引用正常
*/
export function deepClone3 (target, map=new Map()) {
if (target!==null && typeof target==='object') {
// 从缓存容器中读取克隆对象
let cloneTarget = map.get(target)
// 如果存在, 返回前面缓存的克隆对象
if (cloneTarget) {
return cloneTarget
}
// 创建克隆对象(可能是{}或者[])
cloneTarget = target instanceof Array ? [] : {}
// 缓存到map中
map.set(target, cloneTarget)
for (const key in target) {
if (target.hasOwnProperty(key)) {
// 递归调用, 深度克隆对象, 且传入缓存容器map
cloneTarget[key] = deepClone3(target[key], map)
}
}
return cloneTarget
}
return target
}