有道

君子爱才,取之有道


  • 首页

  • 分类

  • 归档

  • 标签

  • 公益404

20176-Simple-Modules

发表于 2017-10-22   |  

前言


这篇文字简单的实现了define方法,了解闭包的使用场景.

阅读全文 »

20176-20179summary

发表于 2017-10-07   |  

前言


鉴于产品竞争对手步步紧逼的优化升级,自身的服务质量亟需提升,决定拿业务使用频率高的业务线做重构优化,在界面不大变的前提下提高信息提取和展示速度,提升用户体验。

阅读全文 »

Array sort

发表于 2017-09-09   |  

前言

近期在使用Array的sort api排序的遇到了问题,所以花了些时间研究它,这里记录下供参考。

Demo

1
2
3
4
5
6
7
8

let ary = [90, 89, 44, 92, 123, 99];

ary.sort((prev, next) => {
return prev - next > 0 ? -1 : 1;
})

console.log(ary); // [123, 99, 92, 90, 89, 44]

上面的例子很简单,大家在日常业务中应该会用到,结果也显而易见,把 ary 按降序排列;如果改变排序条件prev - nex > 0 ? 1 : -1, 结果就会变成把ary按升序排列。

解析

阅读全文 »

The memory in javascript

发表于 2017-09-03   |  

前言


最近在和新同事的沟通中被问到了javascript是如何管理内存的,以前虽有了解过,但没有系统的记忆,所以答的不理想,乘此机会在这里尽可能整理记忆下。

内存

什么是内存

定义内存就是暂时存储程序和数据的地方。程序语言中低级语言一般都有低级的内存管理接口供开发者有选择性的释放内存,如C语言。高级语言则在创建变量时选择性的分配内存,然后在不使用的时候释放自动释放,这种『垃圾回收』机制并不意味着在高级语言开发中可以不顾内存管理因素肆意滥用。

生命周期

大体了解一个框架可以从生命周期入手,如React,Vue。其实了解任何已被定义的事物都可以如此。程序语言中的内存生命周期大体一致:

  • 分配所需要的内存
  • (读,写)内存
  • 不使用时释放/归还内存
    在所有语言中第一步和第二步都很清晰,但是在javascript中,第三步是隐藏的、不透明的,弄清楚第三步是如何执行的体现了差异性。

内存策略

javascript中分堆内存和栈内存。

  • 堆内存
    堆内存用于存放new创建的对象和数组,因为javascript无法确定声明的函数或者数组对象的具体内存大小,所以无法存放在栈内。它是由虚拟机自动垃圾回收来管理。通常会在栈中定义一个特殊变量,这个变量的取值等于数组或者对象在堆内存中的地址,这就创建了引用变量。数组和对象本身在堆中的分配即时程序在运行到new产生数组和对象的代码块之外,也不会被释放,数组和对象在没有引用变量指向它的时候,才变成垃圾,但只是不能被使用,仍然占用着堆内存,在随后的一个不确定时间被回收。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var o = {
    a: 1,
    b: null
    }; // 给对象及其包含的值分配内存

    // 给数组及其包含的值分配内存(就像对象一样)
    var a = [1, null, "a"];

    function f(a){
    return a + 2;
    } // 给函数(可调用的对象)分配内存

    // 函数表达式也能分配一个对象
    someElement.addEventListener('click', function(){
    someElement.style.backgroundColor = 'blue';
    }, false);
  • 栈内存
    基本类型的变量和对象都是在栈内存中分配。当在一个代码块中定义一个变量时,系统就为这个变量分配内存空间,在当前作用域不包含此变量时,释放栈内存空间。

    1
    2
    var n = 1; // 给数值变量分配内存
    var s = "a"; // 给字符串分配内存

读写内存

阅读全文 »

Think about React

发表于 2017-08-27   |  

前言


近一个月开始着手用React重构项目,从0开始到最终成型,从无头绪到摸到套路还是挺坎坷的,有必要把所有的思考在这里记录。

业务场景


原先的项目是用jquery写的,业务逻辑不复杂,总的就是取数据做展示。抽象出了若干组件:

  • 导航
  • 表单
    • 日期
    • input
    • select
    • check
    • autoComplete
  • 列表
    • table
    • page

注意点

阅读全文 »

Jest-4

