漫溯


  • 首页

  • 归档

  • 标签

年中感悟

发表于 2017-08-06

最近两个多月真的是经历了好多好多事情,注定会成为人生中最铭记的一个阶段了。
甚至觉得自己做出的许多选择都是错误的,但是年轻哪有不犯错的时候。
其实某种意义上来讲,这错误倒也不至于毁天灭地的那种,换算成收益也许就是少收获些东西。
不过长远上来说,却也未必不是一件好事,早些试错总好过于迟来犯错。
至于得失也都是早晚的事,该是你的总归是你的。

有时是要深思熟虑,有时是要坚决果断。
我问了太多人的意见,最后决定还是要靠自己,过多的追问反而乱了方寸。
结果让自己不如意。

面对二选一的时候其实可以两个都不选的。
这是隐藏的第三个选项。

究竟路要怎么走,毕竟路还很长。
虽然有点绕弯,有时还倒退了,但方向总是对的吧。

一位同学,喜欢深圳,因为那里的气候在十一月也可以穿裙子;
另一位同学,久居重庆,因为和一个妹子有了羁绊。

简单心思反而成就了一生。

我什么都不知道。

从children与childNodes说说nodeType

发表于 2017-06-05

image

nodeType

在读Zepto v1.2.0的源码时,有处函数如下,用来获取子元素

1
2
3
4
5
function children(element) {
return 'children' in element ?
slice.call(element.children) :
$.map(element.childNodes, function(node){ if (node.nodeType == 1) return node })
}

里面涉及到了children和childNodes两个属性,函数中首先判断children属性是否存在,否则从childNodes属性中获取所需元素。
对于这些属性首先看下在MDN中的解释:

ParentNode.children 是一个只读属性,返回 一个Node的子elements 的HTMLCollection

HTMLCollection 接口表示一个包含了元素(元素顺序为文档流中的顺序)的通用集合(generic collection)。

ChildNodes 接口包含特定于Node 对象的方法,这些对象可以有一个父对象。

节点有多种类型,比如元素、文本和注释,区分这些节点可以通过检查nodeType 属性。共计有12种节点类型,其中比较有代表性的如下:

节点类型 描述 值 常量
Element 一个元素节点, 如<p>和<div> 1 ELEMENT_NODE
Attr 属性 2 ATTRIBUTE_NODE
Text Element或者Attr中实际的文字 3 TEXT_NODE
Comment 注释 8 COMMENT_NODE
Document 代表整个文档(DOM 树的根节点) 9 DOCUMENT_NODE

HTML DOM nodeType 属性

也就是说,childNodes包含了children的内容。
回到zepto中,children函数获取了父元素中所有子元素,并以数组的形式返回;而childNodes则筛选了nodeType == 1的节点,即 Element类型的节点。

除此之外,zepto在其他地方同样用到了nodeType相关的属性,在上面的表格中有判断节点类型的常量来代替数字值。所以zepto在判断是否为document函数中写成了如下形式:

1
function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }

其中DOCUMENT_NODE的值为9

此children非彼children

在zepto源码中继续查找可以发现$.fn上同样定义了一个children函数,这里的函数实际上就是平常所使用的选择器了,能够把获取到的数组转换为zepto类型。

1
2
3
4
5
$.fn = {
children: function(selector){
return filtered(this.map(function(){ return children(this) }), selector)
}
}
阅读全文 »

深度优先与广度优先方法对DOM树的遍历

发表于 2017-06-01

image

深度优先搜索算法(英语:Depth-First-Search,简称DFS)是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。

wikipediaen.wikipedia.org/wiki/Depth-first_search

image

广度优先搜索算法(英语:Breadth-First-Search,缩写为BFS),又译作宽度优先搜索,或横向优先搜索,是一种图形搜索算法。简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。广度优先搜索的实现一般采用open-closed表。

wikipediaen.wikipedia.org/wiki/Breadth-first_search

DOM的结构和数据结构中的“树”型结构比较类似,所以很自然的就可以使用DFS和BFS进行遍历。

深度优先可以理解为“一条路走到黑”,只有在撞到了“南墙”才回头。具体到DOM树中来说就是,从根节点开始,继而访问它的直接子元素,并依此往复直到不存在子元素。下面通过JavaScript使用了递归的方法实现了DFS,在控制台依次打印出节点的元素名,类名和层次

1
2
3
4
5
6
7
8
9
10
11
const DFS = function(node) {
if (!node) {
return
}
let deep = arguments[1] || 1
console.log(`${node.nodeName}.${node.classList} ${deep}`)
if (!node.children.length) {
return
}
Array.from(node.children).forEach((item) => DFS(item, deep + 1))
}

