나는 로더쉬를 저장하고 가방의 크기를 줄이는 14가지 기능을 했다.

Lodash와 밑줄은 Javascript를 작성하는 방식을 영원히 바꾸었지만, 오늘은 가장 흔히 볼 수 있는 함수에 대해 더 좋은 선택이 있을 것입니다.
나는 최근에 우리의 메인 프로그램을 훑어보았는데, 패키지의 크기를 줄이기를 희망했고, 비록 우리가 특정 기능의 도입을 위해 최선을 다했지만, 우리는 여전히 대부분의lodash를 수입하고 있다는 것을 곧 발견했다.
우리는lodashes로 이사했는데, 이것은 약간의 도움이 되었지만, 나는 여전히 실용적인 기능을 찾고 있다. 그것들은 패키지의 30퍼센트 정도를 차지한다.
문제는 노드 모듈로서 라이브러리에서polyfilling의 낡은 기능에 대한 선택이 많이 이루어졌기 때문에 목표 브라우저에 따라 필요하지 않은 코드가 많을 수 있습니다.
나는lodash에서 사용한 14개의 핵심 함수를 확정하고 현대Javascript로 다시 작성하기 시작했다. 이렇게 묶는 과정은 목표에 따라 어떤 다중 충전이 필요한지 결정할 수 있다.수입 규모가 대폭 감소하다.
나무가 흔들리면 내 기능에 앞서

내 코드: 4.1kb (압축되지 않음/통일되지 않음, 비교적 오래된 브라우저에 다각형 채우기 필요)

핵심 기능


다음은 함수 목록에 대한 내 작업입니다.

