aymaxli / aymaxli.github.io Goto Github PK
View Code? Open in Web Editor NEW:point_right: my blog,show in issues ~
Home Page: https://github.com/AymaxLi/AymaxLi.github.io/issues
:point_right: my blog,show in issues ~
Home Page: https://github.com/AymaxLi/AymaxLi.github.io/issues
常见的数组去重方法是利用对象去模拟一个 Map
,让数组元素作为 Map
的 key
值来达到去重的效果。但这种方法也是存在一定弊端的,因为他会把元素强制转换成字符串,若数组元素中既有 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"]
要解决这个问题也不难,我们可以将 Map
的 value
值设置为该元素的类型,在判断元素是否存在时对类型也进行判断。
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"]
利用 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())
通过 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)
}
<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(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))
}
}
用迭代法实现,借助队列,复杂度也为 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))
}
}
从给定的无序、不重复的数组 DATA 中,取出 N 个数,使其相加和 为 SUM
对所有可能的结果进行 dfs 直到找到合法解
sum = first + f(n - 1, sum - first)
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))
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)
计划通 ~
前端经常会遇到的一个场景,解决的方法很多,这里总结一哈用正则来捕获的方法。
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'))
微信小程序的 wx.request()
并没有实现保存 cookie 以及下次请求自动带上 cookie 的功能。
由于小程序没有实现这一功能,我在项目中对小程序的 wx.request()
进行了封装,polyfill 了浏览器中 cookie 的相应功能。大概思路是:
Set-cookie
头部,有则缓存在小程序的离线缓存中当请求成功,调用 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 以 key0=val0;key1;=val1;
的形式拼接成一串字符串写入离线缓存中。更新 cookie 时,逻辑如下:
['key0=val0;' 'key1=val1;']
的旧 cookie 数组。// 更新 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('')
})
}
}
上面的方法截取自我在小程序项目中封装的 zajax.js 中,一个类 axios
的小程序请求工具,若有需要可直接引入项目中使用 ~
这周回实验看看师弟师妹,她们说遇到一个 cookie 丢失的问题。
确认了一下场景如下
withCredentials = true
之后就能带上 cookie ,所以安卓和 PC 是没有问题的,然而 iOS 的这个系统向来比较看重安全,要用户手动设置允许才行 😓。。。。然鹅,我们肯定不能让用户去手动设置允许滴,所以我就曲线救国一下,帮她们配置一下 nginx,把前端页面和后端应用代理到同一个域下就好了。🌝
JavaScript 中常见的类数组有:
xxx.childNodes
返回的 NodeListxxx.children
和 node.getElementsByXXX
返回 HTMLCollectionfunction
的 arguements 对象类数组是没有 Array.prototye
上的方法的,但是很多时候我们需要使用一些常用的数组 api, 就可以用下面方法将就转化为数组 ~
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)
Array.from
进行转化;(function sum (a, b, c) {
let args = Array.from(arguments)
console.log(args.reduce((pre, cur) => pre + cur)) // 6
})(1, 2, 3)
...
拓展运算符;(function sum (a, b, c) {
let args = [...arguments]
console.log(args.reduce((pre, cur) => pre + cur)) // 6
})(1, 2, 3)
前端静态资源主要有:
index.html
*.js
、*.css
、*.less
等*.svg
、*.ico
、*.eot
、*.ttf
等static/
目录下的文件,webpack 不会对该目录下的文件进行打包其中,该项目使用 webpack 进行构建,并对除了 index.html
和 static/*
外的静态资源拼接了 hsah 版本号,因此每次构建后,除了 index.html
和 static/*
外的所有静态资源都会有新的 URL。
用户直接访问的是 index.html
,打开某个模块,才会通过标签中 URL 访问某个静态资源。
在这之前,需要了解 http1.0/1.1 的缓存机制,了解强缓存和协商缓存的异同优劣,这里就不一一赘述了。
dist/index.html
因为 URL 不会改变切体积非常的小,使用协商缓存或者直接不缓存。dist/static/
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;
}
##############################
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
前端处理页面经常会有格式化金额的需求,常用的格式为 12,345,678.12
,即整数部分每三位用逗号隔开,保留两位小数。
捕获逗号前的数字,不捕获逗号后的位置,把逗号前捕获的数字替换成 $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))
})
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.