Toffee's Blog

  • 首页

  • 归档

  • 搜索

指数运算性能比较

发表于 2019-04-08

自定义递归函数

1
2
3
4
function calculateExponent(base,exponent){
if(exponent == 1) return base
return base * calculateExponent(base,--exponent)
}

Math.pow()

Math.pow() 函数返回基数(base)的指数(exponent)次幂,即 base^exponent。

ES7指数操作符

在ES7中引入了指数运算符**

1
2 ** 2 // 4

比较性能(测试环境 v8)

比较使用console.time和console.timeEnd方法,得出运算耗时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//依次比较10次幂,100次幂,1000次幂,10000次幂循环100次耗时
console.time()
for(let i =0;i<100;i++){
calculateExponent(i,10)
}
console.timeEnd()

console.time()
for(let i =0;i<100;i++){
Math.pow(i,10)
}
console.timeEnd()

console.time()
for(let i =0;i<100;i++){
i ** 10
}
console.timeEnd()

结果

得出四次结果耗时

1
2
3
4
['10', 0.2, 0.052, 0.052],
['100', 0.503, 0.057, 0.089],
['1000', 3.273, 0.090, 0.083],
['10000', 28.346, 0.081, 0.120]

可以发现递归函数时间大大多于其他两个方法,那么剔除递归函数的结果再看:

根据图可以发现,两个方法在不同数量级耗费的时间有长有短,在1000数量级时候**运算符时间却少于Math.pow,来到10000数量级时**运算符时间大幅度长于Math.pow,我们可以根据实际情况选择使用**还是Math.pow

实现小程序拖拽排序

发表于 2019-03-27

实现效果


这是最近我做的一个需求,由于产品项目模块众多,达近80个模块,所以有了这个模块管理排序的需求,得益于小程序自带的组件movable-view(基础库 1.2.0 开始支持),使得拖拽这一交互效果的实现变得优雅。在这里记录下写这个模块的一些心得

核心方法

有三个核心方法,分别是bindchange事件(movable-view自带事件,类似于touchmove事件),touchend事件,touchstart事件
核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
touchStart(index, fatherIndex) {
this.clientX = null
this.clientY = null
},
touchMoving(index, fatherIndex, event) {
this.clientX = event.detail.x + (this.moveAreaWidth / 4 / 2) //this.moveAreaWidth每个modal
this.clientY = event.detail.y + ITEM_HEIGHT / 4
},
touchEnd(index, fatherIndex) {
if (this.clientX && this.clientY) {
const row = Math.floor(this.clientY / (ITEM_HEIGHT / 2))
const column = Math.floor(this.clientX / (this.moveAreaWidth / 4))
const newIndex = row * 4 + column
index == newIndex ? this.hasChangedSort = false : (this.hasChangedSort = true,this.handleDragEnd(index, fatherIndex, newIndex))
}
}

在这里,touchStart事件初始化每次拖拽后设定的clientX,clientX值
touchmove事件中需要处理这样一件事,根据每个模块的高度以及宽度(需要通过小程序APIwx.createSelectorQuery计算得出)实时计算出落点所在的x,y坐标,最后touchend事件计算出x,y坐标所对应的行列数,根据行列数计算出当前模块所在对应数组中的索引序列(handleDragEnd方法),在data数据源数组中替换两个模块数据的索引下标

遇到的问题

由于直接改变索引下标会造成瞬闪现象,就是拖拽完成后模块会先在原来位置闪现一下再回到拖拽后的位置,这是因为操作数据会直接改变视图,所以我们需要一个延时操作$nextTick,在这个延时方法中去操作新位置模块的x,y坐标,就可以解决这个问题

Java链表基本功能实现

发表于 2019-03-05

链表

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

接口

一个基本的Java链表主要有一下的功能:

add

首先,新建一个List类,以及一个Node类,通过List类来调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class List{
private Node root;//定义链表的根节点
private int count = 0;//定义当前列表节点个数
}

public class Node{
private String data;//这是一个数据类型是 String 的链表
private Node next; //每个Node类需要一个指向当前Node的下一个Node,才行形成链关系

pulic Node(String data){
this.data = data;
}

public Node getNext() {
return next;
}

public String getData() {
return data;
}
}

定义完基本数据类,我们来实现add方法
在List中定义add

1
2
3
4
5
6
7
8
9
10
11
public void add(String data){
Node newNode = new Node(data);
if(this.root == null){
this.root = newNode;
} else{
this.root.addNode(newNode,this); //我们需要传入当前list对象,用于新增节点后跟新count
}
}
public void addCount() {
this.count++;
}