일치 기능

  • 필터
  • forEach(배열 및 객체)
  • 그룹당
  • 키 대
  • 매핑(수 그룹 및 대상)
  • 통합
  • 생략
  • sortBy
  • uniq
  • uniqBy
  • "그만해".

  • 선택
  • get(배열 구문은 지원되지 않음)
  • 집합(수조 문법 지원하지 않음)
  • 탈온스(maxWait,flush,cancel 포함)
  • 기능


    다음은 이 함수들입니다. 이 함수들은 무엇을 하는지, 그리고 제가 어떻게 실현하는지.

    선택(기능(항목)=>값|속성 이름)


    우리는 다른 모든 것에 매우 유용하기 때문에 pick부터 시작할 것이다.pick은 대상에서 속성을 추출하는 함수를 되돌려줍니다. 문자열을 이 문자열로 변환하지만 다른 값을 사용하지 않습니다.pick:
    const array = [{ name: "mike", a: 1 }, { name: "bob", a: 2 }]
    
    console.log(array.map(pick('name')) //=> ["mike", "bob"]
    
    
    구현
    import {get} from './get'
    export function pick(fn) {
      return typeof fn === "string" ? (v) => get(v,fn) : fn
    }
    

    필터(배열, 함수(항목)=>부울 값|문자열)


    우리는name 속성을 가진 filter을 자주 사용하기 때문에 Filter는 기본적으로pick이고 기존의 Filter 함수는 다음과 같다.
    const array = [{ name: "mike", a: 1 }, { name: "bob", a: 2 }, { a: 4 }]
    
    console.log(filter(array, 'name')) //=> [{ name: "mike", a: 1 }, { name: "bob", a: 2 }]
    
    
    구현
    import { pick } from "./pick"
    
    export function filter(target, fn) {
      return target.filter(pick(fn))
    }
    

    forEach(배열 | 객체, 함수(값, 키)


    lodash에서 forEach에 대상이나 그룹을 사용할 수 있기 때문에 이를 실현하기 위한 실현이 필요합니다.리셋 함수 획득 매개 변수 valuekey.작동 방식은 다음과 같습니다.
    const data = { a: 1, b: 2, d: "hello" }
    forEach(data, (value, key)=>console.log(`${key}=${value}`) 
          //=> a=1
          //=> b=2
          //=> d=hello
    
    구현
    import { pick } from "./pick"
    
    export function applyArrayFn(target, fnName, fn) {
      fn = pick(fn)
      if (Array.isArray(target)) return target[fnName](fn)
      if (target && typeof target === "object")
        return Object.entries(target)[fnName](([key, value], index) =>
          fn(value, key, target, index)
        )
      throw new Error(`Cannot iterate ${typeof target}`)
    }
    
    export function forEach(target, fn) {
      return applyArrayFn(target, "forEach", fn)
    }
    

    가져오기(객체, 속성 경로, 기본값)

    get에서는 객체에서 속성을 읽을 수 있으며 브로커나 최종 값을 찾지 못하면 기본값을 반환합니다.
    const data = { a: { b: {d: 1 } } }
    get(data, "a.b.d") //=> 1
    get(data, "a.c.d", "hmmm") //=> hmmm
    
    구현
    export function get(object, path, defaultValue) {
      const parts = path.split(".")
      for (let part of parts) {
        if(!object) return defaultValue
        object = object[part]
      }
      return object ?? defaultValue
    }
    

    그룹 By (배열, 함수 (항목) = > 키 | propertyName)


    함수(또는 선택된 속성 이름)의 결과로 키프레임을 설정하는 객체를 만듭니다. 여기서 각 값은 같은 키프레임을 가진 항목의 배열입니다.
    const array = [{ name: "mike", type: "user" }, { name: "bob", type: "user" }, { name: "beth", type: "admin"} ]
    
    console.log(groupBy(array, 'type'))
        /*=>
           {
              admin: [{name: "beth", type: "admin" }],
              user: [{name: "mike", type: "user" }, {name: "bob", type: "user"}]
           }
        */
    
    
    구현
    import { pick } from "./pick"
    
    export function groupBy(target, fn) {
      fn = pick(fn)
      return target
        .map((value) => ({ value, key: fn(value) }))
        .reduce((c, a) => {
          c[a.key] = c[a.key] || []
          c[a.key].push(a.value)
          return c
        }, {})
    }
    

    keyBy (배열, 함수 (항목) = > key | propertyName)

    groupBy과 유사하지만, 결과는 마지막 키와 일치하는 항목입니다. 보통 이것은 주어진 것이고, 그 중 키는 유일한 (예: id) 이며, 검색을 만들 것입니다.
    const array = [{ id: "a7", name: "mike", type: "user" }, { id: "z1", name: "bob", type: "user" }, { id: "a3", name: "beth", type: "admin"} ]
    
    console.log(keyBy(array, 'id'))
        /*=>
           {
              "a3": {name: "beth", type: "admin", id: "a3" },
              "a7": {name: "mike", type: "user", id: "a7" },
              "z1": {name: "bob", type: "user", id: "z1"}
           }
        */
    
    
    구현
    import { pick } from "./pick"
    
    export function keyBy(target, fn) {
      fn = pick(fn)
      return target
        .map((value) => ({ value, key: fn(value) }))
        .reduce((c, a) => {
          c[a.key] = a.value
          return c
        }, {})
    }
    
    

    매핑(배열 | 객체, 함수(값, 키) = >value | 속성 이름)


    매핑 객체 및 배열(예: forEach)
    const records = {
              "a3": {name: "beth", type: "admin" },
              "a7": {name: "mike", type: "user" },
              "z1": {name: "bob", type: "user"}
           }
    console.log(map(records, 'name')) /=> ["beth", "mike", "bob"]
    
    구현
    import { pick } from "./pick"
    
    export function applyArrayFn(target, fnName, fn) {
      fn = pick(fn)
      if (Array.isArray(target)) return target[fnName](fn)
      if (target && typeof target === "object")
        return Object.entries(target)[fnName](([key, value], index) =>
          fn(value, key, target, index)
        )
      throw new Error(`Cannot iterate ${typeof target}`)
    }
    
    export function forEach(target, fn) {
      return applyArrayFn(target, "map", fn)
    }
    

    병합(대상,...소스)


    그 작업 원리는 Object.assign과 유사하지만, 끝까지 깊이 들어가 그것들을 교체하는 것이 아니라 더욱 깊은 대상을 갱신할 것이다.
    const record = { id: "2", name: "Beth", value: 3, ar: ["test", { a: 3, d: { e: 4 } }] }
    console.log(merge(record, { ar: [{ b: 1 }, { c: 3, d: { f: 5 } }]))
    
       /*=>
        {
          id: "2",
          name: "Beth",
          value: 3,
          ar: [{ b: 1 }, { c: 3, d: { f: 5, e: 4 } }]
        }
       */
    
    구현
    export function merge(target, ...sources) {
      for (let source of sources) {
        mergeValue(target, source)
      }
    
      return target
    
      function innerMerge(target, source) {
        for (let [key, value] of Object.entries(source)) {
          target[key] = mergeValue(target[key], value)
        }
      }
    
      function mergeValue(targetValue, value) {
        if (Array.isArray(value)) {
          if (!Array.isArray(targetValue)) {
            return [...value]
          } else {
            for (let i = 0, l = value.length; i < l; i++) {
              targetValue[i] = mergeValue(targetValue[i], value[i])
            }
            return targetValue
          }
        } else if (typeof value === "object") {
          if (targetValue && typeof targetValue === "object") {
            innerMerge(targetValue, value)
            return targetValue
          } else {
            return value ? { ...value } : value
          }
        } else {
          return value ?? targetValue ?? undefined
        }
      }
    }
    

    생략(객체, arrayOffrops)


    제거한 아이템의 대상을 되돌려줍니다
    const record = { a: 1, b: 2, c: 3}
    console.log(omit(record, ['b', 'c'])) //=> {a: 1}
    
    구현
    export function omit(target, props) {
      return Object.fromEntries(
        Object.entries(target).filter(([key]) => !props.includes(key))
      )
    }
    

    컬렉션(객체, 속성 경로, 값)


    대상의 값을 설정하고 필요하면 빈 대상 {}을 만듭니다.
    const record = { a: 1, d: { e: 1 } }
    set(record, "a.d.e", 2) //=> { a: 1, d: { e: 2 } }
    set(record, "a.b.c", 4) //=> { a: 1, b: { c: 4 }, d: { e: 2 } }
    
    구현
    export function set(object, path, value) {
      const parts = path.split(".")
      for (let i = 0, l = parts.length - 1; i < l; i++) {
        const part = parts[i]
        object = object[part] = object[part] || {}
      }
      object[parts[parts.length - 1]] = value
    }
    

    정렬 방식(배열, 함수(항목)=>value | 속성 이름)


    배열을 하위 요소별로 정렬합니다.
    const array = [{ id: "a7", name: "mike", type: "user" }, { id: "z1", name: "bob", type: "user" }, { id: "a3", name: "beth", type: "admin"} ]
    console.log(sortBy(array, 'name'))
         /*=>
          [
            { id: "a3", name: "beth", type: "admin"} 
            { id: "z1", name: "bob", type: "user" }, 
            { id: "a7", name: "mike", type: "user" }, 
          ]
         */
    
    구현
    import { pick } from "./pick"
    
    export function sortBy(array, fn) {
      fn = pick(fn)
      return array.sort((a, b) => {
        const va = fn(a)
        const vb = fn(b)
        if (va < vb) return -1
        if (va > vb) return 1
        return 0
      })
    }
    
    

    어레이


    기존 스토리지에서 고유한 스토리지 생성
    const array = ['a', 'b', 'c', 'b', 'b', 'a']
    console.log(uniq(array)) //=> ['a', 'b', 'c']
    
    구현
    export function uniq(target) {
      return Array.from(new Set(target))
    }
    

    uniqBy(배열, 함수(항목)=>value | 속성 이름)


    그룹의 대상의 속성을 사용하여 uniq 그룹을 만듭니다.
    const array = [{a: 1, b: 2}, {a: 4, b: 2}, {a: 5, b: 3}]
    console.log(uniqBy(array, 'b')) //=> [{a: 1, b: 2}, {a: 5, b: 3}]
    
    구현
    import { pick } from "./pick"
    
    export function uniqBy(target, fn) {
      fn = pick(fn)
      const dedupe = new Set()
      return target.filter((v) => {
        const k = fn(v)
        if (dedupe.has(k)) return false
        dedupe.add(k)
        return true
      })
    }
    

    부분적으로 이루어진 탈온스


    lodash debounce은 아주 강해요. 저한테는 너무 강해요. 너무 커요.나는 온스에 가서 가장 긴 대기 시간을 보내고, 마운트된 호출을 새로 고치거나 취소할 수 있는 기능이 필요하다.(그래서 부족한 것은 후연과 전연 등, 그리고 내가 사용하지 않는 다른 옵션이다.)
    const debounced = debounce(()=>save(), 1000, {maxWait: 10000})
    ...
    debounced() // Call the debounced function after 1s (max 10s)
    debounced.flush() // call any pending 
    debounced.cancel() // cancel any pending calls
    
    구현
    export function debounce(fn, wait = 0, { maxWait = Infinity } = {}) {
      let timer = 0
      let startTime = 0
      let running = false
      let pendingParams
      let result = function (...params) {
        pendingParams = params
        if (running && Date.now() - startTime > maxWait) {
          execute()
        } else {
          if (!running) {
            startTime = Date.now()
          }
          running = true
        }
    
        clearTimeout(timer)
        timer = setTimeout(execute, Math.min(maxWait - startTime, wait))
    
        function execute() {
          running = false
          fn(...params)
        }
      }
      result.flush = function () {
        if (running) {
          running = false
          clearTimeout(timer)
          fn(...pendingParams)
        }
      }
      result.cancel = function () {
        running = false
        clearTimeout(timer)
      }
      return result
    }
    
    

    결론


    만약 당신이 이 기능들만 사용한다면,lodash가 더 이상 필요하지 않을 수도 있습니다.우리 프로그램에서, 우리는 확실히 다른lodash 함수를 사용했지만, 모두 타성 가져오기 (예: template) 를 지원하기 때문에, 우리 프로그램의 불러오는 속도가 훨씬 빠르다.
    프로젝트에서 이 코드를 마음대로 사용할 수 있습니다.

    좋은 웹페이지 즐겨찾기