Javascript的this用法

Javascript的this用法

转载-阮一峰的网络日志

  • this是Javascript语言的一个关键字。

它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。比如,

1
2
3
  function test(){
    this.x = 1;
  }

随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。
下面分四种情况,详细讨论this的用法。

情况一:纯粹的函数调用

这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global。
请看下面这段代码,它的运行结果是1。

1
2
3
4
5
  function test(){
    this.x = 1;
    alert(this.x);
  }
  test(); // 1

为了证明this就是全局对象,我对代码做一些改变:

1
2
3
4
5
  var x = 1;
  function test(){
    alert(this.x);
  }
  test(); // 1

运行结果还是1。再变一下:

1
2
3
4
5
6
  var x = 1;
  function test(){
    this.x = 0;
  }
  test();
  alert(x); //0

情况二:作为对象方法的调用

函数还可以作为某个对象的方法调用,这时this就指这个上级对象。

1
2
3
4
5
6
7
  function test(){
    alert(this.x);
  }
  var o = {};
  o.x = 1;
  o.m = test;
  o.m(); // 1

情况三 作为构造函数调用

所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。

1
2
3
4
5
 function test(){
    this.x = 1;
  }
  var o = new test();
  alert(o.x); // 1

运行结果为1。为了表明这时this不是全局对象,我对代码做一些改变:

1
2
3
4
5
6
  var x = 2;
  function test(){
    this.x = 1;
  }
  var o = new test();
  alert(x); //2

运行结果为2,表明全局变量x的值根本没变。

情况四 apply调用

apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。

1
2
3
4
5
6
7
8
  var x = 0;
  function test(){
    alert(this.x);
  }
  var o={};
  o.x = 1;
  o.m = test;
  o.m.apply(); //0

apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。
如果把最后一行代码修改为

1
  o.m.apply(o); //1

运行结果就变成了1,证明了这时this代表的是对象o。

单元测试框架与mock介绍*转

原文地址

单元测试框架的优点与一些问题

在单元测试中引入单测框架使得编写单测用例时,不需要再关注于如何驱动case的执行,如何收集结果,如何管理case集,只需要关注于如何写好单个测试用例即可;同时,在一些测试框架中通过提供丰富的断言集,公用方法,以及运行参数使得编写单个testcase的过程得到了最大的简化。

那这其中会存在什么样的疑问了?

我在单元测试框架中写一个TestCase,与我单独写一个cpp文件在main()方法里写测试代码有什么本质却别吗?用了单元测试框架,并没有解决我在对复杂系统做单测时遇到的问题。

没错,对于单个case这两者从本质上说是没有区别的。单元测试框架本身并没有告诉你如何去写TestCase,在这一点上他是没有提供任何帮助的。所以对于一些复杂的场景,只用单元测试框架是有点多少显得无能为力的。

使用单元测试框架往往适用于以下场景的测试:单个函数,一个class,或者几个功能相关class的测试,对于纯函数测试,接口级别的测试尤其适用,如房贷计算器公式的测试。

但是,对于一些复杂场景:
 被测对象依赖复杂,甚至无法简单new出这个对象
 对于一些failure场景的测试
 被测对象中涉及多线程合作
 被测对象通过消息与外界交互的场景
 …

单纯依赖单测框架是无法实现单元测试的,而从某种意义上来说,这些场景反而是测试中的重点。

以分布式系统的测试为例,class 与 function级别的单元测试对整个系统的帮助不大,当然,这种单元测试对单个程序的质量有帮助;分布式系统测试的要点是测试进程间的交互:一个进程收到客户请求,该如何处理,然后转发给其他进程;收到响应之后,又修改并应答客户;同时分布式系统测试中通常更关注一些异常路径的测试,这些场景才是测试中的重点,也是难点所在。

Mock方法的引入通常能帮助我们解决以上场景中遇到的难题。

Mock的引入带来了什么

在维基百科上这样描述Mock:In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A computer programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior. of a human in vehicle impacts.

Mock通常是指,在测试一个对象A时,我们构造一些假的对象来模拟与A之间的交互,而这些Mock对象的行为是我们事先设定且符合预期。通过这些Mock对象来测试A在正常逻辑,异常逻辑或压力情况下工作是否正常。

引入Mock最大的优势在于:Mock的行为固定,它确保当你访问该Mock的某个方法时总是能够获得一个没有任何逻辑的直接就返回的预期结果。

Mock Object的使用通常会带来以下一些好处:

 隔绝其他模块出错引起本模块的测试错误。
 隔绝其他模块的开发状态,只要定义好接口,不用管他们开发有没有完成。
 一些速度较慢的操作,可以用Mock Object代替,快速返回。
对于分布式系统的测试,使用Mock Object会有另外两项很重要的收益:
 通过Mock Object可以将一些分布式测试转化为本地的测试
 将Mock用于压力测试,可以解决测试集群无法模拟线上集群大规模下的压力

Mock的应用场景

在使用Mock的过程中,发现Mock是有一些通用性的,对于一些应用场景,是非常适合使用Mock的:

真实对象具有不可确定的行为(产生不可预测的结果,如股票的行情)
 真实对象很难被创建(比如具体的web容器)
 真实对象的某些行为很难触发(比如网络错误)
 真实情况令程序的运行速度很慢
 真实对象有用户界面
 测试需要询问真实对象它是如何被调用的(比如测试可能需要验证某个回调函数是否被调用了)
 真实对象实际上并不存在(当需要和其他开发小组,或者新的硬件系统打交道的时候,这是一个普遍的问题)
当然,也有一些不得不Mock的场景:
 一些比较难构造的Object:这类Object通常有很多依赖,在单元测试中构造出这样类通常花费的成本太大。
 执行操作的时间较长Object:有一些Object的操作费时,而被测对象依赖于这一个操作的执行结果,例如大文件写操作,数据的更新等等,出于测试的需求,通常将这类操作进行Mock。
 异常逻辑:一些异常的逻辑往往在正常测试中是很难触发的,通过Mock可以人为的控制触发异常逻辑。

在一些压力测试的场景下,也不得不使用Mock,例如在分布式系统测试中,通常需要测试一些单点(如namenode,jobtracker)在压力场景下的工作是否正常。而通常测试集群在正常逻辑下无法提供足够的压力(主要原因是受限于机器数量),这时候就需要应用Mock去满足。

在这些场景下,我们应该如何去做Mock的工作了,一些现有的Mock工具可以帮助我们进行Mock工作。

Mock工具的介绍

手动的构造 Mock 对象通常带来额外的编码量,而且这些为创建 Mock 对象而编写的代码很有可能引入错误。

目前,有许多开源项目对动态构建 Mock 对象提供了支持,这些项目能够根据现有的接口或类动态生成,这样不仅能避免额外的编码工作,同时也降低了引入错误的可能。

C++: GoogleMock http://code.google.com/p/googlemock/
Java: EasyMock http://easymock.org/

