Giter VIP home page Giter VIP logo

aymaxli.github.io's People

Contributors

aymaxli avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

aymaxli.github.io's Issues

数组去重的优化

Background

常见的数组去重方法是利用对象去模拟一个 Map,让数组元素作为 Mapkey 值来达到去重的效果。但这种方法也是存在一定弊端的,因为他会把元素强制转换成字符串,若数组元素中既有 number ,又有 string ,例 [1, '1'] ,就会出现问题。

const unique = arr => {
  const map = {}

  return arr.filter(el => map.hasOwnProperty(el) ? false : (map[el] = true))
}

console.log(unique([1, 3, '3', 4, 8, 'aa'])) // [1, 3, 4, 8, "aa"]

Solution

要解决这个问题也不难,我们可以Mapvalue 值设置为该元素的类型,在判断元素是否存在时对类型也进行判断

const unique2 = arr => {
  const map = {}

  return arr.filter(el => map.hasOwnProperty(el) ? (map[el] !== typeof el) : (map[el] = typeof el, true))
}

console.log(unique2([1, 3, '3', 4, 8, 'bb'])) // [1, 3, "3", 4, 8, "bb"]

串行执行任务

串行执行任务

Promise & Array.prototype.reduce

利用 Array.prototype.reduce 构成 Promise

// 异步操作
function work(url) {
  return new Promise((resolve, reject) => {
    setTimeout(_ => {
      resolve(url)
    }, Math.random() * 5 * 1000)
  })
}

// 任务列表
let urls = [
  'https://fabiaoqing.com/search/search/keyword/%E6%9D%83%E5%BE%8B%E4%BA%8C',
  'http://n.sinaimg.cn/ent/4_ori/upload/d411fbc6/20170927/OJ9Z-fymesmq8502845.gif',
  'http://www.dachuantuan.com/uploadfile/2017/1211/20171211093255435.jpg',
  'http://pic.cr173.com/up/2017-6/2017621172176384.jpg',
  'http://moviepic.manmankan.com/yybpic/yanyuan/29095.jpg',
  'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQEnuvhLqyDfDDNFb3-nAFeL_4qn3Pz658cnA70xvCET0RXMgz1Eg',
  'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQmEtL5FQkyRog2uec9MycO91Oqz2Plwhtd4DtyV3URcHc09YTv'
]

// 串行执行
urls.reduce((prePromise, curUrl) => {
  return prePromise.then(_ => {
    return work(curUrl).then(console.log)
    // 若要传值,可在 work 中 resolve(xxx) 或者在 then 的回调函数里面 return xxx
  })
}, Promise.resolve())

async / await

通过 ES7 的 async / await API 可以非常便捷的实现串行执行任务,若任务间需要就行传值可在 then 的回调函数中 return xxx ,然后用一个变量缓存起来

(async _ => {
  for (let i = 0; i < urls.length; i ++) {
    await work(urls[i]).then(console.log)
  }
})()

递归

ES5 及以下可以通过递归实现。当前任务完成之后才递归调用下一个任务

function sequence(arr) {
  var index = -1

  // 第一次执行
  next()

  // 封装下次任务
  function next() {

    // 递归结束的条件
    if (index >= arr.length - 1) return

    // 执行下一个任务
    index ++
    arr[index](next)
  }
}

sequence([
  function (next) {
    setTimeout(function () {
      console.log('one')
      next()
    }, Math.random() * 5 * 1000)
  },
  function (next) {
    console.log('two')
    next()
  },
  function (next) {
    setTimeout(function () {
      console.log('three')
      next()
    }, 0)
  }
])

利用 Array.prototype.shift 改良

function sequence (arr) {
  next()

  function next() {
    if (arr.length <= 0) return

    arr.shift()(next)
  }
}

若使用 IIFE (立即执行函数) ,写法更精炼更*,但是可读性就没有前者高了

function sequence (arr) {

  ;(function next() {
    if (arr.length <= 0) return

    arr.shift()(next)
  })()
}

继续优化,加入传值功能

function sequence (arr, initialVal) {
  next(initialVal)

  function next(data) {
    if (arr.length <= 0) return

    arr.shift()(data, next)
  }
}

sequence([
  function (data, next) {
    setTimeout(function () {
      console.log(data)
      next(data + 1)
    }, Math.random() * 5 * 1000)
  },
  function (data, next) {
    console.log(data)
    next(data + 1)
  },
  function (data, next) {
    setTimeout(function () {
      console.log(data)
    }, 0)
  }
], 1)

