1 Star 0 Fork 0

Shadow / code-snippet

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
js小技巧.md 8.99 KB
一键复制 编辑 原始数据 按行查看 历史
Shadow 提交于 2022-04-20 19:52 . no message
theme highlight
vuepress
atom-one-dark

1.函数懒加载

如:判断当前环境是否微信

function isWechat() {
  const result = typeof wx === 'object' && navigator.userAgent.match(/MicroMessenger/i) == "micromessenger";
  // 改写函数,直接返回结果,下次运行则无须经过重重判断了;
  isWechat = function () { 
    return result;
  }
  return result;
}

以上写法会产生闭包,result变量不会被释放,以下针对此问题进行改造:

function isWechat() {
  const result = typeof wx === 'object' && navigator.userAgent.match(/MicroMessenger/i) == "micromessenger";
  // 改写函数,直接返回结果,下次运行则无须经过重重判断了;
  if(result) {
      isWechat = function () { 
        return true;
      }
  } else {
      isWechat = function () { 
        return false;
      }
  }
  
  return isWechat();
}

2.赋值立即执行函数

const obj = {
  // 赋值可以写成立即执行函数,这样不仅仅局限于三元运算符了
  prop: (function() {
    ....
    return value;
  }())
}

3.闭包的应用

如:单请求限制

真实业务场景,如微信小程序的登录,当打开某个页面,用户token若过期,该页面所有接口都会401,此时应发起登录,但需要避免多个接口的401触发多次登录请求,解决办法,可参考以下代码

const handleLogin = (function () {
  let promise;
  return function () {
    // 返回请求的promise
    if(promise) return promise;
    promise = loginApi({
      ...
    }).finally(() => {
      // 请求响应后,需要清空promise
      // 避免下次再发起登录出问题,同时也释放了变量
      promise = null;
    })
    return promise;
  }
}())

以上闭包的应用,单例模式的设计实现也类似

3.1 闭包应用-请求结果缓存

以下请求缓存的封装可直接copy使用哦; 包含请求结果缓存、相同请求单一限制、最大缓存接口数控制。

// 最多缓存接口数
const CACHE_MAX = 20;
const cache = (function () {
  const cache = {};
  const cacheArr = [];
  return {
    get: function (params) {
      const uniKey = JSON.stringify(params);
      return new Promise((resolve, reject) => {
        if (cache[uniKey]) {
          // promise特性,如果resolve出去的是一个promise,则会替代外层promise
          resolve(cache[uniKey]);
        } else {
          const promise = getRequest(params).then(res => {
            if (cacheArr.length > CACHE_MAX) {
              const _api = cacheArr.shift();
              this.remove(_api);
            }
            this.set(uniKey, res);
            resolve(res);
            return res;
          }).catch(err => {
            reject(err);
          })
          // 此处还做了单请求限制
          return cache[uniKey] = promise;
        }
      })
    },
    set: function (uniKey, data) {
      cache[uniKey] = data;
      cacheArr.push(uniKey);
    },
    remove: function (uniKey) {
      // 释放内存
      cache[uniKey] = null;
      delete cache[uniKey];
    }
  }
})()

4.函数结果缓存

对于一些计算需要耗费较大的性能的纯函数,我们可以针对参数进行结果缓存

function _sin(value) {
  return Math.sin(value);
}

const _sin = (function () {
  // 利用闭包创建缓存空间
  const cache = {};
  return function (value) {
    // 缓存中有,则从缓存中拿
    if(cache[value]) return cache[value];
    // 存入缓存并返回结果
    return cache[value] = Math.sin(value);
  }
}())

4.1 函数结果缓存的简单封装:

function fnCache(fn) {
  const cache = {};
  return function (...args) {
    // 将参数变为字符串,作为缓存key
    const key = JSON.stringify(args);
    if(key in cache) {
      return cache[key];
    }
    return cache[key] = fn.apply(this, args);
  }
}
// 用法示例:
function sum() { ...}
const cacheSum = fnCache(sum);

// 我们还可以将key的生成方式暴露出去
function fnCache(fn, keyCreator) {
  const cache = {};
  return function (...args) {
    // 优先自定义的key生成函数
    const key = keyCreator ? keyCreator(args) : JSON.stringify(args);
    if(key in cache) {
      return cache[key];
    }
    return cache[key] = fn.apply(this, args);
  }
}

5.Promise实现chain,将请求变成串行

let promise = Promise.resolve();

const tasks = [
  request1,
  request2,
  request3,
]

tasks.forEach(request => {
  promise = promise.then(() => request())
})

promise.then(res => {
  // 执行到这里表示请求已经串行执行完成
})

6.usePromise

假如有这个一个场景,父组件需要在两个子组件获取完请求结果后做些什么

