徐凌志的博客


  • 首页

  • 标签

  • 分类

  • 所有

js数组求并集交集和差集

发表于 2018-10-13 | 分类于 前端 | 阅读次数:

项目中有遇到一些涉及数学集的运算,在网上寻找解决方法的时候顺便总结一下。

假设有数组a = [1, 2, 3]和b = [2, 4, 5]

ES7 includes

1
2
3
4
5
6
7
8
// 并集
let union = a.concat(b.filter(n => !a.includes(n))) // [1, 2, 3, 4, 5]

// 交集
let inter = a.filter(n => b.includes(n)) // [2]

// 差集
let diff = a.concat(b).filter(n => !a.inclueds(n) || !b.inclueds(n)) // [1, 3, 4, 5]
阅读全文 »

v-for循环中使用自定义属性值进行事件代理性能优化

发表于 2018-09-23 | 分类于 前端 | 阅读次数:

原文:vue组件通信全揭秘(共7章)

官方定义

默认情况下父作用域的不被认作 props 的特性绑定 (attribute bindings) 将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例属性 $attrs 可以让这些特性生效,且可以通过 v-bind 显性的绑定到非根元素上。

这个选项不影响 class 和 style 绑定

事件代理

当我们用 v-for 渲染大量的同样的 DOM 结构时,但是每个上面都加一个点击事件,这个会导致性能问题,那我们可以通过 HTML5 的 data 的自定义属性做事件代理。

阅读全文 »

数组去重全姿势

发表于 2018-09-15 | 分类于 前端 | 阅读次数:

总结一下数组去重的几种方式,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
var arr = [1, 1, 2, '1']

// 双层循环
function unique1(arr) {
var res = []
for (var i = 0, arrLen = arr.length; i < arrLen; i++) {
for (var j = 0, resLen = res.length; j < resLen; j++) {
if (arr[i] === res[j]) {
break
}
}
// 如果array[i]是唯一的,那么执行完循环,j等于resLen
if (j === resLen) {
res.push(arr[i])
}
}
return res
}

// indexOf
function unique2(arr) {
var res = []
for (var i = 0, arrLen = arr.length; i < arrLen; i++) {
if (res.indexOf(arr[i]) === -1) {
res.push(arr[i])
}
}
return res
}

// 排序后去重
function unique3(arr) {
var res = []
var sortedArr = arr.concat().sort()
var seen
for (var i = 0, arrLen = sortedArr.length; i < arrLen; i++) {
// 如果是第一个元素或者相邻的元素不相同
if (i === 0 || seen !== sortedArr[i]) {
res.push(sortedArr[i])
}
seen = sortedArr[i]
}
return res
}

// filter中with indexOf
function unique4(arr) {
var res = arr.filter(function(item, index, array) {
return array.indexOf(item) === index
})
return res
}

// filter中with 排序后去重
function unique5(arr) {
var res = arr
.concat()
.sort()
.filter(function(item, index, array) {
return index === 0 || item !== array[index - 1]
})
return res
}

// Object键值对(对象去重)
function unique6(arr) {
var res = []
var obj = {}
for (var i = 0, arrLen = arr.length; i < arrLen; i++) {
var item = arr[i]
// 因为对象的键值对只能是字符串,防止1和'1'的情况,所以采取一个typeof加类型的方法来区分
// 然而还是无法区分两个对象,比如 {value: 1} 和 {value: 2},因为 typeof item + item 的结果都会是 object[object Object]
// 所以我们可以使用 JSON.stringify 将对象序列化转化成字符串
if (!obj[typeof item + JSON.stringify(item)]) {
obj[typeof item + JSON.stringify(item)] = true
res.push(item)
}
}
return res
}

// Object键值对with filter
function unique7(arr) {
var obj = {}
return arr.filter(function(item, index, array) {
return obj.hasOwnProperty(typeof item + JSON.stringify(item))
? false
: (obj[typeof item + JSON.stringify(item)] = true)
})
}

// ES6 Set
function unique8(arr) {
return Array.from(new Set(arr))
// return [...new Set(arr)];
}

// ES6 Map
function unique9(arr) {
const seen = new Map()
return arr.filter(a => !seen.has(a) && see.set(a, 1))
}

console.log(unique6(arr))

用循环的方式代替递归进行深拷贝(简版)

发表于 2018-09-02 | 分类于 前端 | 阅读次数:

以下为待拷贝对象:

1
2
3
4
5
6
7
8
9
let a = {
a1: 1,
a2: {
b1: 1,
b2: {
c1: 1
}
}
}

可以转换成树形结构:

1
2
3
4
5
6
7
8
9
   a
/ \
a1 a2
| / \
1 b1 b2
| |
1 c1
|
1

用循环遍历一棵树,需要借助一个栈,当栈为空时就遍历完了,栈里面存储下一个需要拷贝的节点

首先往栈里放入种子数据,key 用来存储放哪一个父元素的哪一个子元素拷贝对象

然后遍历当前节点下的子元素,如果是对象就放到栈里,否则直接拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
function cloneLoop(x) {
const root = {} // 根节点
// 处理的栈
const cloneStack = [
{
parent: root,
key: undefined,
data: x
}
]

while (cloneStack.length) {
// 深度优先
const node = cloneStack.pop()
const parent = node.parent
const key = node.key
const data = node.data

// 初始化复制目标,囡key为undefined则直接拷贝到父元素,否则到子元素
let res = parent
if (key !== undefined) {
res = parent[key] = {} // res同parent[key],以下处理res
}

for (const key in data) {
if (data.hasOwnProperty(key)) {
if (isObject(data[key])) {
// 如果子元素存在对象,则往栈顶推入一个待处理的对象
cloneStack.push({
parent: res,
key: key,
data: data[key]
})
} else {
res[key] = data[key] // 基础类型直接复制
}
}
}
}

return root
}