发表于 2017-08-13   |  

前言


Jest-1,Jest-2,Jest-3三节介绍了基本API用法,在介绍测试React/Vue之前,我们先来一餐Jquery的业务场景小试牛刀。

业务场景


简单的登录页面,要求有这么几个:

  • 登录名和用户密码不能为空
  • 登录名长度不能超过8位,密码长度最小4位,不能超过6位
  • 登录名只允许正确格式的邮箱,密码只允许大小写英文字母及数字
  • 登录名和密码都输入的情况下才能点击登录按钮,其他情况登录按钮处于无效状态
  • 登录成功后提示登录成功,一秒后跳转到用户中心页面;登录失败模态窗口提示失败原因

单元测试


小结


本节介绍了异步测试的代码,是不是可以开始实际业务情景测试了?当然可以,但是Jest并不止这些内容。下节先介绍Jquery业务场景的登录测试。

Jest-3

发表于 2017-08-06   |  

前言


Jest-1、Jest-2分别讲了断言基础和Mock基础,满足了大部分同步测试的需求。这节主要描述异步测试的情况,主要场景是回调、Promise以及async/await:

测试代码


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** __mocks__/requestfks.js **/
const users = {
4: {name: 'Mark'},
5: {name: 'Paul'}
}
export default function request(url) {
return new Promise((resolve, reject) => {
const userId = parseInt(url.substr('/users/'.length), 10)
process.nextTick(
() => users[userId]
? resolve(users[userId])
: reject({
error: 'User with '+ userId +' not found.'
})
)
})
}
1
2
3
4
5
6
7
8
9
10
11
12
/** requestfks.js **/
const http = require('http')

export default function request(url){
return new Promise(resolve => {
http.get({path: url}, response => {
let data = ''
response.on('data', _data => (data += _data))
response.on('end', () => resolve(data))
})
})
}
1
2
3
4
5
6
/** user.js **/
import request from './requestfks'

export function getUserName (userId) {
return request('/users/' + userId).then(user => user.name)
}

异步回调


这里引用官网的例子,重点是要引入内置参数done:

1
2
3
4
5
6
7
8
9
test('the data is peanut butter', done => {
function callback(data) {
expect(data).toBe('peanut butter');
// 在回掉执行完成时调用done方法通知Jest异步过程结束
done();
}

fetchData(callback);
});

Promise


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
const resolveFunc = jest.fn((d) => {
return new Promise((resolve, reject) => {
resolve(d)
})
})
const rejectFunc = jest.fn((d) => {
return new Promise((resolve, reject) => {
reject(d)
})
})
const catchFunc = jest.fn((d) => {
return new Promise((resolve, reject) => {
resolve(d+o)
}).catch((d) => {
throw new Error(d.message)
})
})
it('callback', function () {
console.time('done time')
setTimeout(function (){
expect(1).toBeTruthy()
expect(2).toBe(2)
expect(1).toBeFalsy()
console.timeEnd('done time')
}, 2000)
jest.runOnlyPendingTimers()
})
it('promise resolve', () => {
return expect(resolveFunc(1)).resolves.toBe(1)
})
it('promise reject', () => {
return expect(rejectFunc(1)).rejects.toBe(1)
})
it('promise catch', () => {
return expect(catchFunc(2)).rejects.toEqual(new Error('o is not defined'))
})
var states = {
top: false,
bottom: false,
right: false
}
function prepareState(cb) {
// 异步处理多个state
setTimeout(function(){
states = {
top: true,
right: true,
bottom: true
}
cb()
},3000)
jest.runAllTimers()
}

function validateState() {
return states.top && states.bottom && states.right
}
function waitOnState() {
expect(states).toHaveProperty('top', true)
return new Promise((resolve, reject) => {
setInterval(() => {
console.log('...waiting')
if (states.top && states.bottom && states.right) {
resolve()
}
}, 1000)
jest.runAllTimers()
}).then(() => {
console.log(states)
})
}
it('hasAssertions', () => {
expect.hasAssertions()
prepareState(function(){
expect(validateState(states)).toBeTruthy()
})
return waitOnState()
})
it('assertions 2', () => {
expect.assertions(2)
prepareState(function(){
expect(validateState(states)).toBeTruthy()
})
return waitOnState()
})

