中作中异常情况处理制度下怎处理 www.bkjia.com

现在位置:
php中try catch捕获异常实例详解,trycatch
本文实例讲述了php中try catch捕获异常。分享给大家供大家参考。具体方法分析如下:
php中try catch可以帮助我们捕获程序代码的异常了,这样我们可以很好的处理一些不必要的错误了,感兴趣的朋友可以一起来看看。
PHP中try{}catch{}语句概述
PHP5添加了类似于其它语言的异常处理模块。在 PHP 代码中所产生的异常可被 throw语句抛出并被 catch 语句捕获。(注:一定要先抛才能获取)
需要进行异常处理的代码都必须放入 try 代码块内,以便捕获可能存在的异常。
每一个 try 至少要有一个与之对应的 catch。
使用多个 catch可以捕获不同的类所产生的异常。
当 try 代码块不再抛出异常或者找不到 catch 能匹配所抛出的异常时,PHP 代码就会在跳转到最后一个 catch 的后面继续执行。
当然,PHP允许在 catch 代码块内再次抛出(throw)异常。
当一个异常被抛出时,其后(译者注:指抛出异常时所在的代码块)的代码将不会继续执行,而 PHP 就会尝试查找第一个能与之匹配的 catch。
如果一个异常没有被捕获,而且又没用使用 set_exception_handler() 作相应的处理的话,那么 PHP 将会产生一个严重的错误,并且输出 Uncaught Exception ... (未捕获异常)的提示信息。
先来看一下PHP内置异常类的基本属性和方法。(不包括具体实现)
复制代码 代码如下:try{
throw new Exception();
//这里可以捕获到前面一个块抛出的Exception
为了进一步处理异常,我们需要使用PHP中try{}catch{}----包括Try语句和至少一个的catch语句。任何调用 可能抛出异常的方法的代码都应该使用try语句。Catch语句用来处理可能抛出的异常。以下显示了我们处理getCommandObject()抛出的异常的方法:
复制代码 代码如下:&?php
$mgr = new CommandManager();
$cmd = $mgr-&getCommandObject("realcommand");
$cmd-&execute();
} catch (Exception $e) {
print $e-&getMessage();
可以看到,通过结合使用throw关键字和PHP中try{}catch{},我们可以避免错误标记“污染”类方法返回的值。因为“异常”本身就是一种与其它任何对象不同的PHP内建的类型,不会产生混淆。
如果抛出了一个异常,try语句中的脚本将会停止执行,然后马上转向执行catch语句中的脚本。
例子如下:
包含文件错误抛出异常
复制代码 代码如下:&?php
// 错误的演示
require ('test_try_catch.php');
} catch (Exception $e) {
echo $e-&getMessage();
// 正确的抛出异常
if (file_exists('test_try_catch.php')) {
require ('test_try_catch.php');
throw new Exception('file is not exists');
} catch (Exception $e) {
echo $e-&getMessage();
如果异常抛出了却没有被捕捉到,就会产生一个fatal error。
多个catch捕获多个异常
PHP将查询一个匹配的catch代码块。如果有多个catch代码块,传递给每一个catch代码块的对象必须具有不同类型,这样PHP可以找到需要进入哪一个catch代码块。当try代码块不再抛出异常或者找不到catch能匹配所抛出的异常时,PHP代码就会在跳转最后一个catch的后面继续执行。多个异常的捕获的示例如下:
复制代码 代码如下:&?php
&&& class MyException extends Exception{
&&&&&&&&&& //重定义构造器使第一个参数message变为必须被指定的属性
&&&&&&&&&& public function __construct($message, $code=0){
&&&&&&&&&&&&&& //可以在这里定义一些自己的代码
&&&&&&&&&&&&&& //建议同时调用parent::construct()来检查所有的变量是否已被赋值
&&&&&&&&&&&&&& parent::__construct($message, $code);
&&&&&&&&&& }
&&&&&&&&&& //重写父类中继承过来的方法,自定义字符串输出的样式
&&&&&&&&&& public function __toString(){
&&&&&&&&&&&&&& return __CLASS__.":[".$this-&code."]:".$this-&message."&br&";
&&&&&&&&&& }
&&&&&&&&&& //为这个异常自定义一个处理方法
&&&&&&&&&& public function customFunction(){
&&&&&&&&&&&&&& echo "按自定义的方法处理出现的这个类型的异常";
&&&&&&&&&& }
&&& //创建一个用于测试自定义扩展的异常类MyException
&&& class TestException{
&&&&&&& public $&&&&&&&&&& //用来判断对象是否创建成功的成员属性
&&&&&&& function __construct($value=/PHPjc/0){
//通过构造方法的传值决定抛出的异常
&&&&&&&&&&& switch($value){&&&&&&&&&&&&&&&&&&&&&&&&& //对传入的值进行选择性的判断
&&&&&&&&&&&&&&& case 1:&&&&&&&&&&&&&&&&&&&&&&&&&&&&& //掺入参数1,则抛出自定义的异常对象
&&&&&&&&&&&&&&&&&&& throw new MyException("传入的值“1”是一个无效的参数",5);
&&&&&&&&&&&&&&& case 2:&&&&&&&&&&&&&&&&&&&&&&&&&&&&& //传入参数2,则抛出PHP内置的异常对象
&&&&&&&&&&&&&&&&&&& throw new MyException("传入的值“2”不允许作为一个参数",6);
&&&&&&&&&&&&&&& default:&&&&&&&&&&&&&&&&&&&&&&&&&&&& //传入参数合法,则不抛出异常
&&&&&&&&&&&&&&&&&&& $this-&var=$&&&&&&&&& //为对象中的成员属性赋值
&&&&&&&&&&& }
&&& //示例1,在没有异常时,程序正常执行,try中的代码全部执行并不会执行任何catch区块
&&&&&&& $testObj =new TestException();&&&&&&&&&& //使用默认参数创建异常的擦拭类对象
&&&&&&& echo "********&br&";&&&&&&&&&&&&&&&&&&&& //没有抛出异常这条语句就会正常执行
&&& }catch(MyException $e){&&&&&&&&&&&&&&&&&&&&& //捕获用户自定义的异常区块
&&&&&&& echo "捕获自定义的异常:$e&br&";&&&&&&&&& //按自定义的方式输出异常消息
&&&&&&& $e-&customFunction();&&&&&&&&&&&&&&&&&&& //可以调用自定义的异常处理方法
&&& }catch(Exception $e){&&&&&&&&&&&&&&&&&&&&&&& //捕获PHP内置的异常处理类的对象
&&&&&&& echo "捕获默认的异常:".$e-&getMessage()."&br&";&&&&&& //输出异常消息
&&& var_dump($testObj);&&&&&&& //判断对象是否创建成功,如果没有任何异常,则创建成功
&&& //示例2,抛出自定义的异常,并通过自定义的异常处理类捕获这个异常并处理
&&&&&&& $testObj1 =new TestException(1);&&&&&&&& //传1时,抛出自定义异常
&&&&&&& echo "********&br&";&&&&&&&&&&&&&&&&&&&& //这个语句不会被执行
&&& }catch(MyException $e){&&&&&&&&&&&&&&&&&&&&& //这个catch区块中的代码将被执行
&&&&&&& echo "捕获自定义的异常:$e&br&";&&&&&&&&&
&&&&&&& $e-&customFunction();&&&&&&&&&&&&&&&&&&&
&&& }catch(Exception $e){&&&&&&&&&&&&&&&&&&&&&&& //这个catch区块不会执行
&&&&&&& echo "捕获默认的异常:".$e-&getMessage()."&br&";&&&&&&
&&& var_dump($testObj1);&&&&&&& //有异常产生,这个对象没有创建成功
&&& //示例2,抛出自内置的异常,并通过自定义的异常处理类捕获这个异常并处理
&&&&&&& $testObj2 =new TestException(2);&&&&&&&& //传入2时,抛出内置异常
&&&&&&& echo "********&br&";&&&&&&&&&&&&&&&&&&&& //这个语句不会被执行
&&& }catch(MyException $e){&&&&&&&&&&&&&&&&&&&&& //这个catch区块中的代码将被执行
&&&&&&& echo "捕获自定义的异常:$e&br&";&&&&&&&&&
&&&&&&& $e-&customFunction();&&&&&&&&&&&&&&&&&&&
&&& }catch(Exception $e){&&&&&&&&&&&&&&&&&&&&&&& //这个catch区块不会执行
&&&&&&& echo "捕获默认的异常:".$e-&getMessage()."&br&";&&&&&&
&&& var_dump($testObj2);&&&&&&& //有异常产生,这个对象没有创建成功
在上面的代码中,可以使用两个异常处理类:一个是自定义的异常处理类MyException;另一个则是PHP中内置的异常处理类Exception。分别在try区块中创建测试类TestException的对象,并根据构造方法中提供的不同数字参数,抛出自定义异常类对象、内置的异常类对象和不抛出任何异常的情况,跳转到对应的catch区块中执行。如果没有异常发生,则不会进入任何一个catch块中执行,测试类TestException的对象创建成功
希望本文所述对大家的php程序设计有所帮助。今天看啥 热点:
iOS开发中如何合理地制造BUG,ios开发bug什么是BUG,简单点说就是,程序没有按照我们预想的方式运行。我比较喜欢把BUG分成两类:
没有Crash掉的
可能在平时的编程实践中,往往简单的把BUG与Crash基本等价了。而且我们很多精力也都放在解决Crash的Bug上面。而对于没有Crash掉的BUG,似乎没有过多的关注。但是,实际情况上那些让人痛彻心扉的&天坑&往往是那些没有Crash掉的BUG造成的,比如前一段时间OpenSSL心脏大出血。为什么这么说呢?且听我慢慢道来。
如何合理地制造BUG
Crash掉的BUG,用程序的死证明了你的程序存在问题,你必须抓紧时间来解决程序的问题了。而没有Crash掉的Bug,像是一个善于撒谎的人,伪装成可以正常运转的样子,让整个程序运行在一个不稳定的状态下。虽然外表看起来好好地(没有crash),但是里子早就烂透了,一旦报露出问题往往是致命的,比如OpenSSL的心脏大出血。这就是前人总结的&死程序不说谎&。
Crash不可怕,可怕的是程序没有Crash而是运行在一个不稳定的状态下,如果程序还操作了数据,那带来的危害将是灾难性的。
所以放心的让程序Crash掉吧,因为当他Crash掉的时候,你还有机会去修正自己的错误。如果没有Crash,那就有可能要给整个程序和产品收尸了。因此合理制造&BUG&的原则之一,也是最大的原则就是:尽量制造Crash的BUG,减少没有Crash的BUG,如果有可能将没有Crash掉的Bug转换成Crash的BUG以方便查找。
这个应该都比较熟悉,他的名字叫做&断言&。断言(assertion)是指在开发期间使用的、让程序在运行时进行自检的代码(通常是一个子程序或宏)。断言为真,则表明程序运行正常,而断言为假,则意味着它已经在代码中发现了意料之外的错误。断言对于大型的复杂程序或可靠性要求极高的程序来说尤其有用。而当断言为假的时候,几乎所有的系统的处理策略都是,让程序死掉,即Crash掉。方便你知道,程序出现了问题。
断言其实是&防御式编程&的常用的手段。防御式编程的主要思想是:子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据。这种思想是将可能出现的错误造成的影响控制在有限的范围内。断言能够有效的保证数据的正确性,防止因为脏数据让整个程序运行在不稳定的状态下面。
关于如何使用断言,还是参考《代码大全2》中&防御式编程&一章。这里简单的做了一点摘录,概括其大意:
用错误处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况。
避免把需要执行的代码放到断言中
用断言来注解并验证前条件和后条件
对于高健壮性的代码,应该先使用断言再处理错误
对来源于内部系统的可靠的数据使用断言,而不要对外部不可靠的数据使用断言,对于外部不可靠数据,应该使用错误处理代码。 而在IOS编程中,我们可以使用NSAssert来处理断言。比如:
-&(void)printMyName:(NSString&*)myName&&
&&&&NSAssert(myName&==&nil,&@"名字不能为空!");&&
&&&&NSLog(@"My&name&is&%@.",myName);&&
我们验证myName的安全性,需要保证其不能为空。NSAssert会检查其内部的表达式的值,如果为假则继续执行程序,如果不为假让程序Crash掉。
每一个线程都有它自己的断言捕获器(一个NSAssertionHanlder的实例),当断言发生时,捕获器会打印断言信息和当前的类名、方法名等信息。然后抛出一个NSInternalInconsistencyException异常让整个程序Crash掉。并且在当前线程的断言捕获器中执行handleFailureInMethod:object:file:lineNumber:description:以上述信息为输出。
当时,当程序发布的时候,不能把断言带入安装包,你不想让程序在用户机器上Crash掉吧。打开和关闭断言可以在项目设置中设置assert ,在release版本中设置了NS_BLOCK_ASSERTIONS之后断言失效。
尽可能不要用Try-Catch
并不是说Try-Catch这样的异常处理机制不好。而是,很多人在编程中,错误了使用了Try-Catch,把异常处理机制用在了核心逻辑中。把其当成了一个变种的GOTO使用。把大量的逻辑写在了Catch中。弱弱的说一句,这种情况干嘛不用ifelse呢。
而实际情况是,异常处理只是用户处理软件中出现异常的情况。常用的情况是子程序抛出错误,让上层调用者知道,子程序发生了错误,并让调用者使用合适的策略来处理异常。一般情况下,对于异常的处理策略就是Crash,让程序死掉,并且打印出堆栈信息。
而在IOS编程中,抛出错误的方式,往往采用更直接的方式。如果上层需要知道错误信息,一半会传入一个NSError的指针的指针:
-&(void)&doSomething:(NSError*&__autoreleasing*)error
&&&&if(error&!=&NULL)
&&&&&&&&*error&=&[NSError&new];
而能够留给异常处理的场景就极少了,所以在IOS编程中尽量不要使用Try-Catch。
(PS:见到过使用Try-Catch来防止程序Crash的设计,如果不是迫不得已,尽量不要使用这种策略)
尽量将没有Crash掉的BUG,让它Crash掉
上面主要讲的是怎么知道Crash的&BUG&。对于合理的制造&BUG&还有一条就是尽量把没有Crash掉的&BUG&,让他Crash掉。这个没有比较靠谱的方法,靠暴力吧。比如写一些数组越界在里面之类的。比如那些难调的多线程BUG,想办法让他Crash掉吧,crash掉查找起来就比较方便了。
总之,就是抱着让程序&死掉&的心态去编程,向死而生。
如何查找BUG
其实查找BUG这个说法,有点不太靠谱。因为BUG从来都不需要你去找,他就在那里,只增不减。都是BUG来找你,你很少主动去找BUG。程序死了,然后我们就得加班加点。其实我们找的是发生BUG的原因。找到引发BUG的罪魁祸首。说的比较理论化一点就是:在一堆可能的原因中,找到那些与BUG有因果性的原因(注意,是因果性,不是相关性)。
于是解决BUG一般可以分两步进行:
合理性假设,找到可能性最高的一系列原因。
对上面找到的原因与BUG之间的因果性进行分析。必须确定,这个BUG是由某个原因引起的,而且只由改原因引起。即确定特定原因是BUG的充分必要条件。 找到原因之后,剩下的事情就比较简单了,改代码解决掉。
合理性假设
其实,BUG发生的原因可以分成两类:
我们自己程序的问题。
系统环境,包括OS、库、框架等的问题。 前者找到了,我们可以改。后者就比较无能为力了,要么发发牢骚,要么email开发商,最后能不能被改掉就不得而知了。比如IOS制作framework的时候,category会报方法无法找的异常,到现在都没有解决掉。
当然,一般情况下导致程序出问题的原因的99.999999%都是我们自己造成的。所以合理性假设第一条:
首先怀疑自己和自己的程序,其次怀疑一切
而程序的问题,其实就是开发者自己的问题。毕竟BUG是程序员的亲子亲孙,我们一手创造了BUG。而之所以能够创造BUG,开发者的原因大致有三:
知识储备不足,比如IOS常见的空指针问题,发现很多时候就是因为对于IOS的内存管理模型不熟悉导致。
错心大意,比较典型的就是数组越界错误。还有在类型转化的时候没注意。比如下面这个程序:
for&(int&i&=&100;&array.count&-&(unsigned&int)i&&&10&;&)
按道理讲,这应该是个可以正常执行的程序,但是你运行的话是个死循环。可能死循环的问题,你改了很多天也没解决。直到同事和你说array.count返回的是NSUInterge,当与无符号整形相间的时候,如果出现负值是回越界的啊。你才恍然大悟:靠,类型的问题。
这个就是思维方式的问题,但是也是问题最严重的。一旦发生,很难查找。人总是最难怀疑自己的思维方式。比如死循环的问题,最严重的是函数间的循环引用,还有多线程的问题。 但是庆幸的是绝大多数的BUG都是由于知识储备不足和粗心大意造成的。所以合理性假设的第二条:
首先怀疑基础性的原因,比如自己知识储备和粗心大意等人为因素,通过这些原因查找具体的问题。之后再去怀疑难处理的逻辑错误。 有了上面的合理性怀疑的一些基本策略,也不能缺少一些基本的素材啊。就是常见的Crash原因,最后我们还是得落地到这些具体的原因或者代码上,却找与BUG的因果性联系。
访问了一个已经被释放的对象,比如:NSObject * aObj = [[NSObject alloc] init]; [aObj release]; NSLog(@&%@&, aObj);&
访问数组类对象越界或插入了空对象
访问了不存在的方法
字节对齐,(类型转换错误)
多线程并发操作
Repeating NSTimer
合理性假设第三条:尽可能的查找就有可能性的具体原因。
因果性分析
首先必须先说明的是,我们要找的是&因果性&而不是&相关性&。这是两个极度被混淆的概念。而且,很多时候我们错误的把相关性当成了因果性。比如,在解决一个多线程问题的时候,发现了一个数据混乱的问题,但是百思不得其解。终于,有一天你意外的给某个对象加了个锁,数据就正常了。然后你就说这个问题是这个对象没有枷锁导致的。
但是,根据上述你的分析,只能够得出该对象枷锁与否与数据异常有关系,而不能得出就是数据异常的原因。因为你没能证明对象加锁是数据异常的充分必要条件,而只是使用了一个单因变量实验,变量是枷锁状态,取值x=[0,1],x为整形。然后实验结果是枷锁与否与数据异常呈现正相关性。
相关性:在概率论和统计学中,相关(Correlation,或称相关系数或关联系数),显示两个随机变量之间线性关系的强度和方向。在统计学中,相关的意义是用来衡量两个变量相对于其相互独立的距离。在这个广义的定义下,有许多根据数据特点而定义的用来衡量数据相关的系数。
因果性:因果是一个事件(即&因&)和第二个事件(即&果&)之间的关系,其中后一事件被认为是前一事件的结果。 错误的把相关性等价于因果性。不止是程序员,几乎所有人常见的逻辑错误。为了加深认识,可以看一下这篇小科普:相关性 & 因果性。
因果性分析的首要问题就是,别被自己的逻辑错误欺骗,正确的分辨出相关性和因果性之间的区别。不要把相关性等价于因果性。
之后便是因果性分析的内容了,之前一直反复说,因果性分析的目的就是确定特定原因是BUG发生的充分必要条件。那么确定这个事情,就需要两步:
充分性证明
必要性证明
关于充分性证明,这个基本上就是正常的逻辑推理。基本思路就是,能够还原出BUG出现的路径,从原因到BUG发生处的代码,走了怎样的函数调用和控制逻辑。确定了这个基本上就能够证明充分性。一般情况下根据Crash的堆栈信息能够,非常直接的证明充分性。
关于必要性证明,这个就比较困难了。充分性和必要性的定义如下:当命题&若A则B&为真时,A称为B的充分条件,B称为A的必要条件。那么必要性就是,BUG能够作为导致BUG的原因的原因。这个说法比较拗口。换种说法,就是你得确认这个BUG能够解释原因,这个BUG就是而且只是这个原因造成的。
只有证明了充分必要性,才能算是真正找到了BUG的原因。
问啊-一键呼叫程序员答题神器,牛人一对一服务,开发者编程必备官方网站:
QQ群 聚集很多互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!
暂无相关文章
相关搜索:
相关阅读:
相关频道:
IOS教程最近更新今天看啥 热点:
拣了个iPad,查了下没出保,未越狱,现在有点故障了,能拿去修吗,需要什么东西吗,问了下别人,说维修时好像要填个什么单子,好象有购买地点,有的话填什么,这个单子都要填写些什么内容类,答的详细的话在100分的基础上酌情加分
售后服务是不维修的,补差价换新机。只有去大点的手机修理店去修,不知道你在哪,上海我在不夜城修过。其实IPAD设计就是一次性使用的,维修太麻烦了,因为外表面没有一颗螺丝,要用电烙铁烤溶化屏幕的胶才能打开,内部也是如此,电池也是粘在底壳上,拆卸维修很是麻烦,即便是修复,再安装起来严密性也不好了。所以,若不是很大毛病就凑合着用吧!追问
没有出保 回答
看是什么故障了,人为因素比如摔,进水什么都不保的。机器本身故障的话好办,直接拿去售后换新机,发票什么都不需要提供,他们全球联保,只认序列号。 追问
单子类,要填不,填的话怎么办 回答
随便留个名字电话,又不查身份证.明白你的顾虑,这种可能性还是有。 追问
购买地址填什么 回答
朋友送的,抽奖抽的,买房买车还赠送呢。。。 提问者评价谢谢,这么晚了还给我解答
&& iPad是一款苹果公司于日发表的平板电脑,定位介于苹果的智能手机iPhone和笔记本电脑产品(MacBook、MacBook Pro与MacBook Air)之间,提供浏览互联网、收发电子邮件、观看电子书、播放音频或视频等功能。iPad有一个9.7吋的IPS显示器,厚度为0.5吋,重量为1.5磅。与使用英特尔处理器的苹果公司其他电脑产品不同,iPad使用了苹果公司自家的Apple A4 1GHz处理芯片。iPad也支持多点触控,内置16至64GB的闪存。电池可提供10小时的续航使用时间,以及最长达一个月的待机能力。   通信能力方面,iPad支持WiFi 802.11n规格的无线网络,以及蓝牙(Bluetooth)2.1,iPad亦具备内置3G模块的版本。另外,iPad同时内置动态感应器、电子罗盘、喇叭、麦克风。3G版本的iPad更具备gps模块,可以提供更精准和快速的导航。   由于iPad与iPhone采用同样的操作系统iOS,因此可直接运行所有iPhone的应用程序,推出时并没有支持Adobe Flash。   iPad分为支持WiFi和支持WiFi+3G两个版本,有16GB、32GB和64GB三种容量大小,支持WiFi版的16GB容量的iPad最低售价为499美元。已于今年4月份在美国上市,预计于今年五月在其他国家上市。- -!苹果公司出的 平板电脑类别不是手机,就是平板电脑说掌上电脑也成
填单子也只要填你的信息就好了,姓名,住址,电话之类的。ipad一般不能修,只能换,没过保的话可以直接换一个。
维修的话要在网上预约
要填单子吗,单子内容 回答
不需要啊,保修期过了没
哪里捡的??
去淘宝吧,那里有很多的,性价比很高的
http://ishare..cn/f/.html?from=zhishi相关资料: iphone/ipad/ipod破解越狱软件绿毒越狱是指开放用户的操作权限,使得用户可以随意擦写任何区域的  运行状态,只有越狱成功后iphone的iphone(1张)文件系统才处于可读写(rw)状态,可以安装和运行未经过官方认证的第三方程序、插件。  这些软件或者是苹果商店因为某种理由而拒绝的(通常是因为违反了苹果的清规戒律)或者开发者没有MAC系统,也可能是作者拒绝付100美元才能发布他们的作品。而越狱后就能够使用这些软件了。  iPhone的iOS与其他手机系统(如Nokia的Symbian,Google的Android等等最大的不同是,后两者是开放的用户权限,而iOS用户权限极低。简单的说,后两者用户的权限是RW(读写),而iOS的用户权限只有R(只读)。因此iOS的用户只能使用经过苹果验证(Apple Store中购买的)的应用程序,其他行为对无法对系统进行更变. iOS这样的好处是,对比其他系统如Nokia Symbian、Android等iso系统不越狱的情况下稳定性极强,死机率极低...  (用过Nokia Symbian的用户应该都有过死机拔电池的经历..)
相关搜索:
相关阅读:
相关频道:
平板电脑最近更新今天看啥 热点:
在AngularJS框架中处理数据建模的方式解析,angularjs框架我们知道,AngularJS并没有自带立等可用的数据建模方案。而是以相当抽象的方式,让我们在controller中使用JSON数据作为模型。但是随着时间的推移和项目的成长,我意识到这种建模的方式不再能满足我们项目的需求。在这篇文章中我会介绍在我的AngularJS应用中处理数据建模的方式。
为Controller定义模型
让我们从一个简单的例子开始。我想要显示一个书本(book)的页面。下面是控制器(Controller):
BookController
app.controller('BookController', ['$scope', function($scope) {
$scope.book = {
name: 'Harry Potter',
author: 'J. K. Rowling',
{ id: 1, name: 'Barnes & Noble', quantity: 3},
{ id: 2, name: 'Waterstones', quantity: 2},
{ id: 3, name: 'Book Depository', quantity: 5}
这个控制器创建了一个书本的模型,我们可以在后面的模板中(templage)中使用它。
template for displaying a book
&div ng-controller="BookController"&
Id: &span ng-bind="book.id"&&/span&
Name:&input type="text" ng-model="book.name" /&
Author: &input type="text" ng-model="book.author" /&
假如我们需要从后台的api获取书本的数据,我们需要使用$http:
BookController with $http
app.controller('BookController', ['$scope', '$http', function($scope, $http) {
var bookId = 1;
$http.get('ourserver/books/' + bookId).success(function(bookData) {
$scope.book = bookD
注意到这里的bookData仍然是一个JSON对象。接下来我们想要使用这些数据做一些事情。比如,更新书本信息,删除书本,甚至其他的一些不涉及到后台的操作,比如根据请求的图片大小生成一个书本图片的url,或者判断书本是否有效。这些方法都可以被定义在控制器中。
BookController with several book actions
app.controller('BookController', ['$scope', '$http', function($scope, $http) {
var bookId = 1;
$http.get('ourserver/books/' + bookId).success(function(bookData) {
$scope.book = bookD
$scope.deleteBook = function() {
$http.delete('ourserver/books/' + bookId);
$scope.updateBook = function() {
$http.put('ourserver/books/' + bookId, $scope.book);
$scope.getBookImageUrl = function(width, height) {
return 'our/image/service/' + bookId + '/width/height';
$scope.isAvailable = function() {
if (!$scope.book.stores || $scope.book.stores.length === 0) {
return $scope.book.stores.some(function(store) {
return store.quantity & 0;
然后在我们的模板中:
template for displaying a complete book
&div ng-controller="BookController"&
&div ng-style="{ backgroundImage: 'url(' + getBookImageUrl(100, 100) + ')' }"&&/div&
Id: &span ng-bind="book.id"&&/span&
Name:&input type="text" ng-model="book.name" /&
Author: &input type="text" ng-model="book.author" /&
Is Available: &span ng-bind="isAvailable() ? 'Yes' : 'No' "&&/span&
&button ng-click="deleteBook()"&Delete&/button&
&button ng-click="updateBook()"&Update&/button&
在controllers之间共享Model
如果书本的结构和方法只和一个控制器有关,那我们现在的工作已经可以应付。但是随着应用的增长,会有其他的控制器也需要和书本打交道。那些控制器很多时候也需要获取书本,更新它,删除它,或者获得它的图片url以及看它是否有效。因此,我们需要在控制器之间共享这些书本的行为。我们需要使用一个返回书本行为的factory来实现这个目的。在动手写一个factory之前,我想在这里先提一下,我们创建一个factory来返回带有这些book辅助方法的对象,但我更倾向于使用prototype来构造一个Book类,我觉得这是更正确的选择:
Book model service
app.factory('Book', ['$http', function($http) {
function Book(bookData) {
if (bookData) {
this.setData(bookData):
// Some other initializations related to book
Book.prototype = {
setData: function(bookData) {
angular.extend(this, bookData);
load: function(id) {
var scope =
$http.get('ourserver/books/' + bookId).success(function(bookData) {
scope.setData(bookData);
delete: function() {
$http.delete('ourserver/books/' + bookId);
update: function() {
$http.put('ourserver/books/' + bookId, this);
getImageUrl: function(width, height) {
return 'our/image/service/' + this.book.id + '/width/height';
isAvailable: function() {
if (!this.book.stores || this.book.stores.length === 0) {
return this.book.stores.some(function(store) {
return store.quantity & 0;
这种方式下,书本相关的所有行为都被封装在Book服务内。现在,我们在BookController中来使用这个亮眼的Book服务。
BookController that uses Book model
app.controller('BookController', ['$scope', 'Book', function($scope, Book) {
$scope.book = new Book();
$scope.book.load(1);
正如你看到的,控制器变得非常简单。它创建一个Book实例,指派给scope,并从后台加载。当书本被加载成功时,它的属性会被改变,模板也随着被更新。记住其他的控制器想要使用书本功能,只要简单地注入Book服务即可。此外,我们还要改变template使用book的方法。
template that uses book instance
&div ng-controller="BookController"&
&div ng-style="{ backgroundImage: 'url(' + book.getImageUrl(100, 100) + ')' }"&&/div&
Id: &span ng-bind="book.id"&&/span&
Name:&input type="text" ng-model="book.name" /&
Author: &input type="text" ng-model="book.author" /&
Is Available: &span ng-bind="book.isAvailable() ? 'Yes' : 'No' "&&/span&
&button ng-click="book.delete()"&Delete&/button&
&button ng-click="book.update()"&Update&/button&
到这里,我们知道了如何建模一个数据,把他的方法封装到一个类中,并且在多个控制器中共享它,而不需要写重复代码。
在多个控制器中使用相同的书本模型
我们定义了一个书本模型,并且在多个控制器中使用了它。在使用了这种建模架构之后你会注意到有一个严重的问题。到目前为止,我们假设多个控制器对书本进行操作,但如果有两个控制器同时处理同一本书会是什么情况呢?
假设我们页面的一块区域我们所有书本的名称,另一块区域可以更新某一本书。对应这两块区域,我们有两个不同的控制器。第一个加载书本列表,第二个加载特定的一本书。我们的用户在第二块区域中修改了书本的名称并且点击“更新”按钮。更新操作成功后,书本的名称会被改变。但是在书本列表中,这个用户始终看到的是修改之前的名称!真实的情况是我们对同一本书创建了两个不同的书本实例——一个在书本列表中使用,而另一个在修改书本时使用。当用户修改书本名称的时候,它实际上只修改了后一个实例中的属性。然而书本列表中的书本实例并未得到改变。
解决这个问题的办法是在所有的控制器中使用相同的书本实例。在这种方式下,书本列表和书本修改的页面和控制器都持有相同的书本实例,一旦这个实例发生变化,就会被立刻反映到所有的视图中。那么按这种方式行动起来,我们需要创建一个booksManager服务(我们没有大写开头的b字母,是因为这是一个对象而不是一个类)来管理所有的书本实例池,并且富足返回这些书本实例。如果被请求的书本实例不在实例池中,这个服务会创建它。如果已经在池中,那么就直接返回它。请牢记,所有的加载书本的方法最终都会被定义在booksManager服务中,因为它是唯一的提供书本实例的组件。
booksManager service
app.factory('booksManager', ['$http', '$q', 'Book', function($http, $q, Book) {
var booksManager = {
_pool: {},
_retrieveInstance: function(bookId, bookData) {
var instance = this._pool[bookId];
if (instance) {
instance.setData(bookData);
instance = new Book(bookData);
this._pool[bookId] =
_search: function(bookId) {
return this._pool[bookId];
_load: function(bookId, deferred) {
var scope =
$http.get('ourserver/books/' + bookId)
.success(function(bookData) {
var book = scope._retrieveInstance(bookData.id, bookData);
deferred.resolve(book);
.error(function() {
deferred.reject();
/* Public Methods */
/* Use this function in order to get a book instance by it's id */
getBook: function(bookId) {
var deferred = $q.defer();
var book = this._search(bookId);
if (book) {
deferred.resolve(book);
this._load(bookId, deferred);
return deferred.
/* Use this function in order to get instances of all the books */
loadAllBooks: function() {
var deferred = $q.defer();
var scope =
$http.get('ourserver/books)
.success(function(booksArray) {
var books = [];
booksArray.forEach(function(bookData) {
var book = scope._retrieveInstance(bookData.id, bookData);
books.push(book);
deferred.resolve(books);
.error(function() {
deferred.reject();
return deferred.
/* This function is useful when we got somehow the book data and we wish to store it or update the pool and get a book instance in return */
setBook: function(bookData) {
var scope =
var book = this._search(bookData.id);
if (book) {
book.setData(bookData);
book = scope._retrieveInstance(bookData);
return booksM
下面是我们的EditableBookController和BooksListController两个控制器的代码:
EditableBookController and BooksListController that uses booksManager
app.factory('Book', ['$http', function($http) {
function Book(bookData) {
if (bookData) {
this.setData(bookData):
// Some other initializations related to book
Book.prototype = {
setData: function(bookData) {
angular.extend(this, bookData);
delete: function() {
$http.delete('ourserver/books/' + bookId);
update: function() {
$http.put('ourserver/books/' + bookId, this);
getImageUrl: function(width, height) {
return 'our/image/service/' + this.book.id + '/width/height';
isAvailable: function() {
if (!this.book.stores || this.book.stores.length === 0) {
return this.book.stores.some(function(store) {
return store.quantity & 0;
需要注意的是,模块(template)中还是保持原来使用book实例的方式。现在应用中只持有一个id为1的book实例,它发生的所有改变都会被反映到使用它的各个页面上。
AngularJS 中的一些坑
Angular的自动数据绑定功能是亮点,然而,他的另一面是:在Angular初始化之前,页面中可能会给用户呈现出没有解析的表达式。当DOM准备就绪,Angular计算并替换相应的值。这样就会导致出现一个丑陋的闪烁效果。
上述情形就是在Angular教程中渲染示例代码的样子:
&body ng-controller="PhoneListCtrl"&
&li ng-repeat="phone in phones"&
{{ phone.name }}
&p&{{ phone.snippet }}&/p&
如果你做的是SPA(Single Page Application),这个问题只会在第一次加载页面的时候出现,幸运的是,可以很容易杜绝这种情形发生: 放弃{{ }}表达式,改用ng-bind指令
&body ng-controller="PhoneListCtrl"&
&li ng-repeat="phone in phones"&
&span ng-bind="phone.name"&&/span&
&p ng-bind="phone.snippet"&Optional: visually pleasing placeholder&/p&
你需要一个tag来包含这个指令,所以我添加了一个&span&给phone name.
那么初始化的时候会发生什么呢,这个tag里的值会显示(但是你可以选择设置空值).然后,当Angular初始化并用表达式结果替换tag内部值,注意你不需要在ng-bind内部添加大括号。更简洁了!如果你需要符合表达式,那就用ng-bind-template吧,
如果用这个指令,为了区分字符串字面量和表达式,你需要使用大括号
另外一种方法就是完全隐藏元素,甚至可以隐藏整个应用,直到Angular就绪。
Angular为此还提供了ng-cloak指令,工作原理就是在初始化阶段inject了css规则,或者你可以包含这个css 隐藏规则到你自己的stylesheet。Angular就绪后就会移除这个cloak样式,让我们的应用(或者元素)立刻渲染。
Angular并不依赖jQuery。事实上,Angular源码里包含了一个内嵌的轻量级的jquery:jqLite. 当Angular检测到你的页面里有jQuery出现,他就会用这个jQuery而不再用jqLite,直接证据就是Angular里的元素抽象层。比如,在directive中访问你要应用到的元素。
angular.module('jqdependency', [])
.directive('failswithoutjquery', function() {
restrict : 'A',
link : function(scope, element, attrs) {
element.hide(4000)
但是这个元素jqLite还是jQuery元素呢?取决于,手册上这么写的:
Angular中所有的元素引用都会被jQuery或者jqLite包装;他们永远不是纯DOM引用
所以Angular如果没有检测到jQuery,那么就会使用jqLite元素,hide()方法值能用于jQuery元素,所以说这个示例代码只能当检测到jQuery时才可以使用。如果你(不小心)修改了AngularJS和jQuery的出现顺序,这个代码就会失效!虽说没事挪脚本的顺序的事情不经常发生,但是在我开始模块化代码的时候确实给我造成了困扰。尤其是当你开始使用模块加载器(比如 RequireJS), 我的解决办法是在配置里显示的声明Angular确实依赖jQuery
另外一种方法就是你不要通过Angular元素的包装来调用jQuery特定的方法,而是使用$(element).hide(4000)来表明自己的意图。这样依赖,即使修改了script加载顺序也没事。
特别需要注意的是Angular应用压缩问题。否则错误信息比如 ‘Unknown provider:aProvider& &- a' 会让你摸不到头脑。跟其他很多东西一样,这个错误在官方文档里也是无从查起的。简而言之,Angular依赖参数名来进行依赖注入。压缩器压根意识不到这个这跟Angular里普通的参数名有啥不同,尽可能的把脚本变短是他们职责。咋办?用“友好压缩法”来进行方法注入。看这里:
module.service('myservice', function($http, $q) {
// This breaks when minified
module.service('myservice', [ '$http', '$q', function($http, $q) {
// Using the array syntax to declare dependencies works with minification&b&!&/b&
这个数组语法很好的解决了这个问题。我的建议是从现在开始照这个方法写,如果你决定压缩JavaScript,这个方法可以让你少走很多弯路。好像是一个automatic rewriter机制,我也不太清楚这里面是怎么工作的。
最终一点建议:如果你想用数组语法复写你的functions,在所有Angular依赖注入的地方应用之。包括directives,还有directive里的controllers。别忘了逗号(经验之谈)
// the directive itself needs array injection syntax:
module.directive('directive-with-controller', ['myservice', function(myservice) {
controller: ['$timeout', function($timeout) {
// but this controller needs array injection syntax, too!
link : function(scope, element, attrs, ctrl) {
注意:link function不需要数组语法,因为他并没有真正的注入。这是被Angular直接调用的函数。Directive级别的依赖注入在link function里也是使用的。
&Directive永远不会‘完成'
在directive中,一个令人掉头发的事就是directive已经‘完成'但你永远不会知道。当把jQuery插件整合到directive里时,这个通知尤为重要。假设你想用ng-repeat把动态数据以jQuery datatable的形式显示出来。当所有的数据在页面中加载完成后,你只需要调用$(‘.mytable).dataTable()就可以了。 但是,臣妾做不到啊!
为啥呢?Angular的数据绑定是通过持续的digest循环实现的。基于此,Angular框架里根本没有一个时间是‘休息'的。 一个解决方法就是将jQuery dataTable的调用放在当前digest循环外,用timeout方法就可以做到。
angular.module('table',[]).directive('mytable', ['$timeout', function($timeout) {
restrict : 'E',
template: '&table class="mytable"&' +
'&thead&&tr&&th&counting&/th&&/tr&&/thead&' +
'&tr ng-repeat="data in datas"&&td&&/td&&/tr&' +
'&/table&',
link : function(scope, element, attrs, ctrl) {
scope.datas = ["one", "two", "three"]
// Doesn't work, shows an empty table:
// $('.mytable', element).dataTable()
// But this does:
$timeout(function() {
$('.mytable', element).dataTable();
您可能感兴趣的文章:实例剖析AngularJS框架中数据的双向绑定运用详解JavaScript的AngularJS框架中的作用域与数据绑定深入学习AngularJS中数据的双向绑定机制AngularJS数据源的多种获取方式汇总三种AngularJS中获取数据源的方式AngularJS中如何使用$http对MongoLab数据表进行增删改查基于AngularJS实现页面滚动到底自动加载数据的功能angularjs学习笔记之双向数据绑定
相关搜索:
相关阅读:
相关频道:
&&&&&&&&&&&&&&&&&&
Javascript最近更新}

我要回帖

更多关于 异常情况处理制度 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信