通常Mock工具通过简单的方法对于给定的接口生成 Mock 对象的类库。它提供对接口的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。通过这些Mock工具我们可以方便的构造 Mock 对象从而使单元测试顺利进行,能够应用于更加复杂的测试场景。

以EasyMock为例,通过 EasyMock,我们可以为指定的接口动态的创建 Mock 对象,并利用 Mock 对象来模拟协同模块,从而使单元测试顺利进行。这个过程大致可以划分为以下几个步骤:

 使用 EasyMock 生成 Mock 对象
 设定 Mock 对象的预期行为和输出
 将 Mock 对象切换到 Replay 状态
 调用 Mock 对象方法进行单元测试
 对 Mock 对象的行为进行验证

EasyMock的使用和原理: http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/

EasyMock 后台处理的主要原理是利用 java.lang.reflect.Proxy 为指定的接口创建一个动态代理,这个动态代理,就是我们在编码中用到的 Mock 对象。EasyMock 还为这个动态代理提供了一个 InvocationHandler 接口的实现,这个实现类的主要功能就是将动态代理的预期行为记录在某个映射表中和在实际调用时从这个映射表中取出预期输出。

借助类似于EasyMock这样工具,大大降低了编写Mock对象的成本,通常来说Mock工具依赖于单元测试框架,为用户编写TestCase提供便利,但是本身依赖于单元测试框架去驱动,管理case,以及收集测试结果。例如EasyMock依赖于JUint,GoogleMock依赖于Gtest。

那么有了单元测试框架和相应的Mock工具就万事俱备了,还有什么样的问题?正如单元测试框架没有告诉你如何写TestCase一样,Mock工具也没有告诉你如何去选择Mock的点。

如何选择恰当的mock点

对于Mock这里存在两个误区,1.是Mock的对象越多越好;2.Mock会引入巨大的工作量,通常得不偿失。这都是源于不恰当的Mock点的选取。

这里说的如何选择恰当的mock点,是说对于一个被测对象,我们应当在外围选择恰当的mock对象,以及需要mock的接口。因为对于任意一个对象,任意一段代码逻辑我们都是有办法进行Mock的,而Mock点选择直接决定了我们Mock的工作量以及测试效果。从另外一种意义上来说,不恰当Mock选择反而会对我们的测试产生误导,从而在后期的集成和系统测试中引入更多的问题。

在mock点的选择过程中,以下的一些点会是一些不错的选择

 网络交互:如果两个被测模块之间是通过网络进行交互的,那么对于网络交互进行Mock通常是比较合适的,如RPC
 外部资源:比如文件系统、数据源,如果被测对象对此类外部资源依赖性非常强,而其行为的不可预测性很可能导致测试的随机失败,此类的外部资源也适合进行Mock。
 UI:因为UI很多时候都是用户行为触发事件,系统本身只是对这些触发事件进行相应,对这类UI做Mock,往往能够实现很好的收益,很多基于关键字驱动的框架都是基于UI进行Mock的
 第三方API:当接口属于使用者,通过Mock该接口来确定测试使用者与接口的交互。

当然如何做Mock一定是与被系统的特性精密关联的,一些强制性的约束和规范是不合适的。这里介绍几个做的比较好的mock的例子。

  1. 杀毒软件更新部署模块的Mock

这个例子源于一款杀毒产品的更新部署模块的测试。对于一个杀毒软件客户端而言,需要通过更新检查模块与病毒库Server进行交互,如果发现病毒库有更新则触发病毒库部署模块的最新病毒库的数据请求和部署工作,要求部署完成后杀毒软件客户端能够正常工作。

对于这一场景的测试,当时受限于这样一个条件,通常的病毒库server通常最多一天只更新一次病毒库,也就是说如果使用真实的病毒库server,那么针对更新部署模块的测试一天只能被触发一次。这是测试中所不能容忍的,通过对病毒库server进行mock可以解决这个问题。

对于这个场景可以采取这样一种Mock方式:用一个本地文件夹来模拟病毒库server,选择更新部署模块与病毒库server之间交互的两个函数checkVersion(),reqData()函数进行Mock。

checkVersion()工作原先的工作是检查病毒库Server的版本号,以决定是否触发更新,将其行为Mock为检查一个本地文件夹中病毒库的版本号;reqData()原有的行为是从病毒库Server拖取病毒库文件,将其Mock为从本地文件夹中拖取病毒库文件。通过这种方式我们用一个本地文件夹Mock病毒库Server的行为,其带来的产出是:我们可以随意的触发病毒库更新操作以及各种异常。

通过这种方式发现了一个在更新部署过程中,病毒库Server的病毒库版本发生改变造成出错的严重bug,这个是在原有一天才触发一次更新操作的情况下永远也无法发现的。

  1. 分布式系统中对NameNode模块的测试

在测试NameNode模块的过程中存在这样一个问题,在正常逻辑无压力条件下NameNode模块都是工作正常的。但是线上集群在大压力的情况下,是有可能触发NameNode的问题的。但是原有的测试方法下,我们是无法对NameNode模拟大压力的场景的(因为NameNode的压力主要来源于DateNode数量,而我们测试集群是远远无法达到线上几千台机器的规模的),而NameNode单点的性能瓶颈问题恰恰是测试的重点,真实的DataNode是无法满足测试需求的,我们必须对DataNode进行Mock。

如何对DateNode进行Mock了,最直观的想法是选择NameNode与DataNode之间的交互接口进行Mock,也就是他们之间的RPC交互,但是由于NameNode与DataNode之间的交互信息种类很多,所以其实这并不是一种很好的选择。
换个角度来想,NameNode之上的压力是源于对HDFS的读写操作造成的NameNode上元数据的维护,也就是说,对于NameNode而言,其实他并不关心数据到底写到哪里去了,只关心数据是否读写成功。如果是这种场景Mock就可以变的简单了,我们可以直接将DataNode上对块的操作进行mock,比如,对一次写请求,DataNode并不触发真实的写操作,而直接返回成功。通过这种方式,DataNode去除了执行功能,只保留了消息交互功能,间接的实现了我们的测试需求,且工作量比之第一种方案小很多。

12种不宜使用的Javascript语法

12种不宜使用的Javascript语法

转-阮一峰的网络日志