function usePromise() {
  let resolve,reject;
  // Promise的回调函数参数会被同步执行
  const promise = new Promsie((_resolve, _reject) => {
    resolve = _resolve;
    reject = _reject;
  })
  return {
    promise,
    resolve,
    reject
  }
}

<Child1 @on-got-data="child1Promise.resolve"/>
<Child2 @on-got-data="child2Promise.resolve"/>

const child1Promise = usePromise();
const child2Promise = usePromise();

Promise.allSettled([
  child1Promise.promise,
  child2Promise.promise
]).then(() => {
  // 执行到这里表示Child1、Child2都已经派发过on-got-data事件
})

以上写法算是一个怪招,只是提供一下promise的思路,不太建议使用,因为不止会产生闭包,而且当promise一直没有结果时,会一直占用内存。

7.函数缺省值,及必填限制


// 使用或运算设置缺省值
function fn(a) {
  a = a || 1;
}

// 使用es6函数参数默认值
function fn(a = 1) {}

// 当a未传入,则为触发赋值默认值,导致抛出错误
function triggerError(key) {
  throw new Error(`缺少必填参数${key}`);
}
function fn(a = triggerError('a')) {}

8.使用箭头函数简化代码

// 原始写法
[1,2,3].map(function (item) {
  return item + 1;
})

// 将[1,2,3]每个值+1
// 可以省掉return
[1,2,3].map(item => item + 1)

// return 对象的简化写法
[1,2,3].map(item => { value: item }) // 这样写大括号内会被当成函数体
[1,2,3].map(item => ({ value: item })) // 用括号括起来就解决啦

9.访问对象深层属性,避免报错

const obj = {
  info: undefined
}

// 假如我们要访问obj.info.name属性,此时直接访问则为报错

// 1.if判断:
let name;
if(obj.info) {
  name = obj.info.name;
}

// 2.或运算,注意需要将或运算内容括起来
let name = (obj.info || {}).name;

// 3.可选链,需要考虑兼容性
let name = obj?.info?.name;

10.短路语句

function fn(callback) {
  callback && callback();
}

11.创建[1,2,3]数组

const arr = [...new Array(3).keys()];

12.截断、清空数组

// 截断
// 1
arr = arr.slice(0,3);

// 2
arr.length = 3;

// 清空数组
// 1
arr = [];

// 2
arr.splice(0, arr.length)

// 3
arr.length = 0;

13.数组乱序

arr.sort(() => Math.random() - 0.5)

14.数组去重

// 1.双循环
// 其他比如for+for、for+includes等,都可以归类于双循环
function unique(arr) {
  return arr.filter((item, index) => arr.indexOf(item) === index)
}

// 2.hash(对象属性唯一的特点)
function unique(arr) {
  const obj = Object.create(null);
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!obj[arr[i]]) {
      res.push(arr[i])
      obj[arr[i]] = true;
    }
  }
  return res;
}

// 3.Set
function unique(arr) {
  return [...new Set(arr)];
}

15.限制最大最小值

// 1.三元运算
const num = 10;
const result = num > 5 ? 5 : num;

// 2.Math.min
const num = 10;
const result = Math.min(num, 5);

16.字符串补0

// 1
let day = '9';
('00'+day).slice(-2); // 09

// 2
let day = '9';
day.padStart(2, '0');

17.获取当前时间戳

// 1
new Date().getTime()

// 2
+new Date();

// 3
Date.now();

18.交换两个值

let num1 = 1;
let num2 = 2;
[num1, num2] = [num2, num1];

19.对象、数组克隆

// 浅克隆对象
const obj1 = { a: 1, b: 2, o: { c: 3 } }
const obj2 = {...obj1};
const obj3 = Object.assign({}, obj1);

// 浅克隆数组
const arr1 = [1,2,3];
const arr2 = [...arr1];
const arr3 = arr1.slice(0);

// 深克隆,这种方式最简单,只不过缺点也很多
const obj4 = JSON.parse(JSON.stringify(obj1));

20.去除数组假值

const arr = [false, '', null, undefined, 0];
arr.filter(Boolean); // 一行搞定,需要注意的是,0也会被过滤掉

21.干掉if

// 假如有这样一段代码
if(type === 'success') {
  return 'green';
}
if(type === 'warning') {
  return 'yellow';
}
if(type === 'error') {
  return 'red';
}

// switch改造
switch(type) {
  case 'success':
    return 'green';
  case 'warning':
    return 'yellow';
  case 'error':
    return 'red';
}

// 对象映射改造
const typeMap = {
  success: 'green',
  warning: 'yellow',
  error: 'red',
}
return typeMap[type];
1
https://gitee.com/ytiona/code-snippet.git
git@gitee.com:ytiona/code-snippet.git
ytiona
code-snippet
code-snippet
master

搜索帮助