读高性能JavaScript编程 第四章 Conditionals

if else 和 switch    &&    递归

  • if else 和 switch

  一般来说,if-else 适用于判断两个离散的值或者判断几个不同的值域。如果判断多于两个离散值,switch
表达式将是更理想的选择。

  如同 我们在写sql 总习惯把可以过滤掉更多的where 放在前面一样,当 if else 判断的离散值较多时也因该这么干。

  二分搜索法:

 if (value == 0){  //要优化的代码
  return result0;
} else if (value == 1){
  return result1;
} else if (value == 2){
  return result2;
} else if (value == 3){
  return result3;
} else if (value == 4){
  return result4;
} else if (value == 5){
  return result5;
} else if (value == 6){
  return result6;
} else if (value == 7){
  return result7;
} else if (value == 8){
  return result8;
} else if (value == 9){
  return result9;
} else {
  return result10;
} 
 if (value < 6){ //二分搜索法
  if (value < 3){
    if (value == 0){
      return result0;
    } else if (value == 1){
      return result1;
    } else {
      return result2;
    }
  } else {
    if (value == 3){
      return result3;
    } else if (value == 4){
      return result4;
    } else {
      return result5;
    }
  }
} else {
  if (value < 8){
    if (value == 6){
      return result6;
    } else {
      return result7;
    }
  } else {
    if (value == 8){
      return result8;
    } else if (value == 9){
      return result9;
    } else {
      return result10;
    }
  }
} 

代码很简单 ,效果却很明显.

  查表法:

 //define the array of results
var results = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10]
//return the correct result
return results[value]; 

更加简单暴力。

  • 递归

引用:

  JavaScript引擎所支持的递归数量与 JavaScript调用栈大小直接相关。只有 Internet Explorer例外,它的
调用栈与可用系统内存相关,其他浏览器有固定的调用栈限制。大多数现代浏览器的调用栈尺寸比老式浏
览器要大(例如 Safari 2调用栈尺寸是 100)。图 4-2 显示出主流浏览器的调用栈大小。

关于调用栈溢出错误,最令人感兴趣的部分大概是:在某些浏览器中,他们的确是 JavaScript 错误,可
以用一个 try-catch 表达式捕获。异常类型因浏览器而不同。在 Firefox 中,它是一个 InternalError;在 Safari
和 Chrome 中,它是一个 RangeError;在 Internet Explorer中抛出一个一般性的 Error类型。(Opera 不抛出
错误;它终止 JavaScript引擎)。这使得我们能够在 JavaScript 中正确处理这些错误:
try {
  recurse();
} catch (ex){
  alert("Too much recursion!");
}

  如果不管它,那么这些错误将像其他错误一样冒泡上传(在 Firefox中,它结束于 Firebug 和错误终端;
在 Safari/Chrome 中它显示在 JavaScript 终端上),只有 Internet Explorer例外。IE 不会显示一个 JavaScript
错误,但是会弹出一个提示堆栈溢出信息的对话框。

  大多数调用栈错误与递归有关。常见的栈溢出原因是一个不正确的终止条件,所以定位模式
错误的第一步是验证终止条件。如果终止条件是正确的,那么算法包含了太多层递归,为了能够安全地在
浏览器中运行,应当改用迭代,制表,或两者兼而有之。

  任何可以用递归实现的算法都可以用迭代实现。迭代算法通常包括几个不同的循环,分别对应算法过程

的不同方面,也会导致自己的性能为题。但是,使用优化的循环替代长时间运行的递归函数可以提高性能,
因为运行一个循环比反复调用一个函数的开销要低。

优化递归的方案 制表

  一个经典的案例:

 function factorial(n){
  if (n == 0){
    return 1;
  } else {
    return n * factorial(n-1);
  }
} 
var fact6 = factorial(6); //factorial 计算阶乘
var fact5 = factorial(5);
var fact4 = factorial(4); 

  此代码生成三个阶乘结果,factorial()函数总共被调用了 18次。此代码中最糟糕的部分是,所有必要的
计算已经在第一行代码中执行过了。因为 6 的阶乘等于 6乘以 5 的阶乘,所以 5的阶乘被计算了两次。更
糟糕的是,4的阶乘被计算了三次。更为明智的方法是保存并重利用它们的计算结果,而不是每次都重新
计算整个函数。比如这样:

 function memfactorial(n){
  if (!memfactorial.cache){
    memfactorial.cache = {
      "0": 1,
      "1": 1
    };
  }
  if (!memfactorial.cache.hasOwnProperty(n)){
    memfactorial.cache[n] = n * memfactorial (n-1);
  }
  return memfactorial.cache[n];
}

  var fact6 = memfactorial(6);
  var fact5 = memfactorial(5);
  var fact4 = memfactorial(4);

 //总共只调用 memfactorial()函数八次

这样未免太过于繁琐,如果把制表的过程封装起来比如这样:

 function memoize(fundamental, cache){
  cache = cache || {};
  var shell = function(arg){
    if (!cache.hasOwnProperty(arg)){
      cache[arg] = fundamental(arg);
    }
    return cache[arg];
  };
  return shell;
} 
 //memoize the factorial function
var memfactorial = memoize(factorial, { "0": 1, "1": 1 });
//call the new function
var fact6 = memfactorial(6);
var fact5 = memfactorial(5);
var fact4 = memfactorial(4); 