IIFE 写法

function sequence (arr, initialVal) {

  ;(function next(data) {
    if (arr.length <= 0) return

    arr.shift()(data, next)
  })(initialVal)
}

Reference

树的遍历

树的遍历

Demo

  <div id="a">
    <p id="haha"></p>
  </div>
  <div id="b">
    <div id="d">
      <span id="e"></span>
      <span id="f"></span>
    </div>
  </div>
  <div id="c"></div>

DFS

递归法

最弟弟的方法,当树的高度很大的时候函数调用栈会十分的深

// DFS 递归法实现
dfs(document.body, el => console.log(`${el.tagName}#${el.id}`))

function dfs (t, cb) {

  // 对当前节点进行处理
  cb(t)

  // 若该节点是叶子节点,直接返回
  if (!t.children.length) return

  // 若该节点是父节点,查询其叶子节点
  Array.prototype.forEach.call(t.children, el => dfs(el, cb))
}

迭代法(栈)

借助,复杂度为 O(n),树高度很大时会优于递归法

// DFS 迭代法,栈实现
dsf_stack(document.body, el => console.log(`${el.tagName}#${el.id}`))

function dsf_stack (t, cb) {
  let stack = []
  
  stack.push(t)

  while (stack.length > 0) {
    let node = stack.pop() // 出栈

    cb(node)

    // 若该节点为父节点,则将其子节点压入栈中  
    if (node.children.length) Array.prototype.forEach.call(node.children, el => stack.push(el))        
  }
}

WFS

用迭代法实现,借助队列,复杂度也为 O(n)

// WFS 迭代法,队列实现
wsf_queue(document.body, el => console.log(`${el.tagName}#${el.id}`))

function wsf_queue (t, cb) {
  let queue = []
  
  queue.unshift(t)

  while (queue.length > 0) {
    let node = queue.pop() // 出队

    cb(node)

    // 若该节点为父节点,则将其子节点入队 
    if (node.children.length) Array.prototype.forEach.call(node.children, el => queue.unshift(el))        
  }
}

记一个类背包问题

Introduction

从给定的无序、不重复的数组 DATA 中,取出 N 个数,使其相加和 为 SUM

Resolution

解法1 - 回溯法

思路

对所有可能的结果进行 dfs 直到找到合法解

sum = first + f(n - 1, sum - first)

边界值

  • 成功 n == 0 && sum == 0
  • 失败 n <= 0 && sum > 0
  • n > 0 , 继续

js 实现

function search(data, n, sum) {
  // 找到解
  if (n == 0 && sum == 0) return true

  // 回溯
  if (n <= 0 || data.length < n) return false

  const first = data[0]
  const rest = data.slice(1, data.length)
  // 继续进行 dfs
  // 若该子树所有路径都不通,继续遍历相邻子树
  return search(rest, n - 1, sum - first) || search(rest, n, sum)
}

console.log(search([5, 1, 4, 2, 7], 3, 12))

Reference

手撸一个事件订阅器

代码

function EventEmitter() {
  // 依赖队列集
  this.dep = {
    // name1: [{
    //   once: true,
    //   fn: fn
    // }],
    // name2: [{
    //   once: true,
    //   fn: fn
    // }]
  }
}

EventEmitter.prototype.on = function (name, fn, once) {
  var obj = {
    fn,
    once: once || false // 是否为 once,默认为 false
  }

  this.dep[name] ? this.dep[name].push(obj) : this.dep[name] = [obj] // 是否存在事件 name 的依赖队列,是则 push ,否则新建一个数组
}

EventEmitter.prototype.once = function (name, fn) {
  this.on(name, fn, true)
}

EventEmitter.prototype.emit = function (name) {
  var args = Array.prototype.slice.call(arguments, 1)
  var onceArr = [] // 记录这次事件中 once 的函数

  this.dep[name].forEach((el, index) => {
    el.fn.apply(null, args) // 执行函数
    // 假如是 once ,标记
    if (el.once === true) onceArr.push(index)
  })

  // 移除 once 的函数
  onceArr.forEach(index => this.dep[name].splice(index, 1))
}

EventEmitter.prototype.remove = function (name, fn) {
  var index = this.dep[name].findIndex(el => el.fn.name === fn.name) // 找到要 remove 的函数的下标

  this.dep[name].splice(index, 1) // 移除
}

