看过好几把关于机器学习的书,但是很多书只是停留于算法原理阶段,或者更着重介绍算法原理, <<集体智慧编程>>这本书更多的是从实践来介绍书,比如你要撘一个推荐系统你怎么做,,还有怎么来做一个垃圾邮件过滤系统等….
接下来介绍一下我对于做一个推荐系统的理解.

前言

推荐系统是一个什么东西呢
简单来说就是两个字—-推荐

推荐系统的出现是伴随评价系统出现而出现的,评价系统就是我们的打分制,比如对一部电影每个人都对他进行打分

用户 电影A评价 电影B评价 新电影C评价 新电影D评价
张三 5.0 3.5 4.9 2.0
李四 2.5 5.0 2.0 3.0
王二 3.1 4.7

比如现在三个人张三李四王二,王二有两部电影没有看过,但是张三和李四看过了,现在就是要把电影C或D推荐给王二,推荐系统就是从其他数据分析来确定推荐次序

相似度

推荐系统如何根据其他用户的数据来推荐呢

这里就要介绍一个相似度的概念,也可以加权系数,对于张三和李四,这两个人都看过电影C和D,但是我们该听谁的呢,我们可以把两个人的评价加起来然后排序,但是假如张三和李四品味相差太多,而且张三的评价可能会把某个王二喜欢看的电影评价总值拉低

这个时候我们就要考虑一个相似度问题,就是尽量避免一些品味同王二不同的人对排名造成影响

这里我们引进相似度这个概念,我们看张三和李四对电影A、B同王二的差别,这里我们使用距离这个概念,我们使用距离公式 l=((r1^2 + r2^2+....rN^2)^1/n)

然后通过距离算得S(相似度)=1/(1+l)

当l=0时,两者相似度为1(最大),当l很大时,相似度几乎为零

用户 电影A评价 电影B评价 相似度
张三 5.0 3.5 0.23
李四 2.5 5.0 0.59

通过求相似度(权重)我们就把同我们臭味相投的人的评价提高了,那些同我们不一样品味的人评价作用降低了

选择

基于人物的评价推荐有个缺点就是人的评价有时候会比物品多,而且变化频繁,假如使用上面的方法的话,每次每一个用户评价过后,就要重新算一次相似度了,假如用户不多或者电影不多还好,一旦数据偏多服务器就扛不住了

所以我们要换一个角度出发,计算物品的相似度,这样图表就变成这样了

电影 用户-张三 用户-李四 相似度
A 5.0 2.5
B 3.5 5.0

这样有个好处就是可以离线处理数据,我们可以搭建一个离线处理系统,让一个系统专门处理数据,当服务器需要的时候再拿取过去.

总结

搭建一个简单推荐系统并不会你想象中那么简单,但是要搭建一个功能强大速度快的推荐系统需要的不仅仅是这么一点,还需要考虑系统的稳定和速度.

git是当今流行的版本控制工具,一般我们可能只要会push, pull就可以了,
但是当我们同别人共同工作的时候,我们必须要了解git协同开发的一些重要流程.

前言

git作为当今最流行的版本控制工具之一,当时开发出来就是为了管理Linux庞大源代码的分布式版本控制工具.
由于Linux源代码过于巨大,仅靠一个人的力量是完成不了的,那就必须把工作分配下去,然后将代码合并,所以git一开始设计的时候就是一种分布式的、多分支的

概念

所以git最重要的就是分支这个性质,分支是什么呢.

要了解分支必须要了解git工作原理.

git工作原理很简单就是addcommitaddcommit….,简单来说就是添加记录,添加记录,保存快照,添加记录,添加记录,保存快照

git工作分支流程-来源网上

如上图,随着master分支快照的一个一个建立,软件就慢慢的迭代下去了

分支工作流程

接下来我们要着重讲一下分支,我们看到master分支的v0.1版本,我们已经开发出稳定的v0.1版,这时候我们决定开发一个新功能.

在这里我们分了一个Develop分支,我们在Develop分支开发新代码.

这时候我们发现v0,1的一个bug,假如是没有使用版本控制的话,一般人会停下手中的活,然后从当前的新代码处来修复这个bug,当这个bug很简单的时候,我们不会遇到很大困难,但是当bug藏的很深,而且新代码隐藏了这个bug,或者被这个bug影响,这时修复工作就变得很困难.

