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

}

  1. <a name="ogrWV"></a>
  2. # apply
  3. - 语法: apply(fn, obj, args)
  4. - 功能: 执行fn, 使this为obj, 并将args数组中的元素传给fn(功能等同于函数对象的apply方法)
  5. ```javascript
  6. function apply(fn, obj, args) {
  7. // 如果obj是undefined/null, this指定为window
  8. if (obj===undefined || obj===null) {
  9. // return fn(...args)
  10. obj = GlobalThis
  11. }
  12. // 给obj添加一个临时方法, 方法指向的函数就是fn
  13. obj.tempFn = fn
  14. // 通过obj来调用这个方法 ==> 也就会执行fn函数 ==> 此时fn中的this肯定为obj
  15. const result = obj.tempFn(...args)
  16. // 删除obj上的临时方法
  17. delete obj.tempFn
  18. // 返回fn执行的结果
  19. return result
  20. }

bind

  • 语法: bind(fn, obj, …args)
  • 功能: 给fn绑定this为obj, 并指定参数为后面的n个参数 (功能等同于函数对象的bind方法)

    1. import {call} from './call'
    2. /*
    3. 自定义函数对象的bind方法
    4. */
    5. function bind(fn, obj, ...args) {
    6. // 返回一个新函数
    7. return (... args2) => {
    8. // 通过call调用原函数, 并指定this为obj, 实参为args与args2
    9. return call(fn, obj, ...args, ...args2)
    10. }

    throttle节流

  • 语法: throttle(callback, wait)

  • 功能: 创建一个节流函数,在 wait 毫秒内最多执行 callback 一次

    1. function throttle(callback, wait) {
    2. let start = 0
    3. // 返回一个事件监听函数(也就是节流函数)
    4. return function (event) {
    5. console.log('throttle event')
    6. // 只有当距离上次处理的时间间隔超过了wait时, 才执行处理事件的函数
    7. const current = Date.now()
    8. if ( current - start > wait) {
    9. callback.call(this, event) // 需要指定this和参数
    10. start = current
    11. }
    12. }
    13. }


    debounce防抖

  • 语法: debounce(callback, wait)

  • 功能: 创建一个防抖动函数,该函数会从上一次被调用后,延迟 wait 毫秒后调用 callback
    1. function debounce (callback, wait) {
    2. // 用来保存定时器任务的标识id
    3. let timeoutId = -1
    4. // 返回一个事件监听函数(也就是防抖函数)
    5. return function (event) {
    6. console.log('debounce event')
    7. // 清除未执行的定时器任务
    8. if (timeoutId!==-1) {
    9. clearTimeout(timeoutId)
    10. }
    11. // 启动延迟 await 时间后执行的定时器任务
    12. timeoutId = setTimeout(() => {
    13. // 调用 callback 处理事件
    14. callback.call(this, event)
    15. // 处理完后重置标识
    16. timeoutId = -1
    17. }, wait)
    18. }
    19. }

数组方法

  • 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 }

  1. <a name="O1EQE"></a>
  2. ## 数组去重
  3. - 方法1: 利用forEach()和indexOf()
  4. - 说明: 本质是双重遍历, 效率差些
  5. - 方法2: 利用forEach() + 对象容器
  6. - 说明: 只需一重遍历, 效率高些
  7. - 方法3: 利用ES6语法: from + Set 或者 ... + Set
  8. - 说明: 编码简洁
  9. ```javascript
  10. function unique1 (array) {
  11. const arr = []
  12. array.forEach(item => {
  13. if (arr.indexOf(item)===-1) {
  14. arr.push(item)
  15. }
  16. })
  17. return arr
  18. }
  1. function unique2 (array) {
  2. const arr = []
  3. const obj = {}
  4. array.forEach(item => {
  5. if (!obj.hasOwnProperty(item)) {
  6. obj[item] = true
  7. arr.push(item)
  8. }
  9. })
  10. return arr
  11. }
  1. export function unique3 (array) {
  2. // return Array.from(new Set(array))
  3. return [...new Set(array)]
  4. }