生成随机数字串的正确姿势

错误姿势

生成长度为 4 的随机数字串

String.prototype.slice.call(Math.random() * 10000, 0, 4)

之前在生成数字串时用过这个方法,后来发现会出现 453. 这样错误数字串,因为 Math.random() 生成了 0.0453xxxxxxxxxx 这样的随机数,这个数字乘以 10000 就变成 453.xxxxxxx,前面的零被吃掉了。。。

正确姿势

所以正确姿势应该是介个样几

String.prototype.slice.call(Math.random(), 2, 6)

计划通 ~

用正则捕获 URL 中的参数

前端经常会遇到的一个场景,解决的方法很多,这里总结一哈用正则来捕获的方法。

const getParamsFromUrl = function(url = window.location.href) {
  const reg = /(?:[?&])([^=]+)=([^&]*)/g
  const params = {}

  let temp
  while ((temp = reg.exec(url)) !== null) {
    params[temp[1]] = decodeURIComponent(temp[2])
  }

  return params
}

console.log(getParamsFromUrl('http://haha.com/heihei.html?dsf=787&qw12=332-32&fff=ffsa'))

微信小程序 cookie polyfill

Detail

微信小程序的 wx.request() 并没有实现保存 cookie 以及下次请求自动带上 cookie 的功能。

Solution

由于小程序没有实现这一功能,我在项目中对小程序的 wx.request() 进行了封装,polyfill 了浏览器中 cookie 的相应功能。大概思路是:

  1. 接受响应时检查是否有 Set-cookie 头部,有则缓存在小程序的离线缓存中
  2. 发送请求时检查离线缓存中是否有 cookie,有则将其设入请求头中

封装 wx.request

当请求成功,调用 success 回调函数时,调用下面的 _updateCookie() 方法,更新本地 cookie

// 创建请求实例
function _createWxRequest(opts) {
  let requestTask

  return new Promise((resolve, reject) => {
    requestTask = wx.request({
      ...opts,
      header: {
        // 从离线缓存中取出 cookie
        'cookie': wx.getStorageSync("cookies"),
        ...opts.header
      },
      success: function(res) {
        // 更新 cookie
        _updateCookie(res.header)

        resolve(res.data)
      },
      fail: function(error) {
        reject(error)
      }
    })
  })
}

将 cookie 写入离线缓存

由于 cookie 个数不确定,所以我将多个 cookie 以 key0=val0;key1;=val1; 的形式拼接成一串字符串写入离线缓存中。更新 cookie 时,逻辑如下:

  1. 需要读出旧的 cookie 字符串,并利用正则表达式将其重新拆分为形如 ['key0=val0;' 'key1=val1;']旧 cookie 数组
  2. 从响应头的 cookie 字符串中,利用正则表达式读出新 cookie 数组
  3. 新旧 cookie 数组合并,若有 key 值冲突,则采用新的
// 更新 cookie
function _updateCookie(header) {
  // 取得本次请求的 cookie (新)
  let newCookies = header['Set-Cookie'] ? header['Set-Cookie'].match(/[\w\_]+\=\w+;/g) : []
  if (newCookies.length) {
    // 取得旧 cookie
    let cookies = wx.getStorageSync('cookies').match(/[\w\_]+\=\w+;/g) || []

    // 新旧 cookie 合并
    while (newCookies.length) {
      const nc = newCookies.pop()
      const ncKey = nc.match(/([\w\_]+)(?=\=)/g)[0] // 取得 cookie 名

      // 原来就存在这个 cookie 则替换
      let index = cookies.findIndex(el => el.indexOf(ncKey) >= 0)
      if (index >= 0) cookies[index] = nc
      // 否则加入尾部            
      else cookies.push(nc)
    }

    // 写入离线缓存
    wx.setStorage({
      key: 'cookies',
      data: cookies.join('')
    })
  }
}

Summary

上面的方法截取自我在小程序项目中封装的 zajax.js 中,一个类 axios 的小程序请求工具,若有需要可直接引入项目中使用 ~

iOS 微信浏览器或 Safari 跨域请求 cookie 丢失

Background

这周回实验看看师弟师妹,她们说遇到一个 cookie 丢失的问题。

Detail

确认了一下场景如下

  • 环境: iOS 微信浏览器 or Safari
  • 现象:跨域请求 cookie 丢失
    首先,一般浏览器的跨域请求设置了 withCredentials = true 之后就能带上 cookie ,所以安卓和 PC 是没有问题的,然而 iOS 的这个系统向来比较看重安全,要用户手动设置允许才行 😓。。。。