还好我们有git,我们从v0.1直接分一个Hotfix分支,这两个分支的父都是v0.1,我们直接从稳定版本修复,不牵涉到新代码,这样修改好后我们就能很快从Develop分支继续工作了

而且这样有一个好处我们将masterDevelop分支合并的时候很大可能不会产生冲突.

冲突(coflic)是什么了,怎么能避免呢?

从两个分支的父亲v0.1看起,我们每次改动一个保存文件就会产生一个modify(修改)的动作,我们假如分支里面都对同一个文件产生了modify(修改)动作,当我们合并的时候这就是一个冲突,git无法理解采用哪个分支的modify动作,这时候就要你人工来修改采用哪个分支.

假如没有相同的文件有modify(修改)动作,git就会聪明的知道采用每个分支的最新的modify(修改)修改出一份所以文件的最新版.

那我们怎么来避免这个冲突呢,这就要求我们分支要分的合理,分支只要完成特定的工作,不要越俎代庖,那有些人会说我这个分支一定要改父的耦合地方否则我的代码工作不了,这时我们要好好思考自己的分支的功能,把合并耦合的代码放在主分支里面,次分支只要完成特定功能就可以了,这样合并分支时候不但可以安全的merge(合并)了, 而且修改bug的时候也可以对症下药,直接在问题开始的地方修改.

在牛客网刷了16道题了,在这做个总结

概况

编程题无非两种一种考算法,一种考数据结构

算法的话,考验你对事情的分析程度和脑袋的灵光,用好的算法还是又大又重的算法,用算法复杂度来看,一般能到 o(n)就算勉强可以,当到了o(n * n)你就要考虑是不是你算法有问题了.

数据结构的话,队列和链表和二叉树是比较常见的,当然有些奇怪的一般算法反倒很简单.

算法

谈算法的话不得不谈递归了,递归其实我感觉更想一种思想

话不多说请看题,斐波那契数列一直是递归的代表

                      0                 n= 1    