数组合并与切片

  • concat(): 合并

    • 语法: var new_array = concat(array, value1[, value2[, …[, valueN]]])
    • 功能: 将n个数组或值与当前数组合并生成一个新数组, 原始数组不会被改变
      1. export function concat (array, ...values) {
      2. const arr = [...array]
      3. values.forEach(value => {
      4. if (Array.isArray(value)) {
      5. arr.push(...value)
      6. } else {
      7. arr.push(value)
      8. }
      9. })
      10. return arr
      11. }
  • 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]) }

    return arr } ```

    数组扁平化

  • 语法: 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))) {
    1. return pre.concat(flatten1(item))
    } else {
    1. 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 }

  1. <a name="qjXLv"></a>
  2. ## 数组分块
  3. - 语法: chunk(array, size)
  4. - 功能: 将数组拆分成多个 size 长度的区块,每个区块组成小数组,整体组成一个二维数组
  5. - 如: [1, 3, 5, 6, 7, 8] 调用chunk(arr, 4) ==> [[1, 3, 5, 6], [7,8]]
  6. <a name="AAp73"></a>
  7. ## 数组取差异
  8. - 语法: difference(arr1, arr2)
  9. - 功能: 得到当前数组中所有不在arr中的元素组成的数组(不改变原数组)
  10. - 例子: difference([1,3,5,7], [5, 8]) ==> [1, 3, 7]
  11. ```javascript
  12. export function difference (arr1, arr2) {
  13. if (arr1.length===0) {
  14. return []
  15. } else if (arr2.length===0) {
  16. return arr1.slice()
  17. }
  18. return arr1.filter(item => arr2.indexOf(item)===-1)
  19. }

删除数组中部分元素

  • 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) {

    1. array.splice(index, 1)
    2. result.push(item)
    3. index--

    } }

    return result }

export function pullAll (array, values) { if (!values || !Array.isArray(values)) { return [] } return pull(array, …values) }

  1. <a name="QYfgf"></a>
  2. ## 得到数组的部分元素
  3. - drop(array, count)
  4. - 得到当前数组过滤掉左边count个后剩余元素组成的数组
  5. - 说明: 不改变当前数组, count默认是1
  6. - 如: drop([1,3,5,7], 2) ===> [5, 7]
  7. - dropRight(array, count)
  8. - 得到当前数组过滤掉右边count个后剩余元素组成的数组
  9. - 说明: 不改变当前数组, count默认是1
  10. - 如: dropRight([1,3,5,7], 2) ===> [1, 3]
  11. ```javascript
  12. export function drop (array, count=1) {
  13. if (array.length === 0 || count >= array.length) {
  14. return []
  15. }
  16. return array.filter((item, index) => index>=count)
  17. }
  18. export function dropRight (array, count=1) {
  19. if (array.length === 0 || count >= array.length) {
  20. return []
  21. }
  22. 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 }

  1. <a name="vODh5"></a>
  2. # 对象相关
  3. - newInstance()
  4. - myInstanceOf()
  5. - mergeObject()
  6. - clone1() / clone2()
  7. - deepClone1() / deepClone2() / deepClone3() / deepClone4()
  8. <a name="FaJmi"></a>
  9. ## 自定义new
  10. - 语法: newInstance(Fn, ...args)
  11. - 功能: 创建Fn构造函数的实例对象
  12. ```javascript
  13. function newInstance (Fn, ...args) {
  14. // 创建一个空的object实例对象obj, 作为Fn的实例对象
  15. const obj = {}
  16. // 将Fn的prototype属性值赋值给obj的__proto__属性值
  17. obj.__proto__ = Fn.prototype
  18. // 调用Fn, 指定this为obj, 参数为args列表
  19. const result = Fn.call(obj, ...args)
  20. // 如果Fn返回的是一个对象类型, 那返回的就不再是obj, 而是Fn返回的对象
  21. // 否则返回obj
  22. return result instanceof Object ? result : obj
  23. }