Solution

然鹅,我们肯定不能让用户去手动设置允许滴,所以我就曲线救国一下,帮她们配置一下 nginx,把前端页面和后端应用代理到同一个域下就好了。🌝

将类数组 (array-like) 对象转化为数组

Background

What is Array-Like Object

JavaScript 中常见的类数组有:

  • xxx.childNodes 返回的 NodeList
  • xxx.childrennode.getElementsByXXX 返回 HTMLCollection
  • functionarguements 对象

Situation

类数组是没有 Array.prototye 上的方法的,但是很多时候我们需要使用一些常用的数组 api, 就可以用下面方法将就转化为数组 ~

Solutions

使用 Array.prototype.slice 进行转化

;(function sum (a, b, c) {
	let args = Array.prototype.slice.call(arguments)

	console.log(args.reduce((pre, cur) => pre + cur))  // 6
})(1, 2, 3)

使用 ES6 新增的 Array.from 进行转化

;(function sum (a, b, c) {
	let args = Array.from(arguments)

	console.log(args.reduce((pre, cur) => pre + cur))  // 6
})(1, 2, 3)

使用 ES6 的 ... 拓展运算符

;(function sum (a, b, c) {
	let args = [...arguments]

	console.log(args.reduce((pre, cur) => pre + cur))  // 6
})(1, 2, 3)

Reference

基于 vue-cli 的前端静态资源缓存策略方案

Resources Analysis

前端静态资源主要有:

  • 出口文件 index.html
  • 修改频率较*.js*.css*.less
  • 修改频率较的字体、图标的文件 *.svg*.ico*.eot*.ttf
  • 置于 static/ 目录下的文件,webpack 不会对该目录下的文件进行打包

其中,该项目使用 webpack 进行构建,并对除了 index.html static/* 外的静态资源拼接了 hsah 版本号,因此每次构建后,除了 index.htmlstatic/* 外的所有静态资源都会有新的 URL

用户直接访问的是 index.html ,打开某个模块,才会通过标签中 URL 访问某个静态资源

vue-cli 打包图解.png | left | 745x531

Preparing

在这之前,需要了解 http1.0/1.1 的缓存机制,了解强缓存协商缓存的异同优劣,这里就不一一赘述了。

Solution

  • dist/index.html 因为 URL 不会改变切体积非常的小,使用协商缓存或者直接不缓存。
  • dist/static/
    • 带 hash 的静态资源由于每次构建后,index.html 里引用的静态资源 URL 都已经改变了,可直接使用强缓存,减少请求数。
    • 而其他而置于原 static/ 目录下的不经过 webpack 打包的文件,虽然 URL是不会变化的,但是这些资源一般不会改动,因此也使用强缓存
#### 前端 pc 端 - 缓存配置 ####
location / {
  root /opt/project/ncp/ncp-surface;
  index  index.html index.htm;
  # 对于 index.html,由于其更新较为频繁,且 url 不会改变,
  # 因此关闭强缓存,使用协商缓存,通过 Etag 或 Last-Modified 判断,命中则返回 304
  add_header Cache-Control no-cache;
  add_header Pragma no-cache;
}

location ^~ /static/ {
  root /opt/project/ncp/ncp-surface;
  # 对于 static/,使用强缓存,通过 Cache-Control 的 max-age 或者 Expires 判断,命中则返回 200
  expires 30d;
}
##############################

subscription

hi, dev. After reading your first post in your bolg, I suddenly fall in love with your writeing style. So... I want to subscripte your post. It means that I hope you can devlop a subscription

用正则格式化金额

Background

前端处理页面经常会有格式化金额的需求,常用的格式为 12,345,678.12 ,即整数部分每三位用逗号隔开,保留两位小数。

Resolution

正则

捕获逗号前的数字,不捕获逗号后的位置,把逗号前捕获的数字替换成 $1,

function formatAmount(input) {
  const amount = Number(input)

  // 判断输入是否合法
  if (Number.isNaN(amount) || input === '') return '输入金额不合法!'

  // 先保留两位小数,再插入逗号
  return amount.toFixed(2).replace(/(\d{1,3})(?=(?:\d{3})+\.)/g,'$1,')
}

['', '12', '12345', '12345678.123', 'abc'].forEach(el => {
  console.log(formatAmount(el))
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.