async/await


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
jest.mock('../requestfks')
import * as user from '../user'
it('async/await', () => {
expect.assertions(1)
return user.getUserName(15).catch((data) => {
expect(data).toEqual({"error": "User with 15 not found."})
})
})
it('async/await', async () => {
expect.assertions(1)
await expect(user.getUserName(5)).resolves.toEqual('Paul')
})
it('async/await rejects', async () => {
expect.assertions(1)
await expect(user.getUserName(3)).rejects.toHaveProperty('error')
})
it('async/await resolves', async () => {
expect.assertions(1)
const data = user.getUserName(4)
await expect(data).resolves.toEqual('Mark')
})
it('Promise.catch', async () => {
expect.assertions(1)
return user.getUserName(2).catch(e => {
expect(e).toHaveProperty('error')
})
})
it('try/catch', async () => {
expect.assertions(1)
try {
await user.getUserName(12)
} catch (e) {
expect(e).toHaveProperty('error')
}
})

小结


本节介绍了异步测试的代码,是不是可以开始实际业务情景测试了?当然可以,但是Jest并不止这些内容。下节先介绍Jquery业务场景的登录测试。

Jest-2

发表于 2017-08-06   |  

前言


Jest-1主要讲述了Jest冢对Expect的用法,这篇Jest-2将讲述重要的mock部分

Mock


单元测试的主要做的事情给予我们想给的,得到我们期望的。现在的业务代码中都是由各个模块串联而成,你包我,我包他,一个包可能很大很长,但最终会抛出公共接口。我们在测试引入包时,不需关心内部具体实现,这时可以使用Mock功能,直接给出期望的结果。
除主测之外的代码,都可以Mock。

Function Mock


在上一篇中的Function里也提到jest.fn,这里细说:

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
it('mockFn.mock.calls', () => {
const f = jest.fn()
f(1, 2)
f(2, 3)
// 调用过程存入集合
expect(f.mock.calls).toEqual(expect.arrayContaining([[1, 2], [2, 3]]))
})
it('mockFn.mock.instances', () => {
const f = jest.fn()
const a = new f()
const b = new f()
// 引用计数
expect(f.mock.instances[0]).toBe(a)
})
it('mockFn.mockClear', () => {
function f() {
return 3
}
f = jest.fn(()=>4)
const a = new f()
const b = new f()
f(2)
expect(f.mock.calls[2]).toEqual([2])
// 清空执行轨迹
f.mockClear()
expect(f.mock.instances).toEqual([])
expect(f.mock.calls).toEqual([])
// f.mockRestore()
// 重置mock.fn
f.mockReset()
expect(f()).toBeUndefined()
})
it('mockFn.mockImplementationOnce', () => {
var myMockFn = jest.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false))
// 可逐一调用
expect(myMockFn((err, val) => val)).toBeTruthy()
expect(myMockFn((err, val) => val)).toBeFalsy()
expect(myMockFn()).toBeUndefined()
})
it('mockFn.mockReturnValueOnce', () => {
const myMockFn = jest.fn()
// 只执行一次,后重置
myMockFn.mockReturnValueOnce(90)
expect(myMockFn()).toBe(90)
expect(myMockFn()).toBeUndefined()
})

Class Mock


jest.fn是单个函数的模拟,一个具体的Module可能会抛出多个接口,同样可以模拟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
jest.mock('../request', (url) => {
let users = {
4: {name: 'Mark'},
5: {name: 'Paul'}
}
return new Promise((resolve, reject) => {
if (url) {
const userId = url.substr('/users/'.length)
process.nextTick(
() => users[userId]
? resolve(users[userId])
: reject({
error: 'User with '+ userId +' not found.'
})
)
}
})
})

还有一种形式:

1
2
3
jest.mock('../request')
// 接下来针对需要自定义的方法单独jest.fn
jest.default = jest.fn()

计时器Mock


计时器经常在业务中用到,setTimeout,setInterval,clearTimeout,clearInterval;要是在测试中按真实的设置等下去那就疯啦,所以facebook的天才们提供了模拟方案,即做了延迟处理又不用等多时:

1
2
3
4
5
6
7
8
9
10
// 启用timer mock
jest.useFakeTimers()
// 调用全部timer mock
jest.runAllTimers()
// 运行pending中的timers,如一个定时器(1000)的回调触发了另一个定时器,这个后触发的定时器(10000ms)处于pending
jest.runOnlyPendingTimers()
// 快进
jest.runTimersToTime(1000)
// 清空pending的timers
jest.clearTimers()

小结


以上讲了function mock、class mock以及timers mock的种种api,在实际测试环境中出场率还是挺高的。接下一节将介绍异步检测,非常重要哟。

Jest-1

发表于 2017-08-06   |  

前言


本系列内容基本上按照官网的示例来讲解如何入门。

匹配器


首先讲匹配器,是单元测试最基础的部分,一般称为断言,如:assert,tap,chai,should等断言库。Jest内置了断言库,正式场景中不需引入断言库。
我们从基本类型讲起,

字符串


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
it('"" toBe ""', () => {
expect('').toBe('')
})
it('"" toEqual ""', () => {
expect('').toEqual('')
})
it('"1" not toBe ""', () => {
// 逆向断言
expect('1').not.toBe('')
})
it('"1" not toEqual ""', () => {
expect('1').not.toEqual('')
})
it('"1" not toEqual 1', () => {
expect('1').not.toEqual(1)
})
it('"1" not toBe 1', () => {
expect('1').not.toBe(1)
})
it('"hello world" toMatch "/world/"', () => {
// 正则匹配器
expect('hello world').toMatch(/world/)
})
it('"hello world" not toMatch "/world~/"', () => {
expect('hello world').not.toMatch(/world~/)
})
it('"hello world" toContain "world"', () => {
// 交集
expect('hello world').toContain('world')
})
it('"hello world" is String', () => {
expect('hello world').toEqual(expect.any(String))
})
it('"hello world" is not Number', () => {
// 类型判断
expect('hello world').not.toEqual(expect.any(Number))
})
it('"hello wordl" toHaveLength 11', () => {
// 长度判断
expect('hello wordl').toHaveLength(11)
})

数字


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
it('12 toBe 12', () => {
expect(12).toBe(12)
})
it('0.1+0.2 not toBe 0.3', () => {
expect(0.1+0.2).not.toBe(0.3)
})
it('0.1+0.2 toBeCloseTo 0.3', () => {
// 无限接近
expect(0.1+0.2).toBeCloseTo(0.3)
})
it('0.1+0.2 toBeGreaterThan 0.3', () => {
expect(0.1+0.2).toBeGreaterThan(0.3)
})
it('0.1+0.2 not toBeLessThan 0.3', () => {
expect(0.1+0.2).not.toBeLessThan(0.3)
})
it('1+2 toBeGreaterThanOrEqual 3', () => {
expect(1+2).toBeGreaterThanOrEqual(3)
})
it('1+2 toBeLessThanOrEqual 3', () => {
expect(1+2).toBeLessThanOrEqual(3)
})

数组


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
test('[1,2,3,4,500] not toBe [1,2,3,4,500]', () => {
expect([1,2,3,4,500]).not.toBe([1,2,3,4,500])
})
test('[1,2,3,4,500] toEqual [1,2,3,4,500]', () => {
expect([1,2,3,4,500]).toEqual([1,2,3,4,500])
})
test('[1,2,3,4,500] toContain 500', () => {
expect([1,2,3,4,500]).toContain(500)
})
test('[1,2,3,4,500] not toContain "500"', () => {
expect([1,2,3,4,500]).not.toContain('500')
})
test('[1,[2],3,4,500] not toContain 2', () => {
expect([1,[2],3,4,500]).not.toContain(2)
})
test('[1,[2],3,4,500] toContain [2]', () => {
// 多维数组交集多个
expect([1,[2],3,4,500]).toEqual(expect.arrayContaining([[2]]))
})
test('[1,3,4,500] arrayContanin [3,4]', () => {
// 一维数组交集多个
expect([1,[2],3,4,500]).toEqual(expect.arrayContaining([3,4]))
})
it('["hello wordl"] toHaveLength 1', () => {
expect(['hello wordl']).toHaveLength(1)
})
it('stringMatching', () => {
const helloworld = [
'hello',
'world'
]
const sm = [
expect.stringMatching(/^hell/),
expect.stringMatching(/^wo/),
]
// 遍历交集
expect(helloworld).toEqual(expect.arrayContaining(sm))
})