广度优先可以理解为“一层一层的剥离”,对同一层次的元素全部遍历过后,再遍历下一层。广度优先适合使用队列这种数据结构来实现,将每层的节点依次放入队列,并根据队列“先入先出”的特性取出就可以了。在JavaScript中模拟队列的的方法可以使用数组方法的push和shift对应入队和出队操作。同样给出JavaScript实现的DOM树遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const BFS = (root) => {
if (!root) { return }
let queue = [{
item: root,
depth: 1
}]
while (queue.length) {
let node = queue.shift()
console.log(`${node.item.nodeName}.${node.item.classList} ${node.depth}`)
if (!node.item.children.length) {
continue;
}
Array.from(node.item.children).forEach((item, index, arr) => {
queue.push({
item: item,
depth: node.depth + 1
})
})
}
}

在BFS实现时,打印出队元素的同时会将出队元素的直接子节点放入队列,因此在上面计算层次的时候,子节点的层次关系是由它的父级确定的。

阅读全文 »

JavaScript数组操作的一些方法

发表于 2017-05-20

数组的操作方法有很多,根据特性总的来说可以划分为三类,也就是

  • Mutator 改变原数组对象值的方法
  • Accessor 不改变原数组对象带返回值的方法
  • Iteration 数组遍历的方法

修改原数组方法

添加删除 pop, push, shift, unshift

可以通过一个图来解释
image
这些操作都会修改数组的length值。对于类数组,可以使用apply调用

排序 reverse, sort

reverse方法将数组前后位置颠倒,所以一种逆序字符串的方法可以写成

1
2
let str = 'hello'
str.split('').reverse().join(''); //'olleh'

sort() 方法提供了可选的规则对数组进行排序,默认会把元素按照转换为的字符串的每个字符的Unicode位点进行排序。
比如,1,10,21,2默认排序的结果为1,10,2,21。
sort() 方法可以接收一个回调函数callback(a, b)用作设置比较规则,它的返回值的几种对应情况:

  • 小于 0, a 会被排列到 b 之前;
  • 等于 0, a 和 b 的相对位置不变;
  • 大于 0, b 会被排列到 a 之前。

对于数字的比较也可以简单的使二者相减来实现升序排列:

1
2
3
function compareNumbers(a, b) {
return a - b;
}

而降序返回b-a即可。那么回到前面的数组,升序排列的函数使用ES6的话可以便捷的写成:

1
[1,10,21,2].sort((a,b)=>a-b); //[1,2,10,21]

splice

splice()可以删除、添加数组元素

array.splice(start[, deleteCount, item1, item2, …])

可接收3个参数

  • start​ 指定修改的开始位置。
    • 如果超出了数组的长度,则从数组末尾开始添加内容;
    • 如果是负值,则表示从数组末位开始的第几位(从1计数)。
  • deleteCount 表示要移除的数组元素的个数。
    • 如果 deleteCount 是 0,则不移除元素。
    • 如果 deleteCount 大于start 之后的元素的总数,则从 start及后面的元素都将被删除。
    • 如果deleteCount被省略,则其相当于(arr.length - start)。
  • item1, item2, … 要添加进数组的元素,从start 位置开始。

splice()操作可以返回被删除元素的数组,如果没有删除则返回空数组

1
2
3
4
let arr = [1,10,21,2]
arr.splice(2, 0, 32) // [1, 10, 32, 21, 2]
arr.splice(-2, 0, 3) // [1, 10, 32, 3, 21, 2]
arr.splice(3, 1, 43) // [1, 10, 32, 43, 21, 2]

所以替换操作也就相当于是先删掉部分元素后再插入相同数量的元素

填充 fill, copyWithin

fill方法使用给定值,填充一个数组。

arr.fill(value, start, end)

  • value 填充值
  • start 填充起始位置
  • end 填充结束位置
1
2
3
[1, 2, 3].fill(4, 1, 2) // [1, 4, 3]
Array(3).fill(4); // [4, 4, 4]
[].fill.call({length: 3}, 4) // {0: 4, 1: 4, 2: 4, length: 3}

arr.copyWithin(target, start, end)

数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。
它接受三个参数。

  • target(必需):从该位置开始替换数据。
  • start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。
1
2
[1, 2, 3, 4, 5].copyWithin(-2); // [1, 2, 3, 1, 2]
[1, 2, 3, 4, 5].copyWithin(0, 3, 4); // [4, 2, 3, 4, 5]
阅读全文 »

一个 JavaScript 柯里化问题

发表于 2017-05-16