// 判断是否为对象
function isObject(x) {
if (typeof x === 'object' || typeof x === 'function') {
if (x.nodeType === 1) {
return false // 排除html元素
} else {
return Object.prototype.toString.call(x) === '[object Object]'
}
} else {
return false
}
}

// test demo

let a = {
a1: 1,
a2: {
b1: 1,
b2: {
c1: 1
}
}
}

let b = cloneLoop(a)

a.a2.b1 = 4

console.log(a) // { a1: 1, a2: { b1: 4, b2: { c1: 1 } } }
console.log(b) // { a1: 1, a2: { b1: 1, b2: { c1: 1 } } }

理解js的调用堆栈和event-loop

发表于 2018-08-24 | 分类于 前端 | 阅读次数:

先上一张图

1
2
3
4
5
6
7
8
function multiply(x, y) {
return x * y;
}
function printSquare(x) {
var s = multiply(x, x);
console.log(s);
}
printSquare(5);

宏任务和微任务

macro-task(宏任务):包括整体代码script,setTimeout,setInterval, setImmediate, I/O, UI rendering

micro-task(微任务):Promise,process.nextTick, Object.observe, MutationObserver

事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。听起来有点绕,我们用文章最开始的一段代码说明

1
2
3
4
5
6
7
8
9
10
11
setTimeout(function() {
console.log('setTimeout');
})

new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then');
})

console.log('console');
  1. 这段代码作为宏任务,进入主线程。
  2. 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。(注册过程与上同,下文不再描述)
  3. 接下来遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue。
  4. 遇到console.log(),立即执行。
  5. 好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了then在微任务Event Queue里面,执行。
  6. ok,第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event
  7. Queue开始。我们发现了宏任务Event
  8. Queue中setTimeout对应的回调函数,立即执行。
  9. 结束。

其他

  • 函数的直接调用实际上是call的一种语法糖
    1
    2
    function say(word) { console.log(world); } 
    say("Hello world"); // == say.call(window, "Hello world");

页面异步加载js文件的方式

发表于 2018-08-08 | 分类于 前端 | 阅读次数:

当浏览器碰到script标签时

  1. <script src="script.js"></script> 没有defer或者async,浏览器会立即加载并执行js文件,立即指的是该script标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。
  2. <script src="test.js" async></script> 有async,加载和渲染后续文档元素的过程将和script.js的加载与执行并行进行(异步)
  3. <script src="test.js" defer></script> 有defer,加载后续文档元素的过程将和script.js的加载并行进行(异步),但是script.js的执行要在所有元素解析完成之后,DOMContentLoaded事件触发之前完成。

注意的几点

  1. 渲染引擎遇到script标签会停下来,等到执行完脚本,继续向下渲染
  2. defer是“渲染完再执行”,async是“下载完就执行”,defer如果有多个脚本,会按照在页面中出现的顺序加载,多个async脚本不能保证加载顺序
  3. async适合不依赖任何脚本或者不被任何脚本依赖的情况,比如Google Analytics
  4. 加载es6模块的时候设置type=module,异步加载不会造成阻塞浏览器,页面渲染完再执行,可以同时加上async属性,异步执行脚本(利用顶层的this等于undefined这个语法点,可以侦测当前代码是否在es6模块之中)

图解

js数组循环删除的坑

发表于 2018-08-01 | 分类于 前端 | 阅读次数:

改项目上一个遗留的 bug 时,“不小心”掉进了数组循环删除的坑,之前的开发中其实有解决过这类问题,为加深印象,特意再手打一次。

阅读全文 »

现代模块依赖管理器原理

发表于 2018-07-21 | 分类于 前端 | 阅读次数:

读完《你不知道的 JavaScript》上卷后,发现里面有个对模块依赖器原理的探究很值得记录。

原理是使用一个myModule变量接收一个立即执行函数通过闭包返回的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let myModule = (function() {
var modules = {} // 模块保存的区域
function define(name, deps, impl) {
for (let i = 0; i < deps.length; i++) {
// 遍历依赖数组
deps[i] = modules[deps[i]] // 找到modules中保存的模块
}
modules[name] = impl.apply(impl, deps) // 正确追踪到依赖
}
function get(name) {
return modules[name]
}
return {
define,
get
}
})()

从而能通过暴露的 api 完成模块的定义和依赖引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
myModule.define('bar', [], function() {
function hello(who) {
return `let me introduce ${who}! `
}
return {
hello
}
})

myModule.define('foo', ['bar'], function(bar) {
let name = 'louis'
function awesome() {
console.log(bar.hello(name).toUpperCase())
}
return {
awesome
}
})

let bar = myModule.get('bar')
let foo = myModule.get('foo')

foo.awesome() // LET ME INTRODUCE LOUIS!

有那么一首歌,如诗般缱绻

发表于 2018-07-07 | 分类于 杂项 | 阅读次数:
揪心的玩笑与漫长的白日梦


Your browser does not support the audio tag.
阅读全文 »

CSS3之Matrix属性总结

发表于 2018-06-27 | 分类于 前端 | 阅读次数:

背景

在web端刷微博点开图片时,发现有图片向左旋转和向右旋转等功能,出于职业本能,我自然的猜测:当然是用transfrom: rotate(ndeg)做的啦!

但是打开console却发现跟我想的有点不太一样:

阅读全文 »
12
Louis Xu

Louis Xu

一个可能会发牢骚的地方

18 日志
3 分类
20 标签
GitHub 微博
© 2018 Louis Xu
由 Hexo 强力驱动
|
主题 — NexT.Pisces v6.1.0