关键字‘基础知识’的相关文章

js浮点数运算的bug

老实说在今天之前,我对javascript的浮点数运算毫无认识,觉得应该和实际运算中一样,然而,有些事情往往会出人意料。

先看一组数据:
0.1 + 0.11 = 0.21000000000000002
0.2 * 3 = 0.6000000000000001
0.7 / 10 = 0.06999999999999999

你相信吗?如果不信,那么立即打开firefox浏览器测试吧。有一点可以确信,你看到测试结果的表情,一定先是惊讶,后来是迷惑。

这里有几个demo,再来看看出现浮点数运算bug的概率,猛击之:加法乘法除法

对于这样的结果,我不知道是怎么产生的,但是既然是bug,就一定是可以hack的,思路很简单,先把所有的运算数转成int型(表达不准确,javascript的没有int、float之分),计算出两个运算数的小数点后的位数,再进行适当的运算,就可以得到原始的我们期望看到的结果了。代码如下:

加法运算:

  1. function add(num1,num2){
  2.         var reg = /\./i;
  3.     if(!reg.test(num1) && !reg.test(num2)){
  4.         return num1 * num2;
  5.     }
  6.     var r1 = 0, r2 = 0, m;
  7.     var str1 = num1.toString(), str2 = num2.toString();
  8.     if(str1.indexOf('.')>-1){
  9.         r1 = str1.split('.')[1].length;
  10.     }
  11.     if(str2.indexOf('.')>-1){
  12.         r2 = str2.split('.')[1].length;
  13.     }
  14.     m = Math.pow(10,Math.max(r1,r2));
  15.     return (mul(num1,m) + mul(num2,m)) / m;
  16. }

乘法运算:

  1. function mul(num1,num2){
  2.         var reg = /\./i;
  3.     if(!reg.test(num1) && !reg.test(num2)){
  4.         return num1 * num2;
  5.     }
  6.     var len = 0, str1 = num1.toString(), str2 = num2.toString();
  7.     if(str1.indexOf('.')>=0){
  8.         len += str1.split('.')[1].length;
  9.     }
  10.     if(str2.indexOf('.')>=0){
  11.         len += str2.split('.')[1].length;
  12.     }
  13.     return Number(str1.replace('.','')) * Number(str2.replace('.','')) / Math.pow(10,len);
  14. }

除法运算:

  1. function div(num1,num2){
  2.         var reg = /\./i;
  3.     if(!reg.test(num1) && !reg.test(num2)){
  4.         return num1 * num2;
  5.     }
  6.     var len1 = 0, len2 = 0;
  7.     var str1 = num1.toString(), str2 = num2.toString();
  8.     //计算位数差
  9.     if(str1.indexOf('.')>-1){
  10.         len1 = str1.split('.')[1].length;
  11.     }
  12.     if(str2.indexOf('.')>-1){
  13.         len2 = str2.split('.')[1].length;
  14.     }
  15.     return mul(Number(str1.replace('.','')) / Number(str2.replace('.','')),Math.pow(10,len2-len1));
  16. }

巧用”&&”和”||”

对于&&和||运算符,相信大家都用过,分别进行逻辑与运算和逻辑或运算符。如果&&的第一个运算数是false,则不再考虑第二个运算数,直接返回false。如果||的第一个运算数是true,那么也不再考虑第二个运算数。

没有实践就没有发言权,demo如下:

  1. var a = true, b = false;
  2. function test(param){
  3.   alert(param);
  4. }
  5. a && test(1);
  6. b && test(2);
  7. a || test(3);
  8. b || test(4);

执行如上代码,事实上只执行了test(1)和test(4),原理如上所述。

a&&test(1)这种运算,其实我不知道是否有个专业的名词去描述它,在javascript森林群ToFishes同学称之为“惰性运算”,那么暂且叫它“惰性运算”吧,实际上相当于if(a){test(1)}。

其中||运算符,有个很重要的应用就是给function的参数设置默认值,如:

  1. function test(param){
  2.     param = {
  3.         name: param.name || "zhenn",
  4.         age: param.age || 25
  5.     }
  6.     alert(param.name + " is " + param.age + " years old.")
  7. }
  8. test({});

以上内容,针对刚入门javascript的童鞋们,大湿级的淫们可以直接skip.

关于javascript作用域

今天一个挚友给我出了道javascript笔试题,代码如下:

  1. function test(){
  2.     var a = b = 2;
  3. }
  4. test();
  5. alert(b);
  6. alert(a);

我看到这段代码,我笑了,很自信的回答道,会报错,因为a,b都是局部变量,所以a and b is not defined。然后我朋友笑了,迷惑ing,难道我错了吗?迟疑了10几秒,原来a是局部变量,而b没有在test中声明,故而是全局变量,很显然,在这种情况下,执行情况应该是b=2,a is not defined;后来想想,自己还真是缺乏点思维的严谨性啊,特发此贴,以儆效尤!

javascript设计模式学习笔记之“类式继承”

在做一件事情之前,首先要清楚做这件事情的好处有什么,相信不会有哪个人愿意无缘无故的去做事情。一般说来,我们在设计类的时候,实际上就是希望能减少重复性的代码,使用继承可以完美的做到这一点,借助继承机制,你可以在现有类的基础上再次进行设计并且充分利用它们已经具备的各种方法,而对设计的修改也更为轻松。废话不多说了,举例说明:

  1. function Person(name){
  2.     this.name = name;
  3. }
  4. Person.prototype.getname = function(){
  5.     return this.name;
  6. }
  7.  
  8. function Bloger(name,blog){
  9.     Person.call(this,name);
  10.     this.blog = blog;
  11. }
  12. var bloger = new Bloger("zhenn","http://www.men-ideal.com");
  13. alert(bloger.name=="zhenn");   /*返回ture*/
  14. alert(bloger.blog)   /*提示http://www.men-ideal.com*/
  15. alert(bloger.getname()=="zhenn");   /*提示"bloger.getname is not a function"*/