起因是在群里有人问了一道题:

实现如下功能:var a = add(2)(3)(4); // 9

后来有人给出了三个字答案----“柯里化” 维基百科中解释为:

在计算机科学中,柯里化(英语:Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

上述问题的一种实现方式如下:

1
2
3
4
5
6
7
8
9
10
11
let add = x => {
let sum = x
let tem = y => {
sum += y
return tem
}
tem.toString = tem.valueOf = () => sum
return tem
}

那么为什么需要复写toString和valueOf呢?

在权威指南中是这样解释两种方法的:

toString()方法的作用是返回一个可以表示这个对象的字符串。在希望使用字符串的地方用到对象的话,JavaScript会自动调用这个方法,如果没实现这个方法,类会默认从Object.prototype中继承toString()方法,这个方法的结果是[object object]。
valueOf()用来将对象转换为原始值。比如,当数学运算符(除了“+”运算符)和关系运算符作用域数字文本表示的对象时,会自动调用valueOf()方法。

未定义的toString或valueOf方法,返回的是tem声明的函数,而我们需要把对象转换成原始类型的值(数值、字符串和布尔值),因此需要重写两个方法使它返回需要的值。

阅读全文 »

WebSocket的应用实例

发表于 2017-04-14

因为大多数的应用实例都是聊天室,在这个基础上做个拓展,建立一个问答形式的应用。主要规则就是提出问题,并且问题必须是可以用“是”或者“不是”来回答的。

服务端

提问和回答的人各有一个就足够了,所以这个问答聊天室应该是划分为多个房间。房间作为独立的一个功能,可以从业务逻辑中分离出来,所以在组件中新建一个room的模块

1
2
3
4
5
6
var Room = function() {
this.users = []
this.ques = []
}
module.exports = Room

房间的功能应该包括了:

  • 加入用户
  • 移除用户
  • 给房间中所有用户发送消息
  • 添加问题
  • 更新问题答案
  • 其他
阅读全文 »

WebSocket的学习及认识

发表于 2017-04-13

介绍

Web应用的一般信息交换过程是客户端通过浏览器发出一个请求,服务器端接收和处理收到的请求并返回,然后客户端在浏览器中呈现出内容。也即是说一个request对应一个response请求,只有客户端主动发起才会获得服务端的返回信息,而服务端无法主动发送。

可是有什么用呢?

在一些场景里,我们有时会对应用的实时性要求比较高,类似于股票信息,设备监控,聊天室等等,这些都需要服务器能够主动的推送信息到客户端。使用传统的技术来解决这些问题的方案主要有轮询和comet,

  • 轮询是使用定时器周期性的向服务端发送请求,但是同样也会带来问题,首要的是如果服务器未更新信息而频繁的发送请求会增加服务器压力并造成资源的浪费,所以这是一种很低效的方式。
  • comet也是一种轮询,只是在服务器没有信息返回的时候会保持一段时间,直到数据、状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互,但在比较频繁的交互过程里,这种方式并没有本质上的改变。

HTML5 WebSocket的推出让浏览器和服务器之间可以建立无限制的通信,任何一方都可以主动发消息给对方。

阅读全文 »

拖放(三):可拖放交换的组件块

发表于 2017-03-20

可拖放交换位置的widget组件

拖放排序可被应用的地方非常多,像标签页的排序,widget组件的互换位置,或者任意摆放的图片墙等等。

从过程上考虑,首先要能把目标元素给拖动起来(⊙_⊙),之后检测与其他元素之间的距离执行交换,最后来释放掉目标元素。

如果是使用鼠标函数拖放的话,需要将可拖动的对象设定为绝对定位,这样能更方便的操作。因此基本上只要考虑以下几方面:

  • 重绘位置
  • 距离检测
  • 序列重排
  • 拖放函数
阅读全文 »

拖放(二):鼠标事件实现拖放

发表于 2017-03-13

原生的拖放事件(drag and drop)属于HTML5的接口,对它的兼容性可以通过caniuse网站看到。

拖放过程

根据拖放的过程来考虑,拖放的效果其实是实现元素对鼠标的跟随,将鼠标的坐标位移设置到元素上。

阅读全文 »

拖放(一):原生drag and drop

发表于 2017-03-12

拖放流程

用户能够将一个可拖动元素移动到另一个可放置元素中,释放鼠标来应用这些改变。

拖放总的来说有三步: 鼠标按下、移动和释放

image

阅读全文 »
Rain

Rain

javascript, js, html, css

10 日志
1 分类
5 标签
© 2017 Rain
由 Hexo 强力驱动
主题 - NexT.Muse