自定义instanceof

  • 语法: myInstanceOf(obj, Type)
  • 功能: 判断obj是否是Type类型的实例
  • 实现: Type的原型对象是否是obj的原型链上的某个对象, 如果是返回true, 否则返回false

    1. function myInstanceOf(obj, Type) {
    2. // 得到原型对象
    3. let protoObj = obj.__proto__
    4. // 只要原型对象存在
    5. while(protoObj) {
    6. // 如果原型对象是Type的原型对象, 返回true
    7. if (protoObj === Type.prototype) {
    8. return true
    9. }
    10. // 指定原型对象的原型对象
    11. protoObj = protoObj.__proto__
    12. }
    13. return false
    14. }

    合并多个对象

  • 语法: 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' }
  1. function mergeObject(...objs) {
  2. const result = {}
  3. // 遍历objs
  4. objs.forEach(obj => {
  5. Object.keys(obj).forEach(key => {
  6. // 如果result还没有key值属性
  7. if (!result.hasOwnProperty(key)) {
  8. result[key] = obj[key]
  9. } else { // 否则 合并属性
  10. result[key] = [].concat(result[key], obj[key])
  11. }
  12. })
  13. })
  14. // 可以使用reduce来代替forEach手动添加
  15. return result
  16. }

对象/数组拷贝

  • 纯语言表达:
    • 浅拷贝: 只是复制了对象属性或数组元素本身(只是引用地址值)
    • 深拷贝: 不仅复制了对象属性或数组元素本身, 还复制了指向的对象(使用递归)
  • 举例说明: 拷贝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) {

    1. // return target.slice()
    2. // return target.filter(() => true)
    3. // return target.map(item => item)
    4. return [...target]

    } else {

    1. // return Object.assign({}, target)
    2. 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. - 实现一: 大众乞丐版
  3. - 问题1: 函数属性会丢失
  4. - 问题2: 循环引用会出错
  5. - 实现二: 面试基础版
  6. - 解决问题1: 函数属性还没丢失
  7. - 实现三: 面试加强版本
  8. - 解决问题2: 循环引用正常
  9. - 实现四: 面试加强版本2(优化遍历性能)
  10. - 数组: while | for | forEach() 优于 for-in | keys()&forEach()
  11. - 对象: for-in keys()&forEach() 差不多
  12. ```javascript
  13. 1). 大众乞丐版
  14. 问题1: 函数属性会丢失
  15. 问题2: 循环引用会出错
  16. */
  17. export function deepClone1(target) {
  18. return JSON.parse(JSON.stringify(target))
  19. }
  20. /*
  21. 2). 面试基础版本
  22. 解决问题1: 函数属性还没丢失
  23. */
  24. export function deepClone2 (target) {
  25. if (target!==null && typeof target==='object') {
  26. const cloneTarget = target instanceof Array ? [] : {}
  27. for (const key in target) {
  28. if (target.hasOwnProperty(key)) {
  29. cloneTarget[key] = deepClone2(target[key])
  30. }
  31. }
  32. return cloneTarget
  33. }
  34. return target
  35. }
  36. /*
  37. 3). 面试加强版本
  38. 解决问题2: 循环引用正常
  39. */
  40. export function deepClone3 (target, map=new Map()) {
  41. if (target!==null && typeof target==='object') {
  42. // 从缓存容器中读取克隆对象
  43. let cloneTarget = map.get(target)
  44. // 如果存在, 返回前面缓存的克隆对象
  45. if (cloneTarget) {
  46. return cloneTarget
  47. }
  48. // 创建克隆对象(可能是{}或者[])
  49. cloneTarget = target instanceof Array ? [] : {}
  50. // 缓存到map中
  51. map.set(target, cloneTarget)
  52. for (const key in target) {
  53. if (target.hasOwnProperty(key)) {
  54. // 递归调用, 深度克隆对象, 且传入缓存容器map
  55. cloneTarget[key] = deepClone3(target[key], map)
  56. }
  57. }
  58. return cloneTarget
  59. }
  60. return target
  61. }

http://www.atguigu.com/mst/html/gp/17380.html