需要提醒的是:当一个通用制表函数存在显著性能问题时,最好在这些函数中人工实现制表法。

最后:运行的代码总量越大,使用这些策略所带来的性能提升就越明显。 代码全是copy书上的,所以强烈建议直接去看书而不是看我罗里吧嗦的几个字总结。

下一章将学习字符串和正则表达式很期待。

时间: 2024-08-27 10:20:17

读高性能JavaScript编程 第四章 Conditionals的相关文章

读高性能JavaScript编程学英语 第一章第三页第二段话

Script Positioning 脚本位置 The HTML 4 specification indicates that a <script> tag may be placed inside of a <head> or <body> tag in an HTML document and may appear any number of times within each. Traditionally, script> tags that are use

读高性能JavaScript编程 第一章

草草的看完第一章,虽然看的是译文也是感觉涨姿势了, 我来总结一下: 由于 大多数浏览器都是 single process 处理 ui updatas and js execute 于是产生问题: js (script tag) will blocking page processing 上图说话: 于是大家动脑筋想办法 想到了三个办法 1. Parallel downloads, then execute  并行下载,然后执行. 2.Download times less,Less downlo

读高性能JavaScript编程 第二章 让我知道了代码为什么要这样写

代码为什么要这样写? function initUI(){ var doc = document, bd = doc.body, links = doc.getElementsByTagName_r("a"), i = 0, len = links.length; while(i < len){ update(links[i++]); } doc.getElementById("go-btn").onclick = function(){ start(); }

读大道至简第四章有感

第四章题目为流于形式的沟通,顾名思义说的就是我们的沟通只是表面工作,没有深入,也就不会对工作有什么实质性的帮助.但是这个沟通值得是谁与谁之间的沟通呢,没错,就是我们与客户之间的沟通.程序员与计算机之间可以用C语言,java进行沟通但是客户不一定会这些我们也不能要求客户会这些,所以学好基本的编程语言是基础,学会与客户用汉语进行深刻的沟通,并且把这些沟通的内容转化为编程的需求.这是对一个程序员的客观要求. 然而就像书中所说,有的客户会聘用一个专家组来与程序员进行沟通,这时候专业知识就可以很好的应用,

高性能JavaScript 编程实践

前言 最近在翻<高性能JavaScript>这本书(2010年版 丁琛译),感觉可能是因为浏览器引擎的改进或是其他原因,书中有些原本能提高性能的代码在最新的浏览器中已经失效.但是有些章节的有些内容还是相当不错的,譬如第八章编程实践,为了方便以后的查阅,对此做个总结.失效的代码也会在以后做更进一步的探索. 避免双重求值 这个优化策略很好理解,我们可能都已经不知不觉地运用在了实际的编程中: // not use this setTimeout('alert("hello world&qu

读构建之法第四章第十七章有感

第四章 1.原文:"函数最好有单一的出口,为了达到这个目的,可以使用goto.只要有助于程序逻辑的清晰体现,什么方法都可以使用.--P69" 问题:关于goto,我记得老师讲过,这个在编程中是尽力避免的,所以我在之前并没有了解过它.本书却建议用,这让我产生了困惑. 我的看法:goto语句它可以不受限制的灵活跳转,这样程序员在使用goto语句的时候就很容易因为跳转而略过了一些关键语句.但是它能从多重循环体中跳出,用不着写很多次的break语句.我查了一些资料,发现自从提倡结构化设计以来,

java MySQL数据库编程 第四章 高级查询(二)

第四章 高级查询(二) (1)通过在子查询中使用EXISTS子句,可以对子查询中的行是否存在进行检查.子查询可以出现在表达式出现的如何位置 (2)子查询语句可以嵌套在SQL语句中任何表达式出现的位置. 一.EXISTS子查询 1.使用EXISTS语句判断该数据库对象是否存在: DROP TABLE IF EXISTS temp; 2. EXISTS作为WHERE语句的子查询: SELECT .....FROM 表名 WHERE EXISTS(子查询); 3. EXISTS关键字后面的参数是一个任

高性能javascript(第三章 DOM编程)

1.浏览器之DOM 浏览器通常要求 DOM 实现和 JavaScript 实现保持相互独立 这对性能意味着什么呢?简单说来,两个独立的部分以功能接口连接就会带来性能损耗.一个很形象的 比喻是把 DOM 看成一个岛屿,把 JavaScript(ECMAScript)看成另一个岛屿,两者之间以一座收费桥连接 每次 ECMAScript 需要访问DOM 时,你需要过桥,交一次“过桥费”.你操作 DOM 次数越多,费用就越高.一般的建议是尽量减少过桥次数,努力停留在 ECMAScript 岛上.本章将对

【读书笔记】C#高级编程 第四章 继承

(一)继承的类型 1.实现继承和接口继承 在面向对象的编程中,有两种截然不同的继承类型:实现继承和接口继承. 实现继承:表示一个类型派生于一个基类型,它拥有该基类型的所有成员字段和函数.在实现继承中,派生类型采用基类型的每个函数代码,除非在派生类型的定义中指定重写某个函数的实现代码.在需要给现有的类型添加功能,或许多相关的类型共享一组重要的公共功能时,这种类型的继承非常有用. 接口继承:表示一个类型只继承了函数的签名,没有继承任何实现代码.在需要制定该类型具有某些可用的特性时,最好使用这种类型的