Node类中实现addNode,它接受一个Node类型参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void addNode(Node newNode,List list){
if(!this.equals(newNode)){
if(this.next == null){
this.next = newNode;
list.addCount();
} else {
//如果当前下个节点不为空,则让next继续调用addNode方法
this.next.addNode(newNode,list);
}
}
}
//为了做到不重复新增数据,我们需要在Node类中实现一个equals方法

public boolean equals(Node node){
if(this.data == node.data){
return true;
}
return false;
}

get

get方法接受一个int类型的序号,用于找到当前类型对应位置的节点数据,在List类中定义get

1
2
3
4
public String get(int index) {
if (this.root == null) return null;
return this.root.get(index, 0);
}

Node类中定义getNode方法,它除了接受index参数外,还接受一个步进参数step,用于匹配index是否等于当前step

1
2
3
4
public String get(int index, int step) {
if (index == step) return this.data;
return this.next.get(index, ++step);
}

set

set方法用于重新设置链表对应位置节点的数据,

1
2
3
4
public void set(int index, String data) {
if (this.root == null || index < 0 || index > this.count) return;
this.root.setNode(index, data, 0);
}

contains

contains方法返回一个Boolean,判断当前列表是否存在对应的数据

1
2
3
4
5
//List
public boolean contains(String data) {
if (this.root == null) return false;
return this.root.containNode(data);
}

在Node类中定义containNode方法

1
2
3
4
5
6
7
8
9
//Node
public boolean containNode(String data) {
if (this.data.equals(data)) {
return true;
} else {
if (this.next == null) return false;
return this.next.containNode(data);
}
}

size

返回当前链表节点个数

1
2
3
4
//List
public int size() {
return this.count;
}

isEmpty

判断当前链表是否为空

1
2
3
public boolean isEmpty() {
return this.count == 0;
}

remove

删除节点数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//List
public void remove(String data) {
if (this.root == null) return;
if (this.root.equals(data)) { //根节点特殊处理
this.root = this.root.getNext();
this.count--;
} else {
this.root.removeNode(this.root, data,this);
}
}

//同add操作,还需要另外定义一个reduceCount方法供Node实例调用
public void reduceCount() {
this.count--;
}

Node类中定义removeNode

1
2
3
4
5
6
7
8
9
10
//Node
public void removeNode(Node pre, String data, List list) {
if (this.data.equals(data)) {
pre.next = this.next;
list.reduceCount();
} else {
if(this.next != null)
this.next.removeNode(this, data,list);
}
}

toArray

将当前链表输出为array数组对象

1
2
3
4
5
6
7
8
//List 新增一个私有属性arrayList
private String[] arrayList;

publice String[] toArray(){
this.arrayList = new String[this.count];
this.root.toArrayNode(arrayList,0);
return this.arrayList;
}

Node类中定义toArrayNode方法

1
2
3
4
5
6
7
//Node
public void toArrayNode(String [] array,int step){
array[step] = this.data;
if(this.next != null){
this.next.toArrayNode(array,++step);
}
}

前端组件化开发

发表于 2019-02-18

什么是组件化开发


前端组件化开发,就是将页面的某一部分独立出来,将这一部分的 数据层(M)、视图层(V)和 控制层(C)用黑盒的形式全部封装到一个容器内,暴露出一些开箱即用的函数和属性供外部组件调用。

怎么设计一个组件


单一

一个组件的功能是单一的,应该只做一件事。
一个功能如果可以拆分成多个功能点,那就可以将每个功能点封装成一个组件,当然也不是组件的颗粒度越小越好,只要将一个组件内的功能和逻辑控制在一个可控的范围内即可。

与业务逻辑解耦

一个组件应该是纯粹,不参杂业务逻辑的,当涉及到业务时,如日期选择组件,产品要求日期小于当日的不可以选择,否则弹出提示框提示用户,这是我们就应该通过传入的配置参数,来进行判断,如果开启不能选择小于当前日期配置的话,当用户点击不符合要求的日期时,通过组件事件传递,将该事件告知父组件,由父组件进行业务逻辑的处理。

可配置

在一开始设计组件的时候就应该结合业务需求考虑哪些功能应该做成可配置式的,如干警端src/components/noResultComponent(用于空数据时的展示),默认的文案是暂无数据,但是在有时候刚进入页面时,此时网络正在请求数据,页面此时用暂无数据就不太合适,这时候可以传正在加载来告知用户,此时正在请求数据过程中请等待。

属性值的检验

1.传入属性是否合法
2.属性是否必传

在wepy中进行类型检查:

1
2
3
4
5
6
props = {
text: {
type: String,
default: '正在加载'
}
}

Vue中类型检查

1
2
3
4
5
6
7
props = {
someprop: {
type: String,
default: 'someText',
required: true
}
}

