对象操作

遍历对象

利用Object.entries和for...of

for (const [key, value] of Object.entries(object1)) {
  console.log(`${key}: ${value}`);
}

for (const [key, value] of Object.entries(object1)) { console.log(: ); }

对象条件封装

通过...和三目

{
    key,
    ...(keyOuter ? { keyOuter } : {})
}

过滤空值

基于 lodash:

/**
 * 过滤对象中null/undefined/[]/{}/''
 * @param {object} obj 需要过滤的对象 */
export function omitEmpty(obj) {
  return (function prune(current) {
    _.forOwn(current, function(value, key) {
      if (
        _.isUndefined(value) ||
        _.isNull(value) ||
        _.isNaN(value) ||
        (_.isString(value) && _.isEmpty(value)) ||
        (_.isObject(value) && _.isEmpty(prune(value)))
      ) {
        delete current[key];
      }
    });
    // remove any leftover undefined values from the delete // operation on an array
    if (_.isArray(current)) _.pull(current, undefined);
    return current;
  })(_.cloneDeep(obj));
}

缓存

该方案基于typescript

以cache为例,首先是一个简单的cache类:简单的cache类

// 一个简单的cache 类
export class MemoryStorage {

    private memCache: any = {};

    constructor() { }

    public async getItem<T>(key: string): Promise<T> {
        return this.memCache[key];
    }

    public async setItem(key: string, content: any): Promise<void> {
        this.memCache[key] = content;
    }

    public async clear(): Promise<void> {
        this.memCache = {};
    }
}

可以看到只是做了简单的get和set,如果需要给其增加过期时间的话,可以通过代理模式来完成,再封装一个带过期策略的cache类:

import { MemoryStorage } from './memory-storage';

interface IExpiringCacheItem {
    content: any;
    meta: {
        createdAt: number;
        ttl: number;
    }
}

interface IOptions {
    ttl?: number;
    isLazy?: boolean;
    isCachedForever?: boolean;
}
// 一个带过期时间功能的cache类,对普通的cache类包装了一层进行了代理模式,以及promise的封装
export class ExpirationStrategy {

    private readonly storage: MemoryStorage;

    constructor(storage: MemoryStorage) {
        this.storage = storage;
    }

    public async getItem<T>(key: string): Promise<T> {
        return new Promise<T>(async (resolve, _) => {
            const item = await this.storage.getItem<IExpiringCacheItem>(key);
            if (item && item.meta && item.meta.ttl && this.isItemExpired(item)) {
                await this.storage.setItem(key, undefined);
                resolve(undefined);
            }
            item ? resolve(item.content) : resolve(undefined);
        });
    }

    public async setItem(key: string, content: any, options: IOptions): Promise<void> {
        options = { ttl: 60, isLazy: true, isCachedForever: false, ...options }

        let meta = {};

        if (!options.isCachedForever) {
            meta = {
                ttl: options.ttl! * 1000,
                createdAt: Date.now()
            };

            if (!options.isLazy) {
                setTimeout(() => {
                    this.unsetKey(key);
                }, options.ttl!);
            }
        }
        await this.storage.setItem(key, {meta, content});
    }

    public async clear(): Promise<void> {
        await this.storage.clear();
    }

    private isItemExpired(item: IExpiringCacheItem): boolean {
        return Date.now() > item.meta.createdAt + item.meta.ttl;
    }

    private async unsetKey(key: string): Promise<void> {
        await this.storage.setItem(key, undefined);
    }
}

然后将第一个cache类作为实例化对象传入即可:

this.cache = new ExpirationStrategy(new MemoryStorage());

从而完成整个代理过程。

proxy对象属性透传

找bridge.a其实就是去找bridge.rnCallNative.a

适合于将一个对象上所有东西,挂到另一个对象上往外去暴露

export const bridge = new Proxy(bridgeBase, {
  get(obj, prop) {
    if (prop in obj) {
      return obj[prop];
    }

    if (supportBridgeName.indexOf(prop) >= 0) {
      return (obj.bridge.rnCallNative || f).bind(obj.bridge, prop);
    }

    return null;
  },
});

mixin增强对象