通过上例可以看到,Bloger在其内部通过call动态调用了其父类Person的原生属性和方法(关于call的讲解请参考),即可以理解为Bloger继承了Person,成为它的一个子类,但是细心的同学会发现,Person原型对象中的方法,仅仅依靠call,是不能继承过来的,这也就是会提示”bloger.getname is not a function”的原因所在了。不过不用担心,对上述代码稍作处理,即可解决这个问题!

  1. function Person(name){
  2.     this.name = name;
  3. }
  4. Person.prototype.getname = function(){
  5.     return this.name;
  6. }
  7.  
  8. function Bloger(name,blog){
  9.     Person.call(this,name);
  10.     this.blog = blog;
  11. }
  12. /*请注意以下两行代码*/
  13. Bloger.prototype = new Person();  
  14. Bloger.prototype.constructor = Bloger;
  15.  
  16. var bloger = new Bloger("zhenn","http://www.men-ideal.com");
  17. alert(bloger.name=="zhenn");   /*返回ture*/
  18. alert(bloger.blog)   /*提示http://www.men-ideal.com*/
  19. alert(bloger.getname()=="zhenn");   /*提示true*/

在这里需要对这两行代码解释一下,我们知道,每一个构造函数都有一个prototype属性,这个属性指向该构造函数的原型对象,其实原型对象也是实例对象,只不过在原型对象中定义的属性和方法可以提供给所有的实例对象共享,由此可以得出,新添加两行代码的意图就是设置子类的原型对象指向父类的一个实例化对象,而父类的实例化对象会把父类的原型属性方法统统继承过来,这样也就达到了我们的目的,子类的原型继承了所有父类实例对象具有的属性和方法。

但是还应该注意Bloger.prototype.constructor = Bloger;这行代码,因为把子类的prototype设置为父类的实例时,其constructor属性会指向父类,所以要设置子类原型的constructor重新指向子类,至此,已经完美实现了javascript的类式继承!

为了简化子类的声明,可以把扩展子类的整个过程写在一个名为extend的函数中,作用就是基于一个给定的类结构去创建一个新的类:

  1. function extend(childClass,parentClass){
  2.     var F = new Function();
  3.     F.prototype = parentClass.prototype;
  4.     childClass.prototype = new F();
  5.     childClass.prototype.constructor = childClass;
  6. }

有了这个extend这个函数,就可以很方便的扩展子类了,只需调用这个函数即可,上述添加的两行代码可改为extend(Bloger,Person), 一样可以实现完全继承!

神奇的call()

先看看关于call()的官方解释,“调用一个对象的一个方法,以另一个对象替换当前对象。”,看了这样的解释,或许让你更摸不着头脑了。看例子:

  1. var x = "我是全局变量";    //定义全局变量x
  2. function a(){         //定义函数类结构a  
  3.     this.x = "我是在函数类结构a中声明的哦";   
  4. }
  5. //定义普通函数,弹出当前指针所包含的变量x的值
  6. function f(){      
  7.     alert (this.x);
  8. }
  9. //返回值为“我是在函数类结构a中声明的哦”
  10. f.call(new a());

我的理解是,f.call(new a())就是把函数(其实也是对象)f复制到被调用对象“new a()”下面去解析,事实上和下面这段代码的解析结果一样:

  1. function a(){
  2.   this.x = "我是在函数类结构a中声明的哦";
  3.   alert(this.x);   
  4. }
  5. a();

只不过此时变量X的作用域不同而已,咿…看起来好像有点继承的味道哦,难道不是吗?在上例中,f完全被构造函数a的实力对象继承了,如果说这还不足以说明a.call(b)是一种继承模式,那么再看一个更具有继承味道的用法吧。

  1. function f(){   
  2.     this.a ="a";   
  3.     this.b = function(){   
  4.         alert("b");
  5.     }
  6. }
  7. function e(){   
  8.     f.call(this);    
  9. }
  10. var c = new e();
  11. alert(c.a)//弹出a
  12. c.b();    //弹出b

在这个例子中,只要会使用浏览器的朋友,都能看得出来e完全继承了f的属性和方法,否则是无法解释的,因为在e中并没有定义属性a和b,那么按常理推断在e的实例对象c中,并不会出现这两个属性。

return false的用法

在大多数情况下,为事件处理函数返回false,可以防止默认的事件行为。

例如,默认情况下点击一个<a>元素,页面会跳转到该元素href属性指定的页。 Return False 就相当于终止符Return True 就相当于执行符。在jsreturn false的作用一般是用来取消默认动作的。比如你单击一个链接除了触发你的onclick事件(如果你指定的话)以外还要触发一个默认事件就是执行页面的跳转。所以如果你想取消对象的默认动作就可以return false。

同样的,return false可以应用于表单的提交,比如,我们通常在验证表单的时候,要在客户端判断信息是否属实,是否漏填信息,如果符合上面任一种的条件,那么我们需要,在客户端输出提示信息,而并不会提交至服务器处理,return flase正好能在此处大显身手  – -!