React中进行类型检查

1
2
3
4
5
6
7
8
9
10
import React from 'react'
import PropTypes from 'prop-types'
class MyComponent extends React.Component {
render() {
// ... do things with the props
}
}
MyComponent.propTypes = {
someProps:PropTypes.string.isRequired
}

组件生命周期


初始化阶段,读取属性值,并进行处理

展示阶段,监听属性变化,进行相应逻辑处理

组件销毁阶段,及时移除组件中对整个系统产生副作用的操作,如addEventListener,setInterval等等。

在小程序中定义组件生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Component({
lifetimes: {
attached() {
// 在组件实例进入页面节点树时执行
},
detached() {
// 在组件实例被从页面节点树移除时执行
},
},
// 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
attached() {
// 在组件实例进入页面节点树时执行
},
detached() {
// 在组件实例被从页面节点树移除时执行
},
// ...
})

事件传递


在wepy中,wepy.component基类提供$broadcast、$emit、$invoke三个方法用于组件之间的通信和交互
在Vue中,子组件通过this.$emit发送,父组件只需监听该发送的事件即可
在’React’中,父组件可以使用 props 向子组件传值,而子组件向父组件传值,需要在父组件内定义函数并通过属性传递给子组件,在子组件内通过调用该属性对应的函数,传入参数,传递给父组件内的函数,并在父组件的该函数中做逻辑的处理。

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
//子组件
class ChildComponent extends React.Components {
render() {
return (
<button onClick={
() => {
this.props.clickHandler('This is a click')
}
}></button>
)
}
}
//父组件
import ChildComponent from './child-component'

class ParentComponent extends React.Components {
clickHandler(message) {
console.log(message)
}

render() {
return (
<child-component
clickHandler={ this.clickHandler.bind(this) }
/>
)
}
}

RxJs实现基本拖拽

发表于 2019-01-04

HTML

1
2
3
4
5
6
7
8
9
<div id="anchor">
<div class="video" id="video">
<video width="100%" height="100%" controls >
<source src="http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_stereo.ogg"
type="video/ogg">
Your browser does not support HTML5 video.
</video>
</div>
</div>

CSS

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
<style>
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }

html, body {
margin: 0;
padding: 0;
height: 2000px;
background-color: #eee; }

#anchor {
height: 360px;
width: 100%;
background-color: #F0F0F0; }

.video {
width: 640px;
height: 360px;
margin: 0 auto;
background-color: black; }
.video.video-fixed {
position: fixed;
top: 10px;
left: 10px;
width: 320px;
height: 150px;
cursor: all-scroll; }
.video.video-fixed .masker {
display: none; }
.video.video-fixed:hover .masker {
display: block;
position: absolute;
width: 100%;
height: 180px;
background-color: rgba(0, 0, 0, 0.8);
z-index: 2; }

</style>

JS

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
   import {Observable} from 'rxjs/Rx'

const $video = document.querySelector('#video')
const $anchor = document.querySelector('#anchor')
const scroll = Observable.fromEvent(document, 'scroll'). //监听document事件
map(e => $anchor.getBoundingClientRect().bottom < 0). //map将event事件映射为判断式布尔值
subscribe(bool => { //如果当前元素不在视窗内,则改变样式
if(bool){
$video.classList.add('video-fixed')
}else{
$video.classList.remove('video-fixed')
$video.style.transform = ''
}
})

const mouseDown = Observable.fromEvent($video, 'mousedown') //observe事件
const mouseMove = Observable.fromEvent(document, 'mousemove')
const mouseUp = Observable.fromEvent(document, 'mouseup')

const videoBoundingRect = $video.getBoundingClientRect() //获取viedo元素rect对象
const validValue = (value,max,min)=>{ //比较函数,防止拖拽出屏幕
return Math.min(Math.max(value,min),max)
}
mouseDown.filter(e => $video.classList.contains('video-fixed')). //filter过滤
switchMap(e => mouseMove.takeUntil(mouseUp)). //将高阶Oberve转换为低阶
withLatestFrom(mouseDown, (move, down) => { //结合上一个observable源的结果
return { //move.clientX - down.offsetX =>处理抖动
x: validValue(move.clientX - down.offsetX,window.innerWidth - videoBoundingRect.width/2,0),
y: validValue(move.clientY - down.offsetY,window.innerHeight - videoBoundingRect.height/2,0),
}
}).
subscribe(e => {
$video.style.transform = `translate3D(${e.x}PX,${e.y}px,0)`
})
1…567…9
Toffee

Toffee

44 日志
GitHub E-Mail 我的网站 StackOverflow
0%
© 2018 – 2021 Toffee