当对象的方法需要区分命名空间或者说是外部增强,可以通过mixin的思路。

参考笔记内容

//mixin模式扩展Cropper类
assign(Cropper.prototype, render, preview, events, handlers, change, methods);

通过其他几个文件暴露不同的方法来增强对象,assign函数为:

export const assign = Object.assign || function assign(target, ...args) {
  if (isObject(target) && args.length > 0) {
    args.forEach((arg) => {
      if (isObject(arg)) {
        Object.keys(arg).forEach((key) => {
          target[key] = arg[key];
        });
      }
    });
  }

  return target;
};

其他的文件如event,暴露几个函数即可:

import {
  EVENT_CROP,
  EVENT_CROP_END,
  EVENT_CROP_MOVE,
  EVENT_CROP_START,
  EVENT_DBLCLICK,
  EVENT_POINTER_DOWN,
  EVENT_POINTER_MOVE,
  EVENT_POINTER_UP,
  EVENT_RESIZE,
  EVENT_WHEEL,
  EVENT_ZOOM,
} from './constants';
import {
  addListener,
  isFunction,
  removeListener,
} from './utilities';

export default {
  bind() {
    const { element, options, cropper } = this;

    if (isFunction(options.cropstart)) {
      addListener(element, EVENT_CROP_START, options.cropstart);
    }

    if (isFunction(options.cropmove)) {
      addListener(element, EVENT_CROP_MOVE, options.cropmove);
    }

    if (isFunction(options.cropend)) {
      addListener(element, EVENT_CROP_END, options.cropend);
    }

    if (isFunction(options.crop)) {
      addListener(element, EVENT_CROP, options.crop);
    }

    if (isFunction(options.zoom)) {
      addListener(element, EVENT_ZOOM, options.zoom);
    }

    addListener(cropper, EVENT_POINTER_DOWN, (this.onCropStart = this.cropStart.bind(this)));

    if (options.zoomable && options.zoomOnWheel) {
      addListener(cropper, EVENT_WHEEL, (this.onWheel = this.wheel.bind(this)), {
        passive: false,
        capture: true,
      });
    }

    if (options.toggleDragModeOnDblclick) {
      addListener(cropper, EVENT_DBLCLICK, (this.onDblclick = this.dblclick.bind(this)));
    }

    addListener(
      element.ownerDocument,
      EVENT_POINTER_MOVE,
      (this.onCropMove = this.cropMove.bind(this)),
    );
    addListener(
      element.ownerDocument,
      EVENT_POINTER_UP,
      (this.onCropEnd = this.cropEnd.bind(this)),
    );

    if (options.responsive) {
      addListener(window, EVENT_RESIZE, (this.onResize = this.resize.bind(this)));
    }
  },

  unbind() {
    const { element, options, cropper } = this;

    if (isFunction(options.cropstart)) {
      removeListener(element, EVENT_CROP_START, options.cropstart);
    }

    if (isFunction(options.cropmove)) {
      removeListener(element, EVENT_CROP_MOVE, options.cropmove);
    }

    if (isFunction(options.cropend)) {
      removeListener(element, EVENT_CROP_END, options.cropend);
    }

    if (isFunction(options.crop)) {
      removeListener(element, EVENT_CROP, options.crop);
    }

    if (isFunction(options.zoom)) {
      removeListener(element, EVENT_ZOOM, options.zoom);
    }

    removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);

    if (options.zoomable && options.zoomOnWheel) {
      removeListener(cropper, EVENT_WHEEL, this.onWheel, {
        passive: false,
        capture: true,
      });
    }

    if (options.toggleDragModeOnDblclick) {
      removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);
    }

    removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);
    removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);

    if (options.responsive) {
      removeListener(window, EVENT_RESIZE, this.onResize);
    }
  },
};


对象根据value排序

弯道超车:

  // 对object的value排序拿到对应的key列表
  const sortedObjKeys = Object.keys(appSuccessMap).sort((a, b) => {
    return appSuccessMap[b] - appSuccessMap[a]
  })
  // 再通过指定key拿到内容
  const successDataList = sortedObjKeys.map(key => appSuccessMap[key])