f(n) ={           1                   n= 2
            f(n-1) + f(n-2)     n>2

虽然

因为这段时间待在家没什么事干,虽然完成一个小的项目,也还有很多功能需要完善,但是说实话还是有点迷茫,明年就大三了,接下来有两条路给我走,要么是考研,要么是出去工作.
自己算是个半个程序员吧,web开发那一套前后端撸起袖子都能搞出点名堂来,但是为什么说我是半个程序员呢,因为现在我还不知道我是爱他还是不爱他呢.

兴趣这东西是培养来的,谁也不是天生就死心塌地的爱上某个东西.

现在学习新东西的成本太低了,这两个月来我也尝试过很多以前没有接触的东西,Android,Deskapp,用了几个框架做了几个apk和软件,虽然用前端栈实现的东西但是所以的语言都差不多.

当然项目基本功能都很单一,当然复杂的也就是简单的堆积而成.

完成之后反倒更迷茫了,为什么了,互联网技术太好学了,无论你的文化程度有多低,按照demo依葫芦画瓢搞几下也能像模像样,我们作为一个文化程度比较高的一群人,或许花半天就能比别人一天能掌握的东西.

但是人家索要的报酬却比你的四分之一还要低,你拿什么跟人家竞争

我有一些志同道合的同学虽然像我一样学了几年web这一块,但是他们决定考研,web谁都能做,不用读大学就能做,他们想多读点出来改造世界,也可以说让世界更美好吧.

其实我对考研的态度是消极的,我现在嘛并不准备投身科研这方面,在美国其实大学都是少部分人才能上,比起我们现在博士硕士满天飞,在美国其实只有真正喜欢科研的人才会去读大学.

其实我写的时候很纠结,该怎么写才不跑题……

我其实想总结的是注重基础,但是我脑海里面出现无数个论点,好像回到了高中时代写议论文了, 想把主人公塑造成一个焕然大悟顿悟人生的形象…..

以上是个小插曲跳过跳过

现在开始正式总结了,越短越好.

总结

七八两个月自己还是很急躁的两个月,一方面在于选择技术栈,一方面在于选择出路.

虽然没有想通,但是在想的过程中发现自己好焦躁…….

总是想一秒钟决定自己未来N年的时,有点像人生规划一样

虽然说搞个人生规划很好,但是发现真的是白费力气,因为无论那个伟人,谁也不知道自己将来会干什么,奥巴马在大学客堂睡觉的时候也不知道自己将领导美国.

焦躁点找到了,接下来就静下来想了怎么解决了,既然无法预测未来那就好好准备未来吧.

怎么准备呢?做你不屑做的事,并坚持下去.

你们不要想歪了,不屑做的事是指那些你看起来没什么卵用的建议.

比如说每天背几个单词,每天看一点英文原著,每天刷一刷编程题

在这我要提一句,虽然很多人从高中过来有点嫌弃刷题了,但是我们是大学生不能用高中生的观点去看待问题,高中的刷题是为了分数,大学的刷题是为了醒脑

你看不起一天在leetcode刷几道题,那是因为你刷不动,不愿刷,以前刷题刷到你一眼就能看出答案,现在我们刷题要每刷一遍都要有新的感受,让大脑活跃起来.

而且当年把运动刷题背单词看书写博客当成副本规定题量字数的时候,每次完成你的任务都会很爽,一方面脑子变得活跃了,另一方面你有种马上就升级的感觉.

每天都能发现自己的进步这是你前进的动力,虽然我们无法预测未来,好好准备,当未来真的来的时候,不会慌.

私下插个话,现在app泛滥,你刷单词完全可以搞个扇贝,刷题搞个牛客或leetcode,健身搞个keep,虽然我没收他们的广告费,但是这些东西给我的反馈比我自己搞个小本记着好多了.不多说了与君共勉,希望大家都能沉下焦躁的心,一起慢慢”升级”.

翻译自TDD-byexample
作者Kent Beck, Three Rivers Institute
有删减

表现

测试驱动开发核心:

  1. 除非你有失败的自动化测试千万不要写一行新代码
  2. 拒绝重复

这两个的简单原则构成了TDD的核心,但是他能规划一个复杂的项目乃至一个团队.这里有一些TDD的建议.

  • 你的项目设计不能太过全面,只要有一个模型或者相应的功能,然后你让你的测试代码测试你模型,通过反馈来完善你设计.
  • 你必须自己写测试代码,你不能依靠别人来每天帮你修改无数次测试
  • 你的开发环境必须能监控到代码的微小的变化
  • 你测试代码必须要非常简单,复杂的测试代码说明你的程序有问题

根据核心我们总结了一种具体的测试方法:红绿重构法

红 --- 写一段测试代码让他无法通过,有时候可以编译都通过不了
绿 --- 写尽可能少的代码让测试代码通过,通过后保存一下系统状态
重构 ----删掉所以重复的代码只要让测试代码还能通过

红绿重构法是TDD最高作战计划,他看起来很笼统,其实他具体到了每一行代码.

如果我们按照TDD这种开发模式,我们会有什么好处呢

  • 如果我们新功能出了问题,质检部门能很快的将新代码回归到上一个稳定代码,尽快避免损失
  • 如果代码的测试能将”惊喜”挖的差不多的话,项目经理能更准确的知道客户在使用过程中碰到的各种奇葩问题
  • 如果我们的测试能够让各种技术交流变得更加清晰,我们能更快的进行技术排错
  • 如果我们项目的bug更少的话,我们的项目能变得更加灵活,我们可以很轻松的添加新功能呈现给我们顾客

这些概念看起来很简单,但是我们的动机呢?为什么我要在写代码过程添加自动测试?为什么我要每次迈小小的一步在我能够做到更多的情况下?两个字:担忧

担忧

测试驱动开发是一种管理你担忧的一种编程方式,担忧也不是说就是坏东西,自信过头也不好,但是恐惧给你一种在项目开头时,”我看不到这个项目的能够完成”,这种感觉,

假如说痛给你一个”停下来”的信号,担忧给你一个”要认真”的提醒信号,但是与之而来的,担忧带来一些你消极的影响

  • 让你变得迟疑
  • 让你开始抱怨
  • 让你开始不愿交流
  • 让你开始不想接受反馈

这些没有一个对你编程有帮助,尤其是当你在编一个比较复杂的软件的时候,所以你怎么来面对这些呢

  • 抛弃迟疑,学习快速有效的编程
  • 不要拖沓,跟别人交流思路要清晰
  • 不要拒绝反馈,去寻找更多有帮助的反馈

想象一下编程就是你提桶着水过河,当你水桶很小的时候,你轻微的震动没什么影响,但是当你的水桶很大,而且水很满的时候,你会很累,你无时无刻不在担心你的水是否会撒掉.

这个时候你需要一个运水的管道,每当你用水桶打一点水,你可以把他放进管道,确保这点水安全到达对面,继续打水.

这个测试驱动开发中的测试就是运水的管道,一旦你的测试通过,你就知道水已经送过去了,不需要担心水到不到对岸了,你就这样一步一步让所以的工作正常进行,但你测试失败的时候,专注于让他通过,这样下一个下一个,慢慢的我们接触到了编程难题,你的测试慢慢覆盖到整个项目.

TDD给你一种控制的能力.当年在外面开车碰到下雪,你可以迅速停车去做其他琐事,当雪停了,你可以继续开车.

所以很多人说他们使用TDD对于项目的变化更有控制力.

接下来我会用一个例子来详细的介绍TDD开发的流程.

由于原作者是用java来介绍的,本人用Python较多,所以就用自己写的一个项目sample来做介绍,详细链接
接下来翻译一下书后面关于TDD的一些答疑

TDD答疑

我希望在这里提出一些或大或小问题帮助你思考如何将TDD引入到你的个人实践里面

你的测试步伐到底该多大?

这里有两个引申过来的问题

  • 每个测试覆盖范围该多大
  • 当你重构时到底有要迈多大步伐

你的测试可以覆盖到你写的每一行代码和你每下一构,你的测试也可以覆盖你上百行代码和你几个小时后的重构,但是哪个才是我们该写的测试呢.

从某方面来看,你应该要能做到其中一个测试,虽然TDD宗旨就是每一步都非常清晰,每一步伐都要求非常小,但是我们对软件驱动开发的经验会使这个步伐或多或少产生影响.

当你开始重构的时候,你应该准备好将一个重构分成很多小的步伐.重构很有可能会发生错误,你把坑都踩一遍然后填上,然后你接下来重构的可能性就会小很多.一旦你完成每次用多个小步伐的一个重构,你可以试试遗漏一些步骤.

你需要多少反馈

其实写这个项目的初衷是想实践一下TDD开发,因为自己刚看完一本<<Test-Driven Development with Python>>,以前只是了解一点开发测试,看完这本书感觉这种敏捷开发方式非常适合我,自己完整写过一些小项目,但是大项目经常由于各种代码框架,代码规模搞得最后成了烂项目,而且关于TTD一些建议比如YAGNI(You ain't gonna need it)(你不需要这个)对于你对项目的规模有一定的控制.写这篇博客一方面将我开发hookmanTDD开发经历告诉大家,一方面希望更多人了解TDD开发,换一种开发方式,或许能让你找回编程的乐趣.

##引言

当然网上有些人对TDD测试开发嗤之以鼻,认为开发过程中不应该由测试驱动,应该先把所有的核心都先完善,最后在来完成项目.

在这里我想说一点我的看法,测试驱动是一种从核心到细节的开发方式.

用画一个人来打比方,给你一张白纸,TDD要求你先画一个躯干,这个首先要有个人样,当我们想画一个奔跑的人时,TDD要求你得给他再画两条裸腿,然后这两条腿怎么摆才能控制平衡,这两条腿穿什么鞋才比较生动,要接下来你慢慢测试考虑.

而我们传统的开发方式,是先从局部到整体,这种开发方式适合于小项目,而且像一个流水线出来的产物,比如说web开发,我们知道下一个要做的是视图层,然后模型层.但是这种开发方式不适合开发比如说一个新的软件,假如我们用传统开发方式,除非leader的掌控力非常好,一个越来越臃肿的项目很难坚持到最后,因为我们在项目完成的时候才能让项目真正的运行起来,在完成的过程中我们很容易迷失最后将项目烂掉.

而且我比较欣赏TDD开发的一个主要原因就是他同linux推崇的那种简单的软件开发文化很默契,我们在让我们测试通过时候,我们感觉在:欺骗自己”.

举个例子:

比如我有一个比较两个值谁大谁小的函数

def my_max(a, b):
    pass

我们写一个单元测试

Mytest(unittest.TestCase):
       test_my_max(self):
           m = my_max(1,2)
           self.aseertEqual(m, 2)

我们运行这个测试肯定失败,然后我们开始”欺骗自己”让测试通过,修改my_max

def my_max(a, b):
    return b

再运行测试,通过了,ok,”欺骗成功”,但我们用脑袋一想就知道,不行这个代码不对,但是咋办”测试通过了”,修改测试.

Mytest(unittest.TestCase):
        test_my_max(self):
            m = my_max(1,2)
            self.assertEqual(m, 2)
            n = my_max(2, 1)
            self.assertEqual(n, 2)

这下测试又通不过了,我们想要的功能就在一步一步测试拖动中慢慢实现,我们尽量用最简单的代码通过我们的测试.

看到这里有些人会认为我很傻,明明可以用两行代码(不能用系统函数max)

def my_max(a, b):
    if a > b: return a
    return b

轻轻松松通过测试,但是这反倒是TDD非常不推崇的,你一次走了太多步,你让你的代码跑到Test前面去了,其实很多程序员都会写测试,但是他们的测试很多都是基于代码的,当一个大功能实现的时候才开始测试,这个时候你会发现很多你的隐藏bug就藏在你一大堆代码里面,所以有些人为了排bug一行一行删代码来debug.

诚然很多时候我们感觉我们能毫无bug的完成一大段的代码,但是我们不能保证100%正确,而且或许今天能行明天就不行了,TDD推崇测试山羊精神,想象一只山羊行走在陡崖峭壁上,他只能走一步停一步,我们写代码也一样,我们前面是陡崖峭壁,我们不能保证我们下一步就不掉坑里,所以我们要学习山羊精神一步一步.

接下来关于TDD开发一些详细经历,我会每天抽出一点时候写.

hookman 是基于github上的webhooks开发的一个用Python写的小程序
,基于TDD开发.
如果你想向项目贡献代码,请看hookman 开发blog

简单用法

  1. 安装 pip install hookman
  2. 配置github的webhooks,设置监听服务器ip和端口3610(默认)
  3. 使用
    3.1 进入github项目目录cd /my/github/projectdir
    3.2 运行 hookman --run -d
  4. 关闭
    3.1 hookman --stop

版本差异

v0.1.0

  1. 基本实现监听端口
  2. 添加 run,stop,daemon,pidfile,logfile,projectdir选项

v0.1.1

  • 修复了在前台shell模式下的bug

隔离测试是相对与于整合测试来说的,现代软件架构流行分层式、模块化,而隔离测试就是相当于在每层上进行测试,整合测试就是跨越多个层进行测试

introduction

举个简单例子来说,在django系列中,我们把表单提交分成两个层,一个form层,一个式model

form层相当于接近用户交互层,而model层与数据库联系更大, form层负责获取用户数据并验证,而model层根据form层数据将数据存入数据库。

隔离测试就是隔离form层向model层提交,而整合测试就是直接测试form层和model层。

判断一个测试是整合测试还是隔离测试就是看测试的边界,整合测试相当于我们更加熟悉,我们测试时通过伪造form提交,然后通过数据库获取存入数据来得到验证,而隔离测试就比较复杂,因为我们很难在一个耦合度高代码找到怎么隔离两个层的方法。

接下来我就介绍python里一个神器:mock

自己用过其他语言框架中的mock,但是python里面mock里面最神奇的的是里面的patch,就像一个超级补丁一样。

Example

接下来我用一个例子来介绍一下如何写隔离测试

首先我们在 lists.forms.py中一个表单model

class ListForm(forms.models.ModelForm):
    def save(self):
        pass

我们在lists.models.py有个model

class List(models.Model):
    def create_now():
        pass

现在来分析一下这个隔离测试,我们要测试ListFormsave方法

首先ListFormList两个类是耦合的,一个整合测试,我们只要调用save方法,然后查询数据库就可以完成这个测试,然而隔离测试不同,我们只能测试在调用save方法时,他“干”了什么。他可能调用了Listcreate_now方法,将得到表单数据传了过去。

意味着我们只能测试到save方法调用了List方法。

那这个隔离单元测试该怎么写?

接下来我们隆重介绍mock里面的最强补丁patch

根据TDD原则,我们先新建一个单元测试IsolutionFormTest

import unittest
from unittest.mock import patch, Mock      ##load super patch 
from lists.models import List
from lists.forms import ListForm


class IsolutionFormTest(unitest.TestCase):

       @patch('lists.forms.List.create_new')
       def test_save_creates_new_list(self, mock_list_create_new):
            form = ListForm(data={'text': "example text"})
            form.is_valid()    # get clean data
            form.save()

            mock_list_create_new.assert_called_once_with(
                text= "example text"
            )   # the major test

我来介绍一下这个patch,就像名字一样补丁,通过我们使用字符串将要替换的函数写出来,当test运行时,会自动将函数替换成一个mock对象,通过参数(上面的mock_list_create_new)赋给函数。

你可以这样想象,当form.save()调用时,在save函数里面,我们如果使用了lists.forms.List.create_new这个函数,这个函数就会被直接被补丁替换掉,你如果使用了lists.forms.List.create_new(text="xxx")就会变成mock_list_create_new(text="xxx"),当你调用了mock_list_create_new(text="xxx")时,mock_list_create_new这个mock对象就会记录下来。

这样我们就通过mock_list_create_new测试了函数是否执行了没有,因为隔离开form层和model层的就是通过两者之间的接口。我们只用测试接口是否执行了没有就可以了。

这样我们就完成了隔离单元测试,运行一下肯定失败,我们接下来就把save方法完善一下通过测试。

from django import form
form list.models import List

class ListForm(forms.models.ModelForm):
    def save(self):
        List.create_new(text=self.cleaned_data['text'])

ok测试通过了,我们就可以歇一口气了。

Conclusion

相对于整合测试,隔离测试运行速度更快,但是相对的隔离测试对接口要求非常严格,好的方面利用我们进行更好的代码设计,更好的分析代码的复杂程度,并且当接口变迁的时候,隔离测试能迅速发现变化而报警,然而隔离测试工作量比较大,而且没有整合测试那么好理解。在实际生产中对于复杂的接口我们尽量进行隔离测试,对于简单接口我们使用整合测试能根据减少程序的耦合性,而且能迅速发现集成问题。

ps:对patch感兴趣的童鞋可以自行google,patch好玩的地方还有很多,这里为了篇幅我只介绍了最核心的使用方法,大家可以自行探索patch更多好玩的东西。s

看了 << Python Web 开发 测试驱动方法>> 以后, 感觉自己找到自己的一些项目之所以不能够很好的 维护下去的原因.总结了一下自己的理解, 记录下来.

什么是 TDD

Test-Driven Development

TDD 是一种以测试为驱动开发的方法, 自己以前也听到过这个名词, 但是平常只是稍微写一点测试,并没有让测试领导开发节奏.

简单来说TDD是先写测试再写代码, 这于我们平时开发的时候有很大不同,比如我们写一个web页面, 我们一般是先写好代码,然后在浏览器上调试(相当于测试),然而测试驱动开发就不同了,我们一开始只写测试, 然后写代码让他通过我们的测试.

TDD的主题基本上很多人都知道,但是具体步骤和诀窍很多人不明就里.下面来详细介绍怎么让Test
引导你的开发.

怎么进行TDD

首先让我来看一看Test是什么, Test 分两种

  1. 功能测试
  2. 单元测试

功能测试

很多人知道单元测试却很少听到功能测试, 功能测试在开发过程中经常被人忽略,但是功能测试能很好的把握开发方向

功能测试就是从用户角度出发, 从用户的角度测试代码.

用web项目开发来打比方, 用户只能通过浏览器来浏览你的web, 所有的交互只能通过浏览器来实现,功能测试就是模拟用户进行浏览器上的操作,具体来说我们可用使用selenium来操控firefoxchrome,通过我们使用selenium提供的接口操作浏览器访问页面获取html, 来进行功能测试.

功能测试就是测试一个项目成品的功能, 在平常的项目开发中,这个测试往往是提到项目完成之后人工进行, 然而在TDD中这个却被提到了最前面,他成了一个风向标,所有的代码目的都是为了实现这个功能测试.

单元测试

单元测试这个很多人都很了解, python里面有unittest 这个单元测试框架.就是专门为单元测试而开发的.

单元测试历史悠久,很多人都写过单元测试,不过大部分人写单元测试都是在函数或者类完成之后写的.

在TDD中,单元测试进行最频繁的测试, 在功能测试完, 写每一个函数都提倡先写单元测试, 然后进行开发.

单元测试关注点与功能测试不同, 单元测试注重的是每一个函数执行的结果, 给定一个输入就一定要得到一个确定的输出,比功能测试他更关注底层代码,毕竟功能测试只关注用户最后得到结果,单元测试将你的代码函数形成一个单元,逐个运行,逐个测试.

前面介绍了一大堆概念,却没有落到实处, 功能测试很简单,我们评价一个项目,能很快的写出测试方法,但是对于单元测试来说,我到底该怎么进行,这同我平时的开发有什么不同,下面就详细的介绍单元测试的几个重要的要点.


留个坑慢慢填.

最近这几天在开发一个hmtl5的游戏, 但是对于js怎么使用面对对象来编程有点困惑,查了一些资料
整理如下.

js的this用法

  • 非对象属性函数(内部函数)

    var point = { 
     x : 0, 
     y : 0, 
    moveTo : function(x, y) { 
        // 内部函数
        var moveX = function(x) { 
    this.x = x;//this 绑定到了哪里?
        }; 
          // 内部函数
        var moveY = function(y) { 
            this.y = y;//this 绑定到了哪里?
         }; 
    
         moveX(x); 
       moveY(y); 
     } 
    }; 
    point.moveTo(1, 1); 
     point.x; //==>0 
     point.y; //==>0 
     x; //==>1 
    y; //==>1
    
  • 对象属性函数
var point = { 
        x : 0, 
        y : 0, 
        moveTo : function(x, y) { 
                this.x = this.x + x; 
                this.y = this.y + y; 
                             } 
         };

 point.x; // ==> 1
 point.y; // == >1 

 point.moveTo(1, 1)//this 绑定到当前对象,即 point 对象
 point.x; // ==> 1
 point.y; // == >1 

其实this的用法还有很多,我为什么只列出上面两种是因为所有的this用法都可以归为这两类.

非对象属性就是这个函数不存在对象的内存空间里面的函数
对象属性就是说这个函数存在于对象内存空间的函数

这样说很绕, 抽象的来说, 把每个函数都看做是对象, 我们把这个对象point想象成一个上锁的柜子A, 每个变量名就是一把钥匙, 我们这个柜子A里面可用放很多钥匙,每个钥匙又对应着其他柜子,我们也可以放东西,但是这些东西只能是一些数字字符串什么的,这就对应着对象的这种继承性,

现在我们来看放在柜子A里面的钥匙,有一个钥匙可用开其他某一个的柜子B(相当于对象的属性的moveTo函数),当我们就在柜子B里面使用this时,这个this是什么呢.

这个this就要找一个柜子, 那为什么要找一个柜子(对象呢)

这就是js语言设计的松散, 当在非严格模式下, this 会被强制转换成一个对象, 对于例子一,因为内部函数 this 并没有给他赋值(你可以把他看做一个我们找不到他的钥匙的柜子), 所以this被强制转换成了全局的柜子(全局变量)

ps: 严格模式在函数或变量前加上 'use strict';

怎么解决这个问题了, 有两种方法, 不是没有赋值给this嘛, 我们可用call来给函数内部的this赋值,

moveX.call(this, x);   // 我们把当前函数的this赋给内部函数

但是这种方法不够优美, 我们还可以直接把 this用个变量that给内部函数用,

var that = this;
var moveY = function(y) { 
        that.y = y;//this 
 }; 

这样这个this 就不会被强制转换成全局变量了, 当然你可以开启严格模式, 这样this的话就会变成undefined, 你也不会因为你的代码问题而污染全部变量.

总结

js 的 this 是面对对象编程的一种体现, 但是js的this由于有点不严格,所以有时候会出现一些令人意向不到的结果,

引用