《Javascript语言精粹》的附录B中,Douglas Crockford列出了12种应该避免使用的Javascript语法,非常值得推广。

  1. ==
    Javascript有两组相等运算符,一组是==和!=,另一组是===和!==。前者只比较值的相等,后者除了值以外,还比较类型是否相同。
    请尽量不要使用前一组,永远只使用===和!==。因为==默认会进行类型转换,规则十分难记。如果你不相信的话,请回答下面五个判断式的值是true还是false:
      false == ‘false’
      false == undefined
      false == null
      null == undefined
      0 == ‘’
    前三个是false,后两个是true。
  2. with
    with的本意是减少键盘输入。比如
      obj.a = obj.b;
      obj.c = obj.d;
    可以简写成
      with(obj) {
        a = b;
        c = d;
      }
    但是,在实际运行时,解释器会首先判断obj.b和obj.d是否存在,如果不存在的话,再判断全局变量b和d是否存在。这样就导致了低效率,而且可能会导致意外,因此最好不要使用with语句。
  3. eval
    eval用来直接执行一个字符串。这条语句也是不应该使用的,因为它有性能和安全性的问题,并且使得代码更难阅读。
    eval能够做到的事情,不用它也能做到。比如
      eval(“myValue = myObject.” + myKey + “;”);
    可以直接写成
      myValue = myObject[myKey];
    至于ajax操作返回的json字符串,可以使用官方网站提供的解析器json_parse.js运行。
  4. continue
    这条命令的作用是返回到循环的头部,但是循环本来就会返回到头部。所以通过适当的构造,完全可以避免使用这条命令,使得效率得到改善。
  5. switch 贯穿
    switch结构中的case语句,默认是顺序执行,除非遇到break,return和throw。有的程序员喜欢利用这个特点,比如
      switch(n) {
        case 1:
        case 2:
          break;
      }
    这样写容易出错,而且难以发现。因此建议避免switch贯穿,凡是有case的地方,一律加上break。
      switch(n) {
        case 1:
          break;
        case 2:
          break;
      }
  6. 单行的块结构
    if、while、do和for,都是块结构语句,但是也可以接受单行命令。比如
      if (ok) t = true;
    甚至写成
      if (ok)
        t = true;
    这样不利于阅读代码,而且将来添加语句时非常容易出错。建议不管是否只有一行命令,都一律加上大括号。
      if (ok){
        t = true;
      }
  7. ++和–
    递增运算符++和递减运算符–,直接来自C语言,表面上可以让代码变得很紧凑,但是实际上会让代码看上去更复杂和更晦涩。因此为了代码的整洁性和易读性,不用为好。
  8. 位运算符
    Javascript完全套用了Java的位运算符,包括按位与&、按位或|、按位异或^、按位非~、左移<<、带符号的右移>>和用0补足的右移>>>。
    这套运算符针对的是整数,所以对Javascript完全无用,因为Javascript内部,所有数字都保存为双精度浮点数。如果使用它们的话,Javascript不得不将运算数先转为整数,然后再进行运算,这样就降低了速度。而且”按位与运算符”&同”逻辑与运算符”&&,很容易混淆。
  9. function语句
    在Javascript中定义一个函数,有两种写法:
      function foo() { }

      var foo = function () { }
    两种写法完全等价。但是在解析的时候,前一种写法会被解析器自动提升到代码的头部,因此违背了函数应该先定义后使用的要求,所以建议定义函数时,全部采用后一种写法。
  10. 基本数据类型的包装对象
    Javascript的基本数据类型包括字符串、数字、布尔值,它们都有对应的包装对象String、Number和Boolean。所以,有人会这样定义相关值:
      new String(“Hello World”);
      new Number(2000);
      new Boolean(false);
    这样写完全没有必要,而且非常费解,因此建议不要使用。
    另外,new Object和new Array也不建议使用,可以用{}和[]代替。
  11. new语句
    Javascript是世界上第一个被大量使用的支持Lambda函数的语言,本质上属于与Lisp同类的函数式编程语言。但是当前世界,90%以上的程序员都是使用面向对象编程。为了靠近主流,Javascript做出了妥协,采纳了类的概念,允许根据类生成对象。
    类是这样定义的:
      var Cat = function (name) {
        this.name = name;
        this.saying = ‘meow’ ;
      }
    然后,再生成一个对象
      var myCat = new Cat(‘mimi’);
    这种利用函数生成类、利用new生成对象的语法,其实非常奇怪,一点都不符合直觉。而且,使用的时候,很容易忘记加上new,就会变成执行函数,然后莫名其妙多出几个全局变量。所以,建议不要这样创建对象,而采用一种变通方法。
    Douglas Crockford给出了一个函数:
      Object.beget = function (o) {
        var F = function (o) {};
        F.prototype = o ;
        return new F;
      };
    创建对象时就利用这个函数,对原型对象进行操作:
      var Cat = {
        name:’’,
        saying:’meow’
      };
      var myCat = Object.beget(Cat);
    对象生成后,可以自行对相关属性进行赋值:
      myCat.name = ‘mimi’;
  12. void
    在大多数语言中,void都是一种类型,表示没有值。但是在Javascript中,void是一个运算符,接受一个运算数,并返回undefined。
      void 0; // undefined
    这个命令没什么用,而且很令人困惑,建议避免使用。

MVC,MVP 和 MVVM 的图示

MVC,MVP 和 MVVM 的图示

[原文地址-阮一峰的网络日志](http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html)

复杂的软件必须有清晰合理的架构,否则无法开发和维护。
MVC(Model-View-Controller)是最常见的软件架构之一,业界有着广泛应用。它本身很容易理解,但是要讲清楚,它与衍生的 MVP 和 MVVM 架构的区别就不容易了。

一、MVC

MVC模式的意思是,软件可以分成三个部分。

视图(View):用户界面。
控制器(Controller):业务逻辑
模型(Model):数据保存

各部分之间的通信方式如下。

View 传送指令到 Controller
Controller 完成业务逻辑后,要求 Model 改变状态
Model 将新的数据发送到 View,用户得到反馈

所有通信都是单向的。

二、互动模式

接受用户指令时,MVC 可以分成两种方式。一种是通过 View 接受指令,传递给 Controller。

另一种是直接通过controller接受指令。

三、实例:Backbone

实际项目往往采用更灵活的方式,以 Backbone.js 为例。

  1. 用户可以向 View 发送指令(DOM 事件),再由 View 直接要求 Model 改变状态。
  2. 用户也可以直接向 Controller 发送指令(改变 URL 触发 hashChange 事件),再由 Controller 发送给 View。
  3. Controller 非常薄,只起到路由的作用,而 View 非常厚,业务逻辑都部署在 View。所以,Backbone 索性取消了 Controller,只保留一个 Router(路由器) 。

四、MVP

MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。

  1. 各部分之间的通信,都是双向的。
  2. View 与 Model 不发生联系,都通过 Presenter 传递。
  3. View 非常薄,不部署任何业务逻辑,称为”被动视图”(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。

五、MVVM

MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。

唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。Angular 和 Ember 都采用这种模式。

界面之下:还原真实的 MVC、MVP、MVVM 模式

界面之下:还原真实的 MVC、MVP、MVVM 模式

转载自: https://github.com/livoras/blog/issues/11作者: 戴嘉华

GUI程序所面临的问题

图形界面的应用程序提供给用户可视化的操作界面,这个界面提供给数据和信息。用户输入行为(键盘,鼠标等)会执行一些应用逻辑,应用逻辑(application logic)可能会触发一定的业务逻辑(business logic)对应用程序数据的变更,数据的变更自然需要用户界面的同步变更以提供最准确的信息。例如用户对一个电子表格重新排序的操作,应用程序需要响应用户操作,对数据进行排序,然后需要同步到界面上。

在开发应用程序的时候,以求更好的管理应用程序的复杂性,基于职责分离(Speration of Duties)的思想都会对应用程序进行分层。在开发图形界面应用程序的时候,会把管理用户界面的层次称为View,应用程序的数据为Model(注意这里的Model指的是Domain Model,这个应用程序对需要解决的问题的数据抽象,不包含应用的状态,可以简单理解为对象)。Model提供数据操作的接口,执行相应的业务逻辑。

有了View和Model的分层,那么问题就来了:View如何同步Model的变更,View和Model之间如何粘合在一起。

带着这个问题开始探索MV模式,会发现这些模式之间的差异可以归纳为对这个问题处理的方式的不同。而几乎所有的MV模式都是经典的Smalltalk-80 MVC的修改版。

Smalltalk-80 MVC

历史背景

早在上个世纪70年代,美国的施乐公司(Xerox)的工程师研发了Smalltalk编程语言,并且开始用它编写图形界面的应用程序。而在Smalltalk-80这个版本的时候,一位叫Trygve Reenskaug的工程师设计了MVC图形应用程序的架构模式,极大地降低了图形应用程序的管理难度。而在四人帮(GoF)的设计模式当中并没有把MVC当做是设计模式,而仅仅是把它看成解决问题的一些类的集合。Smalltalk-80 MVC和GoF描述的MVC是最经典的MVC模式。

MVC的依赖关系

MVC出了把应用程序分成View、Model层,还额外的加了一个Controller层,它的职责为进行Model和View之间的协作(路由、输入预处理等)的应用逻辑(application logic);Model进行处理业务逻辑。Model、View、Controller三个层次的依赖关系如下:

Controller和View都依赖Model层,Controller和View可以互相依赖。在一些网上的资料Controller和View之间的依赖关系可能不一样,有些是单向依赖,有些是双向依赖,这个其实关系不大,后面会看到它们的依赖关系都是为了把处理用户行为触发的事件处理权交给Controller。

MVC的调用关系

用户的对View操作以后,View捕获到这个操作,会把处理的权利交移给Controller(Pass calls);Controller会对来自View数据进行预处理、决定调用哪个Model的接口;然后由Model执行相关的业务逻辑;当Model变更了以后,会通过观察者模式(Observer Pattern)通知View;View通过观察者模式收到Model变更的消息以后,会向Model请求最新的数据,然后重新更新界面。如下图:

看似没有什么特别的地方,但是由几个需要特别关注的关键点:

  1. View是把控制权交移给Controller,Controller执行应用程序相关的应用逻辑(对来自View数据进行预处理、决定调用哪个Model的接口等等)。
  2. Controller操作Model,Model执行业务逻辑对数据进行处理。但不会直接操作View,可以说它是对View无知的。
  3. View和Model的同步消息是通过观察者模式进行,而同步操作是由View自己请求Model的数据然后对视图进行更新。

需要特别注意的是MVC模式的精髓在于第三点:Model的更新是通过观察者模式告知View的,具体表现形式可以是Pub/Sub或者是触发Events。而网上很多对于MVC的描述都没有强调这一点。通过观察者模式的好处就是:不同的MVC三角关系可能会有共同的Model,一个MVC三角中的Controller操作了Model以后,两个MVC三角的View都会接受到通知,然后更新自己。保持了依赖同一块Model的不同View显示数据的实时性和准确性。我们每天都在用的观察者模式,在几十年前就已经被大神们整合到MVC的架构当中。

这里有一个MVC模式的JavaScript Demo,实现了一个小的TodoList应用程序。经典的Smalltalk-80 MVC不需要任何框架支持就可以实现。目前Web前端框架当中只有一个号称是严格遵循Smalltalk-80 MVC模式的:maria.js。

MVC的优缺点

优点:

  1. 把业务逻辑和展示逻辑分离,模块化程度高。且当应用逻辑需要变更的时候,不需要变更业务逻辑和展示逻辑,只需要Controller换成另外一个Controller就行了(Swappable Controller)。
  2. 观察者模式可以做到多视图同时更新。

缺点:

  1. Controller测试困难。因为视图同步操作是由View自己执行,而View只能在有UI的环境下运行。在没有UI环境下对Controller进行单元测试的时候,应用逻辑正确性是无法验证的:Model更新的时候,无法对View的更新操作进行断言。
  2. View无法组件化。View是强依赖特定的Model的,如果需要把这个View抽出来作为一个另外一个应用程序可复用的组件就困难了。因为不同程序的的Domain Model是不一样的

MVC Model 2

在Web服务端开发的时候也会接触到MVC模式,而这种MVC模式不能严格称为MVC模式。经典的MVC模式只是解决客户端图形界面应用程序的问题,而对服务端无效。服务端的MVC模式又自己特定的名字:MVC Model 2,或者叫JSP Model 2,或者直接就是Model 2 。Model 2客户端服务端的交互模式如下:

服务端接收到来自客户端的请求,服务端通过路由规则把这个请求交由给特定的Controller进行处理,Controller执行相应的应用逻辑,对Model进行操作,Model执行业务逻辑以后;然后用数据去渲染特定的模版,返回给客户端。

因为HTTP协议是单工协议并且是无状态的,服务器无法直接给客户端推送数据。除非客户端再次发起请求,否则服务器端的Model的变更就无法告知客户端。所以可以看到经典的Smalltalk-80 MVC中Model通过观察者模式告知View更新这一环被无情地打破,不能称为严格的MVC。

Model 2模式最早在1998年应用在JSP应用程序当中,JSP Model 1应用管理的混乱诱发了JSP参考了客户端MVC模式,催生了Model 2。

后来这种模式几乎被应用在所有语言的Web开发框架当中。PHP的ThinkPHP,Python的Dijango、Flask,NodeJS的Express,Ruby的RoR,基本都采纳了这种模式。平常所讲的MVC基本是这种服务端的MVC。

MVP

MVP模式有两种:

  1. Passive View
  2. Supervising Controller

而大多数情况下讨论的都是Passive View模式。本文会对PV模式进行较为详细的介绍,而SC模式则简单提及。

历史背景

MVP模式是MVC模式的改良。在上个世纪90年代,IBM旗下的子公司Taligent在用C/C++开发一个叫CommonPoint的图形界面应用系统的时候提出来的。

MVP(Passive View)的依赖关系

MVP模式把MVC模式中的Controller换成了Presenter。MVP层次之间的依赖关系如下:

MVP打破了View原来对于Model的依赖,其余的依赖关系和MVC模式一致。

MVP(Passive View)的调用关系

既然View对Model的依赖被打破了,那View如何同步Model的变更?看看MVP的调用关系:

和MVC模式一样,用户对View的操作都会从View交移给Presenter。Presenter会执行相应的应用程序逻辑,并且对Model进行相应的操作;而这时候Model执行完业务逻辑以后,也是通过观察者模式把自己变更的消息传递出去,但是是传给Presenter而不是View。Presenter获取到Model变更的消息以后,通过View提供的接口更新界面。

关键点:

  1. View不再负责同步的逻辑,而是由Presenter负责。Presenter中既有应用程序逻辑也有同步逻辑。
  2. View需要提供操作界面的接口给Presenter进行调用。(关键)

对比在MVC中,Controller是不能操作View的,View也没有提供相应的接口;而在MVP当中,Presenter可以操作View,View需要提供一组对界面操作的接口给Presenter进行调用;Model仍然通过事件广播自己的变更,但由Presenter监听而不是View。

MVP模式,这里也提供一个用JavaScript编写的例子。

MVP(Passive View)的优缺点

优点:

  1. 便于测试。Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter应用逻辑的正确性。这里根据上面的例子给出了Presenter的单元测试样例。
  2. View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做到高度可复用的View组件。

缺点:

  1. Presenter中除了应用逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。

MVP(Supervising Controller)

上面讲的是MVP的Passive View模式,该模式下View非常Passive,它几乎什么都不知道,Presenter让它干什么它就干什么。而Supervising Controller模式中,Presenter会把一部分简单的同步逻辑交给View自己去做,Presenter只负责比较复杂的、高层次的UI操作,所以可以把它看成一个Supervising Controller。

Supervising Controller模式下的依赖和调用关系:

因为Supervising Controller用得比较少,对它的讨论就到这里为止。

MVVM

MVVM可以看作是一种特殊的MVP(Passive View)模式,或者说是对MVP模式的一种改良。

历史背景

MVVM模式最早是微软公司提出,并且了大量使用在.NET的WPF和Sliverlight中。2005年微软工程师John Gossman在自己的博客上首次公布了MVVM模式。

ViewModel

MVVM代表的是Model-View-ViewModel,这里需要解释一下什么是ViewModel。ViewModel的含义就是 “Model of View”,视图的模型。它的含义包含了领域模型(Domain Model)和视图的状态(State)。 在图形界面应用程序当中,界面所提供的信息可能不仅仅包含应用程序的领域模型。还可能包含一些领域模型不包含的视图状态,例如电子表格程序上需要显示当前排序的状态是顺序的还是逆序的,而这是Domain Model所不包含的,但也是需要显示的信息。

可以简单把ViewModel理解为页面上所显示内容的数据抽象,和Domain Model不一样,ViewModel更适合用来描述View。

MVVM的依赖

MVVM的依赖关系和MVP依赖,只不过是把P换成了VM。

MVVM的调用关系

MVVM的调用关系和MVP一样。但是,在ViewModel当中会有一个叫Binder,或者是Data-binding engine的东西。以前全部由Presenter负责的View和Model之间数据同步操作交由给Binder处理。你只需要在View的模版语法当中,指令式地声明View上的显示的内容是和Model的哪一块数据绑定的。当ViewModel对进行Model更新的时候,Binder会自动把数据更新到View上去,当用户对View进行操作(例如表单输入),Binder也会自动把数据更新到Model上去。这种方式称为:Two-way data-binding,双向数据绑定。可以简单而不恰当地理解为一个模版引擎,但是会根据数据变更实时渲染。

也就是说,MVVM把View和Model的同步逻辑自动化了。以前Presenter负责的View和Model同步不再手动地进行操作,而是交由框架所提供的Binder进行负责。只需要告诉Binder,View显示的数据对应的是Model哪一部分即可。

这里有一个JavaScript MVVM的例子,因为MVVM需要Binder引擎。所以例子中使用了一个MVVM的库:Vue.js

MVVM的优缺点

优点:

  1. 提高可维护性。解决了MVP大量的手动View和Model同步的问题,提供双向绑定机制。提高了代码的可维护性。
  2. 简化测试。因为同步逻辑是交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试。

缺点:

  1. 过于简单的图形界面不适用,或说牛刀杀鸡。
  2. 对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。
  3. 数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的。

结语

可以看到,从MVC->MVP->MVVM,就像一个打怪升级的过程。后者解决了前者遗留的问题,把前者的缺点优化成了优点。同样的Demo功能,代码从最开始的一堆文件,优化成了最后只需要20几行代码就完成。MV*模式之间的区分还是蛮清晰的,希望可以给对这些模式理解比较模糊的同学带来一些参考和思路。

web开发的MVVM模式

web开发的MVVM模式*转

MVC VS. MVP VS. MVVM

  了解MVVM模式之前,我们先来简单了解一下从MVC到MVVM的变迁。这个变迁是耦合从紧到松的变迁,是对依赖处理的进化,是应对变化技术的成熟。

MVC

  MVC全名是Model View Controller, 是模型(model)-视图(view)-控制器(controller)的缩写,它用一种将业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。将系统进行MVC分层的核心思路就是分离组件,降低组件耦合性,组件独立演化。

  • MVC模式可以使用下面这幅图来描述各个部分的作用以及与其他组件的交互过程:

MVC模式的优点很明显,那就是耦合性低,重用性高,应对每个组件的变化性好,便于开发,测试与维护。但是MVC模式也还是有缺点的,给系统带来的额外复杂性自然不必说了,此外,MVC三个组件的定义存在一定的模糊性,这样就衍生了很多的比如主动MVC,被动MVC等实施方案,每种方案中MVC三大组件的功能都不太一样,比如有的把业务逻辑放到Model部分,Controller只不过是把用户输入转换一下,直接传给Model来处理,Model是整个系统的核心部分,上面这幅图就是这样的(这种情况其实就是真正的MVC模式的定义),还有的方案把业务逻辑直接放到了Controller中,Model只负责处理数据。而且在不同的方案中,要么View与Controller耦合过于紧密,要么View访问Model的时候比较低效,要么Model由于要与View直接打交道,导致了Model与View直接耦合在了一起,而Model发生变化的时候会牵连View一起变化,这却恰恰违背了MVC组件分层的本意。

MVP

  MVP的全称为Model-View-Presenter,Model提供数据,View负责显示,Presenter负责逻辑的处理。MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过Controller。

  • MVP模式可以使用下面这幅图来描述各个部分的作用以及与其他组件的交互过程:

在MVC里,我们谈到它的缺点主要有两个:

  • 一个是MVC三大组件的定义比较模糊。这个在MVP中就相对明确一些了:Model只负责处理最终数据;View就是很薄的一层,只显示数据,通常认为,View只应该有简单的Set/Get的方法,View只负责用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接访问Model;所有业务逻辑都由Presenter处理,而且,Presenter与具体的View和Model是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View或者Model具体实现的时候可以保持Presenter的不变,这样就实现了业务逻辑的重用。

  • 另一个是View与Model的紧密耦合。在MVC中,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。

在现实中,MVP的实现会根据View的充、贫血而有一些不同,一部分倾向于在View中放置简单的逻辑,在Presenter放置复杂的逻辑;另一部分倾向于在presenter中放置全部的逻辑。这两种分别被称为:Passive View和Superivising Controller。

在Passive View中,为了减少UI组件的行为,使用Controller不仅控制用户事件的响应,而且将结果更新到View上。可以集中测试Controller,减小View出问题的风险。

在Superivising Controller中的Controller既处理用户输入的响应,又操作View处理View的复杂逻辑。

  MVP解决了MVC的一些问题,其实现在说到MVC,通常指的也就是MVP。但是MVP也是不完美的,由于Presenter可以反作用于View,这样View的渲染有可能就放在了Presenter中,所以View和Presenter的交互会过于频繁。如果Presenter过多地渲染了View,往往会使得它与特定的View的联系过于紧密。一旦View需要变更,那么Presenter也需要变更了。

MVVM

  MVVM全称Model-View-ViewModel,Model提供数据,View负责显示,这个还和MVP一样,这个模式的重点就是ViewModel的部分,它通过双向绑定(松耦合)解决了MVP中Presenter与View联系比较紧密的问题。

  • MVVM模式可以使用下面这幅图来描述各个部分的作用以及与其他组件的交互过程:

从图中我们可以看到MVVM与MVP最大的不同就在于View与ViewModel交互的时候使用了松耦合的双向绑定,而不是像View与Presenter那样直接交互。ViewModel作为View的数据映射,通常View上有什么属性,ViewModel上也会存在相应的一个属性,这两个属性通过事件实现了双向的绑定,常见的MVVM框架都替我们完成了这样的绑定过程。
  MVVM最初是微软在WPF中实现的模式,随之在Web开发中也开始了MVVM风潮,这里介绍的3个MVVM框架就是我认为比较典型的几个。

微软的前端框架knockoutjs

  这是一个独立的前端MVVM框架,也是最早的前端MVVM框架,它完成了页面的数据与界面之间的双向绑定,至于真正这些数据是通过何种手段(比如Ajax)获取到的,它并不关心。这是一个只关注前端逻辑与界面解耦的纯前端类库,理论上可以搭配任何的后台技术,来完成整个网站的建设。这是我第一个学习的MVVM框架,使用它给我的感觉是相当良好:绑定好了自动双向更新是多么酷的特性啊。

谷歌的全套服务angularjs

  这是一个比较庞大,但是也无比强大的前端MVVM类库,天生对RESTful API的良好支持,双向绑定的优良特性,专业的HTML模板扩展支持,全面的测试框架,与Bootstrap,Nodejs的完美融合,再加上Google的金字招牌,你可以拒绝么?

最迷你的MVVM框架avalon

  这确实是最mini的MVVM框架,与Angularjs不一样,它侧重数据结构的设计,提倡简单的算法,它是在吸收上面两个类库的基础上发展起来的,而且符合国情,支持IE6,难道不值得期待?

最后附送一个有意思的网站:http://todomvc.com/,自己研究一下吧。

CSS3 @font-face

(转)CSS3 @font-face
原文地址

http://www.fontsquirrel.com/fontface

@font-face是CSS3中的一个模块,他主要是把自己定义的Web字体嵌入到你的网页中,随着@font-face模块的出现,我们在Web的开发中使用字体不怕只能使用Web安全字体,你们当中或许有许多人会不自然的问,这样的东西IE能支持吗?当我告诉大家@font-face这个功能早在IE4就支持了你肯定会感到惊讶。我的Blog就使用了许多这样的自定义Web字体,比如说首页的Logo,Tags以及页面中的手写英文体,很多朋友问我如何使用,能让自己的页面也支持这样的自定义字体,一句话这些都是@font-face实现的,为了能让更多的朋友知道如何使用他,今天我主要把自己的一点学习过程贴上来和大家分享。

首先我们一起来看看@font-face的语法规则:

1
2
3
4
5
6
@font-face {
font-family: <YourWebFontName>;
src: <source> [<format>][,<source> [<format>]]*;
[font-weight: <weight>];
[font-style: <style>];
}

取值说明

YourWebFontName:此值指的就是你自定义的字体名称,最好是使用你下载的默认字体,他将被引用到你的Web元素中的font-family。如“font-family:”YourWebFontName”;”
source:此值指的是你自定义的字体的存放路径,可以是相对路径也可以是绝路径;
format:此值指的是你自定义的字体的格式,主要用来帮助浏览器识别,其值主要有以下几种类型:truetype,opentype,truetype-aat,embedded-opentype,avg等;
weight和style:这两个值大家一定很熟悉,weight定义字体是否为粗体,style主要定义字体样式,如斜体。
兼容浏览器

说到浏览器对@font-face的兼容问题,这里涉及到一个字体format的问题,因为不同的浏览器对字体格式支持是不一致的,这样大家有必要了解一下,各种版本的浏览器支持什么样的字体,前面也简单带到了有关字体的几种格式,下面我就分别说一下这个问题,让大家心里有一个概念:

一、TureTpe(.ttf)格式:

.ttf字体是Windows和Mac的最常见的字体,是一种RAW格式,因此他不为网站优化,支持这种字体的浏览器有【IE9+,Firefox3.5+,Chrome4+,Safari3+,Opera10+,iOS Mobile Safari4.2+】;

二、OpenType(.otf)格式:

.otf字体被认为是一种原始的字体格式,其内置在TureType的基础上,所以也提供了更多的功能,支持这种字体的浏览器有【Firefox3.5+,Chrome4.0+,Safari3.1+,Opera10.0+,iOS Mobile Safari4.2+】;

三、Web Open Font Format(.woff)格式:

.woff字体是Web字体中最佳格式,他是一个开放的TrueType/OpenType的压缩版本,同时也支持元数据包的分离,支持这种字体的浏览器有【IE9+,Firefox3.5+,Chrome6+,Safari3.6+,Opera11.1+】;

四、Embedded Open Type(.eot)格式:

.eot字体是IE专用字体,可以从TrueType创建此格式字体,支持这种字体的浏览器有【IE4+】;

五、SVG(.svg)格式:

.svg字体是基于SVG字体渲染的一种格式,支持这种字体的浏览器有【Chrome4+,Safari3.1+,Opera10.0+,iOS Mobile Safari3.2+】。

这就意味着在@font-face中我们至少需要.woff,.eot两种格式字体,甚至还需要.svg等字体达到更多种浏览版本的支持。

为了使@font-face达到更多的浏览器支持,Paul Irish写了一个独特的@font-face语法叫Bulletproof @font-face:

1
2
3
4
5
  @font-face {
font-family: 'YourWebFontName';
src: url('YourWebFontName.eot?') format('eot');/*IE*/
src:url('YourWebFontName.woff') format('woff'), url('YourWebFontName.ttf') format('truetype');/*non-IE*/
}

但为了让各多的浏览器支持,你也可以写成:

1
2
3
4
5
6
7
8
  @font-face {
font-family: 'YourWebFontName';
src: url('YourWebFontName.eot'); /* IE9 Compat Modes */
src: url('YourWebFontName.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('YourWebFontName.woff') format('woff'), /* Modern Browsers */
url('YourWebFontName.ttf') format('truetype'), /* Safari, Android, iOS */
url('YourWebFontName.svg#YourWebFontName') format('svg'); /* Legacy iOS */
}

说了这么多空洞的理论知识,大家一定有点心痒痒了,那么我们先来看看W3CPLUS首页中导航部分的兰色字体是如何实现的,假如我们有一个这样的DOM标签,需要应用自定义字体:

HTML Code:

< h2 class=”neuesDemo”>Neues Bauen Demo< /h2>
通过@font-face来定义自己的Web Font:

1
2
3
4
5
6
7
8
9
10
@font-face {
font-family: 'NeuesBauenDemo';
src: url('../fonts/neues_bauen_demo-webfont.eot');
src: url('../fonts/neues_bauen_demo-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/neues_bauen_demo-webfont.woff') format('woff'),
url('../fonts/neues_bauen_demo-webfont.ttf') format('truetype'),
url('../fonts/neues_bauen_demo-webfont.svg#NeuesBauenDemo') format('svg');
font-weight: normal;
font-style: normal;
}

我在这里采用的是相对路径,当然大家也可以使用绝路径。到这里我们就需要把定义好的字体应用到我们实际页面中去:

1
2
3
h2.neuesDemo {
font-family: 'NeuesBauenDemo'
}

效果:

看到上面的效果,我想大家会感到@font-face很神奇,同时也想争着做做看,可是一动手才发现,特殊字体我要怎样才能得到,那些.eot,.woff,.ttf,.svg这些字体格式又怎么获取呢?有些朋友可能就不知道如何运手了,那么我们就带着这些问题来一个全程完成的实例吧:

一、获取特殊字体:

我们拿下面这种single Malta字体来说吧:

要得到single Malta字体,不外乎两种途径,其一找到付费网站购买字体,其二就是到免费网站DownLoad字体。当然要给钱的这种傻事我想大家都不会做的,那我们就得到免费的地方下载,在哪有呢?我平时都是到Google Web FontsDafont.com寻找自己需要的字体,当然网上也还有别的下载字体的地方,这个Demo使用的是Dafont.com的Single Malta字体,这样就可以到这里下载Single Malta

二、获取@font-face所需字体格式:

特殊字体已经在你的电脑中了,现在我们需要想办法获得@font-face所需的.eot,.woff,.ttf,.svg字体格式。要获取这些字体格式,我们同样是需要第三方工具或者软件来实现,下面我给大家推荐一款我常用的一个工具fontsquirrel,别的先不多说,首跟我点这里进入到下面这个界面吧。

如果你进入页面没有看到上图,你可以直接点击导航:

如果你看到了上面的界面,那就好办了,我们来看如何应用这个工具生成@font-face需要的各种字体,先把我们刚才下载的字体上传上去:

大家可以看到,解压缩出来的文件格式,里面除了@font-face所需要的字体格式外,还带有一个DEMO文件,如果你不清楚的也可以参考下载下来的DEMO文件,我在这里不对DEMO说明问题,我主要是给大家介绍如何把下载下来的文件有价值的运用到我们的项目中。

例如在自己的本地创建了一个fontface项目:

为了让项目结构更清晰,我们在项目中单独创建一个fonts目录,用来放置解压缩出来@font-face所需的字体格式:

现在@font-face所需字体已经加载到本地项目,现在本地项目中的style.css中附上我们需要的@font-face样式

@font-face {
font-family: ‘SingleMaltaRegular’;
src: url(‘../fonts/singlemalta-webfont.eot’);
src: url(‘../fonts/singlemalta-webfont.eot?#iefix’) format(‘embedded-opentype’),
url(‘../fonts/singlemalta-webfont.woff’) format(‘woff’),
url(‘../fonts/singlemalta-webfont.ttf’) format(‘truetype’),
url(‘../fonts/singlemalta-webfont.svg#SingleMaltaRegular’) format(‘svg’);
font-weight: normal;
font-style: normal;
}
到这里为止,我们已经通过@font-face自定义好所需的SingleMalta字体,离最后效果只差一步了,就是把自己定义的字体应用到你的Web中的DOM元素上:

h2.singleMalta {
font-family: ‘SingleMaltaRegular’
}

看到上面的效果,那大家就知道我们实现成功了。那么关于@font-face帮你打造特殊效果的字体,到这里基本上就完成了,我在这里需要提醒使用者:

1、如果你的项目中是英文网站,而且项目中的Logo,Tags等应用到较多的这种特殊字体效果,我建议你不要使用图片效果,而使用@font-face,但是你是中文网站,我觉得还是使用图片比较合适,因为加载英文字体和图片没有多大区别,但是你加载中文字体,那就不一样了,因为中文字体太大了,这样会影响到项目的某些性能的优化;

2、致命的错误,你在@font-face中定义时,文件路径没有载对;

3、你只定义了@font-face,但并没有应用到你的项目中的DOM元素上;

以上几点都是在平时制作中常出现的问题,希望大家能小意一些,另外我们没有办法在购买所有字体,就算你实力雄厚,那也没有办法在一台服务器主机上放置你所有项目需要的字体。因此我给大家提供几个免费字体下载的网址:Webfonts,Typekit,Kernest,Google Web Fonts,Kernest,Dafont,Niec Web Type,不然你点这里将有更多的免费字体。前面几个链接是帮助你获取一些优美的怪异的特殊字体,但下面这个工具作用更是无穷的大,他能帮你生成@font-face所需要的各种字体,这工具就是Font Squirrel。

最后在提醒一下,使用@font-face别的可以忘了,但Font Squirrel千万不能忘,因为他能帮你生成@font-face所需的各种字体格式。

到此关于@font-face就介绍完了,不知道大家喜欢不喜欢,如果喜欢的话赶快动手实践一下,有Blog的可以马上运用上去,也可以炫一下。

库和框架区别

库和框架区别

库和框架都是一种有别于软件、面向程序开发者的产品形式。正因为如此,也有很多人误以为库就是框架,或者认为指定语言的库就是框架。

库的英语为 Library ( 简写 Lib ),框架的英语为 Framework。

库是将代码集合成的一个产品,供程序员调用。面向对象的代码组织形式而成的库也叫类库。面向过程的代码组织形式而成的库也叫函数库。
在函数库中的可直接使用的函数叫库函数。开发者在使用库的时候,只需要使用库的一部分类或函数,然后继续实现自己的功能。

框架则是为解决一个(一类)问题而开发的产品,框架用户一般只需要使用框架提供的类或函数,即可实现全部功能。可以说,框架是库的升级版。
开发者在使用框架的时候,必须使用这个框架的全部代码。

库是一些方法的集合,避免定义重复功能的函数并具有一定的模式兼容性。类似于一个工具箱,里面有很多的工具,比如jQuery,可以很方便的调用里面的方法和函数,实现一些功能。
框架更应该是一种对开发者的规范,开发者按照框架的设计去实现以下功能或效果,并非简单的工具集的概念,框架可以提供相应的库或者基于库来实现,但是库一般不具备框架的规范性。比如bootstrap,提供了一种完整的规范和功能,我们在它的基础上进行调整。

php连接数据库(转载二)

PHP连接MySQL数据库*转

既然现在你看到了这篇文章,说明你肯定知道PHP和MySQL是怎么一回事,我就不啰嗦了。但为什么你还要继续阅读此文呢?可能是以前你习惯复制粘贴一些代码,并没有真正弄懂代码的含义;也可能你以前弄懂了,但像我一样,有一段时间没有接触,生疏了;再或者,有人问你类似的简单问题,而你已经不屑于回答,直接上网搜索,恰好找到了这篇,于是就推荐给那谁谁谁了…不管怎样,在这里我总结了三种常用的PHP连接MySQL数据库方法,希望能够帮到你,当然也是作为我自己的一个回顾总结。

方法一:普通方法(面向过程)

首先,我先做出如下假设(也适用与方法二和方法三)

$username=your_name;
$userpass=your_pass;
$dbhost=localhost;
$dbdatabase=your_database;
下面是关键步骤:

1
2
3
4
5
6
7
8
9
10
11
 1 //生成一个连接
2 $db_connect=mysql_connect($dbhost,$username,$userpass) or die("Unable to connect to the MySQL!");
3
4 //选择一个需要操作的数据库
5 mysql_select_db($dbdatabase,$db_connect);
6
7 //执行MySQL语句
8 $result=mysql_query("SELECT id,name FROM user");
9
10 //提取数据
11 $row=mysql_fetch_row($result);

代码注释已经说明了一切。不过这里还需要做一些补充。

①在mysql_connect()、mysql_select_db()等函数之前使用@(错误控制运算符),可以忽略掉系统产生的错误信息,然后我们用die()来自定义错误信息;

②提取数据的时候,除了上面的mysql_fetch_row,常见的还有mysql_fetch_assoc和mysql_fetch_array,具体差别请查阅PHP Manual;

③对于mysql_query()函数的返回值,如果执行的语句有返回值(如SELECT、SHOW、DESCRIBE等),则返回相应数据(成功时)或FALSE(失败时);如果执行的语句没有返回值(如DELETE、DROP、INSERT、UPDATE等),则返回TRUE(成功时)或FALSE(失败时)。

方法二:面向对象方法

其实这种方法和普通方法非常类似,只是把相应的函数换成了面向对象方法,直接看代码。

1
2
3
4
5
6
7
8
1 $db=new mysqli($dbhost,$username,$userpass,$dbdatabase);
2 if(mysqli_connect_error()){
3 echo 'Could not connect to database.';
4 exit;
5 }
6
7 $result=$db->query("SELECT id,name FROM user");
8 $row=$result->fetch_row();

这里用到的是mysqli,意思就是mysql的扩展,既可以通过面向过程的方式也可以通过面向对象的方式与数据库进行交互,唯一不同的是调用函数(对象方法)的方式不一样。

方法三:PDO方法

PDO其实是PHP Database Objects的缩写,中文即PHP数据库对象。它提供了一种统一的PHP与数据库交互的方法。

这是目前比较流行的一种连接数据库的方法。它的优势在于:只要正确提供数据源,余下对于数据库的基本操作都是一样的。也就是说,同一段代码既可以同MySQL交互,也可以和SQLite3交互,当然也可以和PostgreSQL进行交互,前提是你提供了正确的数据源。下面看一下连接MySQL的代码:

1
2
3
4
5
6
7
8
9
10
$dsn='mysql:host='.$dbhost.';dbname='.$dbdatabase.';'
$dbh=new PDO($dsn,$username,$userpass);
如果是SQLite3,直接使用下面的代码:

$dsn='sqlite3:"C:\sqlite\user.db"';
$dbh=new PDO($dsn);
如果是PostgreSQL,下面的代码能够对付:

$dsn='pgsql:host='.$dbhost.' port=5432 dbname='.$dbdatabase.' user='.$username.' password='.$userpass;
$dbh=new PDO($dsn);

跟数据库成功建立连接之后,下面就只需要从数据库获取数据或插入更新数据,实例代码如下:

1
2
$stmt=$dbh->query('SELECT id,name FROM user');
$row=$stmt->fetch();

至此,文章告一段落。当然,上面只是简单的介绍了一下PHP与数据库有关的最简单的操作,后面还有很多比如插入、排序、防止SQL注入等等一些方面的知识需要深入学习。希望有机会能够和大家一起探讨,共同进步。

php连接数据库(转载一)

忠告:写程序切记马虎,不细仔,真是写程序的大忌中的大忌。所以以后写代码务必:细心仔细不骄不燥,才能写出来好代码来,避免迷惘。。

繁写:

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
 <?php
echo "This is a test</br>";
echo "asdfasdfadsf";
$mysql_server_name="localhost"; //数据库服务器名称
$mysql_username="root"; // 连接数据库用户名
$mysql_password="??????"; // 连接数据库密码
$mysql_database="??????"; // 数据库的名字

// 连接到数据库
$conn=mysql_connect($mysql_server_name, $mysql_username,
$mysql_password);

// 从表中提取信息的sql语句
$strsql="SELECT * FROM `gbook`";
// 执行sql查询
$result=mysql_db_query($mysql_database, $strsql, $conn);
// 获取查询结果
$row=mysql_fetch_row($result);


echo '<font face="verdana">';
echo '<table border="1" cellpadding="1" cellspacing="2">';

// 显示字段名称
echo "</b><tr></b>";
for ($i=0; $i<mysql_num_fields($result); $i++)
{
echo '<td bgcolor="#000F00"><b>'.
mysql_field_name($result, $i);
echo "</b></td></b>";
}
echo "</tr></b>";
// 定位到第一条记录
mysql_data_seek($result, 0);
// 循环取出记录
while ($row=mysql_fetch_row($result))
{
echo "<tr></b>";
for ($i=0; $i<mysql_num_fields($result); $i++ )
{
echo '<td bgcolor="#00FF00">';
echo $row[$i];
echo '</td>';
}
echo "</tr></b>";
}

echo "</table></b>";
echo "</font>";
// 释放资源
mysql_free_result($result);
// 关闭连接
mysql_close($conn);
?>

简写:

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
<?php
$conn=mysql_connect("localhost", "root", "password");
$result=mysql_db_query("Database", "SELECT * FROM `info`", $conn);
// 获取查询结果
$row=mysql_fetch_row($result);


echo '<font face="verdana">';
echo '<table border="1" cellpadding="1" cellspacing="2">';

// 显示字段名称
echo "</b><tr></b>";
for ($i=0; $i<mysql_num_fields($result); $i++)
{
echo '<td bgcolor="#000F00"><b>'.
mysql_field_name($result, $i);
echo "</b></td></b>";
}
echo "</tr></b>";
// 定位到第一条记录
mysql_data_seek($result, 0);
// 循环取出记录
while ($row=mysql_fetch_row($result))
{
echo "<tr></b>";
for ($i=0; $i<mysql_num_fields($result); $i++ )
{
echo '<td bgcolor="#00FF00">';
echo $row[$i];
echo '</td>';
}
echo "</tr></b>";
}

echo "</table></b>";
echo "</font>";
// 释放资源
mysql_free_result($result);
// 关闭连接
mysql_close($conn);
?>

echo出来的主要是表格

,