布尔


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
it('false toBeFalsy', () => {
expect(false).toBeFalsy()
})
it('"" toBeFalsy', () => {
expect("").toBeFalsy()
})
it('0 toBeFalsy', () => {
expect(0).toBeFalsy()
})
it('null toBeFalsy', () => {
expect(null).toBeFalsy()
})
it('undefined toBeFalsy', () => {
expect(undefined).toBeFalsy()
})
it('NaN toBeFalsy', () => {
expect(NaN).toBeFalsy()
})
it('everything toBeTruthy', () => {
expect(1).toBeTruthy()
})

函数


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
function sum(a) {
return a
}
// 基于mock fn
const func = jest.fn((a, b)=>(a + b))
const func2 = jest.fn((a, b)=>(a + b))
it('func toHaveBeenCalledWith 3, 2', () => {
func(3, 2)
// 调用时的参数3,2
expect(func).toHaveBeenCalledWith(3, 2)
})
it('func toHaveBeenCalled', () => {
// 已被调用
expect(func).toHaveBeenCalled()
})
it('func toHaveBeenLastCalled', () => {
func(9, 9)
// 最后一次被调用
expect(func).toHaveBeenLastCalledWith(9, 9)
})
it('func2 toHaveBeenCalled', () => {
expect(func2).not.toHaveBeenCalled()
})
it('func toHaveBeenCalledTimes 2', () => {
// 被调用两次
expect(func).toHaveBeenCalledTimes(2)
})
it('func toThrowError ', () => {
const throwFunc = function (data){
throw new Error('...error')
}
// 抛出错误
expect(throwFunc).toThrowError('...error')
})
it('func toThrow', () => {
expect(() => {
throw new Error()
}).toThrow()
})

对象


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
it('{hello: "world"} toHaveProperty hello', () => {
const helloworld = {hello: 'world'}
// 属性检测
expect(helloworld).toHaveProperty('hello')
})
it('{hello: "world"} not toHaveProperty hello:"google"', () => {
const helloworld = {hello: 'world'}
expect(helloworld).not.toHaveProperty('hello', 'google')
})
it('{hello: {world: "one"} deep referencing hello.world', () => {
const helloworld = {hello: {world: 'one'}}
// deep属性
expect(helloworld).toHaveProperty('hello.world', 'one')
})
it('[{hello: 1, world: 2, every: 3}] toContainEqual {world: 2, every: 3}', () => {
// 数组或者是可被数组化(Array.from)的类数组对象
const helloworldevery = [{
world: 2,
every: 3
},{
hello: 1,
world: 2
},{
every: 3
}]
const worldevery = {
world: 2,
every: 3
}
// 部分对象检测
expect(helloworldevery).toContainEqual(worldevery)
})
it('现有的对象满足我期望的对象', () => {
const feature = {
height: 177,
width: 90,
padding: {
top: 20,
bottom: 20
},
content: ['h','e','l','l','o']
}
const current = {
height: 177,
width: 90,
padding: {
top: 20,
bottom: 20,
left: 20,
right: 20
},
content: ['h','e','l','l','o'],
checked: false
}
// 整个对象集合检测
expect(current).toMatchObject(feature)
})
it('A toBeInstanceOf Class A', () => {
function A() {}
function B() {}
const newA = new A()
// 实例检测
expect(newA).toBeInstanceOf(A)
})

其他


1
2
3
4
5
6
7
it('undefined toBeUndefined', () => {
expect(undefined).toBeUndefined()
})

it('null toBeUndefined', () => {
expect(null).toBeNull()
})

小结


以上总的讲述了Expect基本的使用API,接下来讲一讲另一个重要的方法mock。

contentEditable

发表于 2017-07-05   |  
  • HTML

    1
    <div contentEditable=true data-ph="My Placeholder String"></div>
  • CSS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [contentEditable=true]{
    border:1px solid grey;
    }

    [contentEditable=true]:empty:not(:focus):before{
    content:attr(data-ph);
    color:grey;
    font-style:italic;
    }
12…7
wangtuda

wangtuda

新一天新积累
I Belive I Can.

64 日志
1 分类
55 标签
© Thu Mar 24 2016 08:00:00 GMT+0800 (CST) - 2017 wangtuda
由 Hexo 强力驱动
主题 - NexT.Pisces