前端性能优化:5.高性能的JavaScript代码

用户通过浏览器访问页面的过程,除了输入URL地址到所访问页面完成首屏渲染,更多的时候页面在相应与用户的交互。
高性能网站的要求不仅是执行顺畅无BUG,还希望对用户的页面操作能够更快速响应,而且在执行完任务的同时占用更少的资源。

5.1 数据存取

无论哪种计算机语言,说到底它们的作用都是对数据的存取与处理,JavaScript也不例外。若能在处理数据之前,更快速的读取到数据,那么必然会对程序执行性能产生积极的作用。本节建工数据的存取及作用域链的角度,探讨一些在JavaScript中能提升性能的方式。

5.1.1 数据存取方式
一般来说JavaScript有四种数据存取方式:

  1. 直接字面量:字面量不存储在特定位置也不需要索引,仅代表自身。包括布尔值、数字、字符串、对象、数组、函数、null、undefined及正则表达式。
  2. 变量:通过关键字const/let/var定义的数据存储单元。
  3. 数组元素:存储在数组对象内部,通过数组下标数字进行索引。
  4. 对象属性:存储在对象内部,通过对象的字符串名称进行索引。

数组元素和对象属性不仅可以是直接字面量的形式,还可以是由其他数组对象或对象属性组成的更为复杂的数据结构。从读取速度看,直接字面量与变量是非常快的,相比之下数组元素和对象属性由于需要索引,其读取速度也会因其组成结构的复杂度越高而越慢。

5.1.2 作用域和作用域链
ES6发布前JavaScript没有明确的块级作用域概念。它只有全局作用域和每个函数内的局部作用域。全局作用域就是无论此时执行的上下文是在函数内部还是函数外部的,都能访问到存在于全局作用域中的变量或对象;而定义存储在函数的局部作用域中的对象,只有在该函数内部执行上下文时才能够访问,而对函数外是不可见的。
对于能够访问到的数据,其在不同作用域中的查询也有先后顺序。这就涉及作用域链的概念。JavaScript引擎会在页面加载后创建一个全局的作用域,然后每碰到一个要执行的函数时,又会为其创建对应的作用域,最终不同的块级作用域和嵌套在内部的函数作用域,会形成一个作用域堆栈。
当前生效的作用于在堆栈的最顶端,由上往下就是当前执行上下文所能访问的作用域链。
例子:

function plus(num){
    return num +1;
}
const ret=plus(6);

函数plus的作用域链中仅有一个指向全局对象的作用域,其中包括this、函数对象plus及常量ret,而在执行到plus时,Js引擎会创建一个新的执行上下文和包含一些局部变量的活动对象。执行过程会先对num标识符进行解析,即从作用域链的最顶层依次向下查找,直至找到num标识符。

变量位于作用域链中的位置越深,被引擎访问到所需的时间就越长,所以我们应当留心对作用域链的管理。
前端性能优化:5.高性能的JavaScript代码_第1张图片

5.1.3 实战经验
1. 对局部变量的使用
如果一个非局部变量在函数中使用次数不止一次,则最好对这个局部变量进行存储。
例子:

function process(){
    const target = document.getElementById('target');
    const imgs = document.getElementById('img');
    for(let i = 0;i

函数process()中,首先通过document的两个不同的成员函数分别获取了特定的元素和元素列表,然后进行一些省略相关处理流程的操作。值得注意的是,document属于全局作用域的对象,位于作用域链最深处,在标识符解析过程中会被最后解析到。由于它在函数中使用了不止一次,所以可以考虑将其声明为一个局部变量,以提升其在作用域链中的查找顺序。
还值得注意的一点是,imgs.length执行了不止一遍。每次通过属性名或索引读取imgs的属性时,DOM都会重复执行一次对页面元素的查找,这个过程本身就会很缓慢。
改进后的代码:

function process(){
    const doc = document;
    const target = doc.getElementById('target');
    const imgs = doc.getElementById('img');
    const len = imgs.length;
    for(let i = 0;i

2. 作用域链的增长
前面讲到可以通过将频繁使用的位于较深作用域链层级中的数据,声明为局部变量来提升标识符解析与访问的速度。若能将全局变量提升到局部变量的访问高度,是否还能提升到比局部变量更高的位置呢?答案是肯定的,在当前局部变量作用域前增加新的活动变量作用域,但这种增长了作用域链的做法会造成过犹不及的效果。
比如with语句,它能将函数外层的变量提升到比当前函数局部变量还要搞的作用域链访问级别上,如下代码由于使用with的缘故,在语句中可直接访问param中的属性值,虽然方便但降低了show()函数原本局部变量的访问速度,所以应尽量少用。

const param = {
    name:'Tian',
    value:100
}
function show(){
    const content = 2;
    with(param){
        console.log('name is ${name}');
    }
}

另一个例子就是try-catch语句,catch代码块被用来处理捕获到的异常,但其中包含错误信息的error的作用域高于当前局部变量所在代码块,所以建议不要在catch语句中处理过多复杂的业务逻辑,会降低数据的访问速度。
3. 警惕闭包的使用

function mkFunc(){
    const name = 'A';
    return function showName(){
        console.log(name);
    }
}
const myFunc = mkFunc();
myFunc();

showName()函数就是一个闭包,它在mkFunc()函数执行时被创建,并能访问mkFunc()函数的局部变量name,为此便需要创建一个独立与mkFunc()函数的作用域链。
前端性能优化:5.高性能的JavaScript代码_第2张图片

一般的函数执行完成后,其中局部变量所占用的空间会被释放,但闭包的这种特性会延长父函数中局部变量的生命周期。也就意味着使用闭包可能会带来更大的内存开销及内存可能泄漏的影响。

5.2 流程控制

流程控制在业务代码中的比重是很大的,因此优化流程控制方面的代码,必然能有效地提升代码运行的速度。

5.2.1 条件判断
1. if-else和switch

通常地if-else如下:
if(value === 0){
    ......
} else if(value === 1){
    ......
} else if(value > 3 && value < 9){
    ......
}

在if-else地判断条件中,变量的取值可以是相应的离散值,也可以是不同的区间范围。
当取值全部为离散值时,可将if-else写成switch:

switch(value){
    case 0: ......; break;
    case 1:.......; break;
    case 2:......; break;
    default:......;
}

对于多个离散值的取值条件判断,switch可以清晰地表明判断条件和返回值之间的对应关系,同时也使它具有更好的代码可读性。
如果只有一两个条件的判断,通常if-else处理条件的时间会比switch更短,当判断条件多到两个以上时,switch效率更高。
2. if-else的优化
如果程序最终的执行路径时最后一个else if子语句,那么当执行到此处之前,其余所有条件判断必然都要经历一遍。耗时也会比之前所有判断条件执行耗时都久。
基于此便有了两种优化方式,第一种时开发者可以预先估计条件被匹配到的频率,按照频率的降序顺序来排列if-else语句,可以让匹配频率高的条件更快执行,从而在整体上降低程序花费在条件判断上的时间,比如

if (value === 8){
    // 匹配到8的概率最高
}else if (value === 7){
    // 匹配到7的概率仅次于8
}else if (value === 6){
    // 匹配到6的概率最低
}else {
    其他判断
}

第二种方式时利用二分法的思路,可能开发人员在编写相应的业务代码时,并不能预先估计出各种条件在多次执行时被匹配到的概率,但却能对取值区间的边界有明确的划分,那么便可以用二分取值范围来降低匹配条件的执行次数,比如

if (value < 4){
    if (value < 2){
        // 值在小于2时的情况
    }else {
        // 值在2或3之间
    }
}else {
    if (value < 6){
         // 值在4或5之间
    }else {
        //值在6到上界之间的取值  
    }
}

仅演示简单的数字区间,实际开发中需自行结合实际情况考虑。
3. 数组索引和对象属性
除了if-else和switch,利用数组的索引查询或对象的属性拆线呢也可以达到类似条件判断的目的,比如

//条件映射数组
const valueArray = [v0,v1,v2,v3,v4];
//提取对应数组索引的处理
valueArray[value];

由于数组中的每个元素既可以是对象,又可以是函数,那么便可将条件匹配的处理过程封装到数组的元素中,并用数组索引映射对应的value变量,通过匹配数组索引执行相应的处理过程。同样基于对象属性的映射方式,也能实现类似的条件查找行为,比如

//基于对象的属性映射
const valueMap = {
    'condition0': ()=>{......},
    'condition1': ()=>{......},
    'condition2': ()=>{......}
}
//提取对应对象属性的处理
valueMap[value];

基于对象属性的索引可以不局限于整数取值,它能匹配符合对象属性名命名规范的任何字符串形式,,与switch类似,其取值范围是离散值。当匹配条件的数量较小时,并不适合使用这种基于数组或对象的查找方式,因为查找数组或映射对象属性值往往比执行少量的条件判断语句要慢,只有当取值范围变得非常大时,这种查找方式的性能优势才会凸显出来。
4. 策略模式
策略模式就是定义一系列的处理流程或算法,把它们分别封装起来,使得它们可以相互替代。其目的就是将算法的使用和实现分离。
一个基于策略模式的程序通常会包含两个部分——一部分是一组策略类,其中包含一系列具体的处理算法,有点类似于包含不同处理过程的映射对象 valueMap;另一部分是环境类,它将根据上下问操作运算,决定调用策略类中的那个策略处理过程,即完成流程控制中条件匹配的部分。
假设一个具体场景:年底公司要根据员工的绩效等级发奖金,绩效考核打S的发四倍工资,打A的发三倍月工资,打B的发两倍月工资……为简化说明,仅以三种绩效考核等级为例。通常的解决方案,直接的想法是定义一个函数,接受两个参数,分别是员工的月薪和绩效考核等级,然后在其中通过if-else判断来分别计算出奖金额度:

//计算奖金的方法
function calculateBonus(salary, level){
    if(level === "S"){
        return salary*4;
    }else if(level === "A"){
        return salary*3;
    }else if(level === "B"){
        return salary*2;
    }
}

上述代码可以看出这样的实现本身没有什么问题,将所有奖金计算规则都包含在一个函数中解决。如果需要改写奖金计算规则,会发现这个函数不符合“对扩展开放,对修改封闭”的设计原则,扩展新功能的工作量不亚于推倒重做做。若重构为策略模式的写法:

//策略类
const strategies = {
    "S":(salary)=> salary*4;
    "A":(salary)=> salary*3;
    "B":(salary)=> salary*2;
}
//计算具体奖金
function calculateBonus(salary,level){
    return strategies[level](salary);
}

如此解耦了各种级别奖金计算的逻辑,如果要针对奖金发放算法进行调整,则只需修改策略类中对应的方法即可。
5. 条件判断的使用建议
· 当所要匹配的条件仅为一两个离散值时,或者容易划分不同取值范围时,使用if-else。
· 当所要匹配的条件超过一两个但少于十个离散值时,使用switch。
· 当所要匹配的条件超过十个离散值时,使用基于数组索引或对象属性的查找方式。

5.2.2 循环语句
与条件判断相比,循环语句对程序执行性能的影响更大。一个循环语句的循环执行次数直接影响程序的时间复杂度,如果代码中还存在去欸按导致循环不能及时停止,从而造成死循环,那么给用户带来的使用体验将会是非常糟糕的。
1. 三种常规循环语句
JavaScript中循环语句常见写法有三种,第一种是标准的for循环,这与大部分编程语言类似,包括初始化、循环结束条件、迭代语句及循环体四部分,第二种和第三种分别是while循环和do-while循环,二者唯一的差别就是do-while循环会先执行一遍循环体。
通常使用这三种循环语句时,基本都是对数组元素进行遍历。从索引的第一个元素开始直到数组的最后一个元素结束,每次在执行循环判断时,都需要将当前的数组索引与数组长度进行比较。由于该比较操作执行的过程中数组长度一般不会改变,且存取局部变量要比查找属性值时更省时,所以提前将要遍历的数组长度声明为局部变量,然后将该局部变量进行循环结束的条件判断,效率会更高。

//较差的循环结束判断
const array = [1,2,3,4,5];
for(let i =0;i

这在对包含较大规模DOM节点树的遍历过程中,效果会更加明显。此外还有一种更简单地提升循环语句性能的方式:将循环变量递减到0,而非递增到数组总长度。

for(let i=array.length-1;i>0;k--){
    ......
}

只有初始化时涉及到了属性值的读取,其比较的运算速度会更快。
2. for-in循环与函数迭代

//遍历object对象的所有属性
for(let prop in object){
    //确保不会遍历到object原型链上的其他对象
    if(object.hasOwnProperty(prop)){
        ......
    }
}

可以看出for-in循环能够遍历对象的属性集,特别适合处理诸如JSON对象这样的未知属性集,但对通常的循环使用场景来说,由于它遍历属性的顺序不确定,循环的结束条件也无法改变,并且因为需要从目标对象中解析出每个可枚举的属性,即要检查对象的原型和整个原型链,所以其循环速度也会比其他循环方式慢许多,若有性能要求尽量避免使用for-in循环。
对于数组的循环,JavaScript原生提供了一中forEach函数迭代的方法,此方法会遍历数组上的所有元素,并对每个元素执行一个方法,所运行的方法作为forEach的入参。
Array.forEach((value,index,arr)=>{

})

这种方式会让数组元素的迭代看起来更加直观,但在通常情况下与三种基本的循环方法相比,其性能方面仅能达到后者的1/8,如果数组长度较大或对运行速度有比较严格的要求,则函数迭代的方式不建议使用。
另外,还有ES6新加入的for-of循环,可以用它来代替for-in和forEach循环,它不仅性能比这二者更好,还支持对任何可迭代的数据结构进行遍历,但与三种常规循环语句相比其性能还是稍逊色一些。

5.2.3 递归
简单来说,递归就是函数执行体内部调用自身的行为,这种方式有时可以让复杂的算法实现变得简单,如斐波那契数列或阶乘。
使用递归也有一些潜在的问题需要注意:比如缺少或不明确递归的终止条件会很容易造成用户界面卡顿,同时由于递归是一种通过空间换时间的算法,其执行过程中会入栈保存大量的中间运算结果,对内存的开销将与递归次数成正比,由于浏览器都会限制JavaScript的调用栈大小,超出限制递归执行便会失败。
1. 使用迭代
任何递归函数都可以改写成迭代的循环形式,虽然循环会引入自身的一些性能问题,但相比于长时间执行的递归函数,其性能开销还是要小很多的。

//递归实现归并排序
function merge(left, right) {
  const result = [];
  while (left.length > 0 && right.length > 0) {
    //把最小的先取出来放到结果中
    if (left[0] < right[0]) {
      result.push(left.shift());
    } else {
      result.push(right.shift());
    }
  }
  //合并
  return result.concat(left).concat(right);
}
//递归函数
function mergeSort(array) {
  if (array.length === 1) return array;
  //计算数组中点
  const middle = Math.floor(array.length / 2);
  //分割数组
  const left = array.slice(0, middle);
  const right = array.slice(middle);
  //进行递归合并与排序
  return merge(mergeSort(left), mergeSort(right));
}

可以看出这段归并排序中,mergeSort()函数会被频繁调用,对于包含n个元素的数组来说,mergeSort()函数会被调用2n-1次,随着所处理数组元素的增多,这对浏览器的调用栈是一个严峻的考验。

//用迭代的方式改写递归函数
function mergeSort(array) {
  if (array.length === 1) return array;
  const len = array.length;
  const work = [];
  for (let i = 0; i < len; i++) {
    work.push([array[i]]);
  }
  //确保总数组长度为偶数
  if (len & 1) work.push([]);
  //迭代两两归并
  for (let lim = len; lim > 1; lim = (lim + 1) / 2) {
    for (let j = 0, k = 0; k < lim; j += 1, k += 2) {
      work[j] = merge(work[k], work[k + 1]);
      //数组长度为奇数时,补一个空数组
      if (lim & 1) work[j] = [];
    }
  }
  return work[0];
}

此处通过迭代实现的mergeSort()函数,其功能上与递归方式相同,虽然在执行时间上来看要慢一些,但它不会受到浏览器对JavaScript调用栈的限制。
2. 避免重复工作
如果在递归过程中,前一次的计算结果能被后一次计算使用,那么缓存前一次的计算结果就能有效避免许多重复工作。比如阶乘操作:

//计算某个数的阶乘
function factorial(n){
    if(n === 0){
        return 1;
    }else{
        return n*factorial(n-1);
    }
}

当我们计算多个数的阶乘(如2、3、4)时,如果分别计算这三个数的阶乘,则函数factorial()总共要被调用12次,其中国在计算4的阶乘时,会把3的阶乘重新计算一遍,计算3的阶乘时又会把2的阶乘重新计算一遍,可以看出如果在计算4的阶乘之前,将3的阶乘数缓存下来,那么在计算4的阶乘时,递归仅需要再执行一次。如此便通过缓存阶乘计算结果,避免多于计算过程,原本12次的递归调用,可以减少到5次。
如:

function memoize(func,cache){
    const cache = cache || {};
    return function(args){
        if(!cache.hasOwnProperty(args)){
            cache[args] = func(args);
        }
        return cache[args];
    }
}

该方法利用函数闭包有效避免了类似计算多次阶乘时的重复操作,确保只有当一个计算在之前从未发生过时,才产生新的计算值,这样前面的阶乘函数便可改写为:

const memorizeFactorial = memorize(factorial,{'0':1,'1':1});

这种方式也存在性能问题,比如函数闭包延长了局部变量的存活期,如果数据量过大又不能有效回收,则容易导致内存溢出。这种方案只有在程序中有相同参数多次调用时才会比较省时,所以综合而言,优化方案还需根据具体使用场景具体考虑。

5.3 字符串处理

为了搞笑处理字符串,使用正则表达式是必不可少的,但两个匹配同一文本的正则表达式并不意味着它们具有相同的执行速度。

5.3.1 字符串拼接
字符串拼接是前端开发中的常规操作,但在大规模数据的循环迭代中进行字符串拼接时,可能稍有不慎就会造成严重的性能问题。
几种常见的拼接方式:

//使用"+"运算符
const str1 = "a" + "b";
//使用"+="运算符
const str2 = "a";
str2 += "b";
//使用数组的join()方法
const str3 =["a","b"].join();
//使用字符串的concat()方法
const str4 = "a";
str4.concat("b");

当处理少量单次或少量字符串拼接时,这些方法的运行速度都很快,根据自己偏好使用即可,但随着需要迭代合并的字符串数量增加,他们之间性能的差异逐渐显现,如下是一个字符串迭代拼接的处理过程:

let len = 1000;
let str = "";
while(len--){
    str += "a" + "b";
}

但看循环内部的字符串拼接操作,其代码运行过程可分为四步:首先在内部创建一个临时的字符串变量,然后将拼接的字符串"ab"赋值给它,接着把该临时变量与str的当前值进行拼接,最后将结果赋值给str。可见由于存在临时变量的存取,其性能并不满足预期,若避免临时变量存取直接向str变量上拼接,在大部分浏览器中,都能将这一操作步骤的执行速度提升20%左右。
//不生产中间临时变量的字符串拼接
str = str + “a” + “b”;

数组对象的 join()方法和字符串对象的concat()方法比使用赋值表达式实现字符串拼接在性能上稍慢。

5.3.2 正则表达式
1. 正则表达式处理步骤
(1) 编译表达式:一个正则表达式对象可以通过RegExp构造函数创建,也可以定义成正则直接量,档期被创建出来后,浏览器会先去验证然后将它转化为一个原生待程序,用于执行匹配任务。
(2) 设置起始位置:当匹配开始时,需要先确定目标字符串的其实搜索位置,初始查询时为整个字符串的起始字符的位置,在回溯查询时为正则表达式对象的lastIndex属性指定的索引位置。
(3) 匹配过程:在确定了起始位置后,便会从左到右逐个测试正则表达式的各组成部分,看能否找到匹配的字元。如果遇到表达式中的量词和分支,则需要进行决策,对于量词(*、+或{3}),需要判断从何时开始进行更多字符的匹配尝试;对于分支("|"或操作),每次需从选项中选择一个分支进行接下来的匹配。在正则表达式做这种决策的时候,会进行记录以备回溯时使用。
(4) 匹配结束:若当前完成一个字元的匹配,则正则表达式会继续向右扫描,如果接下来的部分也都能匹配上,则宣布匹配成功并结束匹配;若当前字元匹配不到,或者后续字元匹配失败,则会回溯到上一个决策点选择余下可选字元继续匹配过程,直到匹配到目标子字符串,宣布匹配成功,或者尝试了所有排列组合后也没找到。宣布匹配失败,结束本轮匹配。回到起始字符串的下一个字符,重复匹配过程。
2. 分支与重复
看看正则表达式在匹配过程中是如何处理分支重复的:

/<(img|p)>.*<\img>/.test("

")

开始匹配时,首先从左向右查找<字符,恰好目标字符串的第一个字符时<,然后进行img|p分支子表达式的处理,分支选择也是从左到右的,先检查img是否能匹配成功,发现字符<随后的字符p并不能匹配,此分支无法继续。需要回溯到最近一次的分支决策点,即首字母<的后面,尝试第二个分支p字符的匹配,发现匹配成功。
接下来的.号标识匹配除换行符外的任意字符,并且带有量词符号*,这是一个贪婪量词,需要重复0次或多次的匹配.号,由于目标字符串中不存在换行符,所以它会过滤掉接下来的所有字符,至字符串尾部往回继续匹配接下来的字元<\/img>,最后一个字符为>,不匹配所需的<,尝试倒数第二个字符p,p也不匹配,如此循环知道匹配到目标字元或找不到目标字元匹配失败。
如果我们仅想查找距离字元<(img|p)>最近的<\/img>,显然这种贪婪量词的搜索过程会扩大正则表达式的匹配空间,为此可以使用惰性量词.*?进行替换。
可以看出这里的目标字符串,对于贪婪量词与惰性量词的正则匹配结果是相同的,但它们的匹配过程却是不同的,所以当目标字符串变得非常大时,获得相同匹配结果的正则表达式,其执行性能可能会存在较大差异。
3. 回溯失控
当某个正则表达式的执行使浏览器卡顿数秒或更长时间,可能出现了回溯失控。如下:

/.*?.*?.*?<\/title>.*?<\/head>.*?<body>.*?<\/body>.*?<\/html>/
</code></pre> 
  <p>该正则表达式在匹配常规html文件时不会存在运行问题,但如果碰到一些必要的html标签缺失,那么其匹配效率或变得非常糟糕。比如当html文件中缺少结束标签<code></html></code>时,在匹配最后一个惰性量词后并未能找到符合字元<code><\/html></code>的字符串,此时正则表达式并不会结束,而是向前回溯到上一次的惰性量词出,继续字元<code><\/body></code>的匹配,以试图找到第二个<code></body></code>标签。如果没有找到,则会一次继续向前回调,可想而知这样的查询性能会很低。<br> 如何解决?首先可以想到具体化模糊字元,比如将.<em>?具体化为[^\r\n]</em>,进而去除集中回溯时可能发生的情况。虽然这种方式控制了可能的回溯失控,但在匹配不完整html文件时所需时间依然喝文件大小成线性关系,所以性能并没有得到有效提高。<br> 更为有效的方式时预查找,它作为全局匹配的一部分,能够仅检查自己所包含的正则表达式字元于当前位置是否能够匹配,并且不消耗字符,即在一个匹配发生后立即开始下一次匹配搜索,而非从包含预查找的字符后开始。预查找的形式是:(?=pattern),pattern代表一个正则表达式,改写上面的例子:</p> 
  <pre><code>/<html>(?=(.*?<head>))\1(?=(.*?<title>))\2(?=(.*?<\/title>))\3(?=(.*?<\/head>))\4(?=(.*?<body>))\5(?=(.*?<\/body>))\6.*?<\/html>/
</code></pre> 
  <p>这样当html问而建结尾部分缺少<code></html></code>标签时,最后的惰性量词.*?会展开至整个字符串末尾,由于没有有效回溯点,所以正则表达式会立即宣布失败。<br> 4. 量词嵌套<br> 另一个可能会引起回溯失控的写法就是量词嵌套,即在一个整体被量词修饰的组中有量词的使用,如:对一个包含大量T字符的字符串使用如下正则表达式进行匹配:</p> 
  <pre><code>/(T+T+)+Q/
</code></pre> 
  <p>这种排列组合会产生数量巨大的查找分支。为防止这种情况的发生,关键要确保正则表达式不对字符串相同的部分进行匹配,并且尽量保持正则表达式简洁易懂,可以改写为:<code>/TT+Q/</code>,但对于较复杂的正则表达式可能难以避免,必要时可采取预查找进行规避。</p> 
  <p><strong>5.3.3 优化正则表达式</strong><br> 一些有效提升正则表达式匹配效率的方法:</p> 
  <ol> 
   <li>化繁为简:大规模的单一正则表达式很呐维护,并且容易出现回溯失控的问题。可使用条件逻辑将复杂的字符串搜索问题拆分为多个正则表达式来解决。</li> 
   <li>将正则表达式赋值给变量:复用时可以避免重新编译。</li> 
   <li>合适的量词:使用合适的量词可以有效提升性能。</li> 
   <li>更快失败:通常正则表达式执行较慢不是其匹配成功较慢,而是匹配失败的过程较慢。若想让正则表达式更快执行完毕,应加快其失败的过程。</li> 
   <li>尽量具体:对于子表达式能够重叠匹配或字符与字元相邻时,正则表达式的可能分支路径会增加,所以表达式应更加具体化。</li> 
   <li>减少分支数量:正则表达式中,使用类似按位或的竖线符号|来表示分支选型,但通常建议尽量减少分支的使用,可以使用字符集或选项数组来代替,因为字符集在切换选项时使用的时位向量的方式,这笔分支采用的回溯快。示例:</li> 
  </ol> 
  <table> 
   <thead> 
    <tr> 
     <th>分支方式</th> 
     <th>字符集</th> 
    </tr> 
   </thead> 
   <tbody> 
    <tr> 
     <td>bat\mat</td> 
     <td>[bm]at</td> 
    </tr> 
    <tr> 
     <td>red\read</td> 
     <td>rea?d</td> 
    </tr> 
    <tr> 
     <td>(.|\r|\n)</td> 
     <td>[\s\S]</td> 
    </tr> 
   </tbody> 
  </table> 
  <p>注意采用非捕获组:由于捕获组需要记录反向引用,会更加消耗内存和引用。比如可以使用(?:pattern)代替(pattern)。<br> 使用反向引用避免后处理:如果使用场景需要引用匹配的一部分,则应尽量用捕获组捕获目标片段,然后通过反向引用进一步处理,而不是剥离出目标字符串后再手动处理。</p> 
  <h3>5.4 快速响应</h3> 
  <p>JavaScript代码的执行通常会阻塞页面的渲染,考虑到用户体验,这就会限制我们在编写代码时需要注意减少或避免一些执行时间过长的逻辑运算。</p> 
  <p><strong>5.4.1 浏览器的限制</strong><br> 由于JavaScript是单线程,这就意味着浏览器的每个窗口或页签在同一时间内,要么执行JavaScript脚本,要么响应用户操作刷新页面,也就是说这二者的行为是相互阻塞的。<br> 对于浏览器的这种限制,我们可能就需要对长时间运行的脚本进行重构,尽量保证一段脚本的执行不超过100ms,若果超过这个时间阈值,用户明显就会网站卡顿变慢的使用体验。<br> 引起JavaScript执行时间过长的原因概括有三点:<br> 第一类是对DOM的频繁操作,相比于JavaScript的运算,DOM操作的开销都是极高的,这也是现代前端框架中普遍采取虚拟DOM的原因。<br> 第二类是不恰当的循环,可能因为循环次数执行过多,或者每次循环中执行了过多操作,若能将功能尽可能分解就会明显缓解这个问题。<br> 第三类是存在过深的递归,前面章节有提到过浏览器对JavaScript调用栈存在限制,将递归改写成迭代能有效地避免此类问题。</p> 
  <p><strong>5.4.2 异步队列</strong><br> JavaScript既要处理运算又要响应与用户的交互,就是通过异步队列完成的。<br> 当创建一个异步任务时,它其实并没有马上执行,而是被JavaScript引擎放置到了一个队列中,当执行完成一个任务脚本后,JavaScript引擎便会挂起让浏览器去做其他工作,比如更新页面,当页面更新完后JavaScript引擎便会查看此异步队列,并从中取出一个任务脚本去执行,只要该队列不为空,这个过程就会不断重复。<br> 故此便有了对执行过长任务的一种优化策略,即将一个长任务拆分为多个异步任务,从而让浏览器给刷新页面留出时间,但过短的延迟时间也可能会让浏览器响应不及时,通常可以用定时器来控制一个100ms左右的延迟。</p> 
  <h3>5.5 其他建议</h3> 
  <p>除了前面的几种方法,还有些小技巧也能帮助浏览器高效执行JavaScript,比如避免多重求值、使用位操作及使用原生方法。</p> 
  <p><strong>5.5.1 避免多重求值</strong><br> 多重求值是脚本语言中普遍存在的一种语法特性,即动态执行包含可执行代码的字符串,虽然当前的主流前端项目很少会有类似的方法,但如果面临优化历史代码的场景,就需要对此多加留意。能够运行代码字符串的方法通常有如下四种:setTimeout()、setInterval()、eval()及Function()构造函数。</p> 
  <pre><code>const a = 1, b = 2;
let result = 0;
//使用setTimeout() 函数执行代码字符串
setTimeout("result = a + b", 100);
//使用setInterval() 函数执行代码字符串
setInterval("result = a + b", 100);
//使用eval() 函数执行代码字符串
result = eval("a + b");
//使用Function() 函数执行代码字符串
result = new Function("a", "b", "return a + b");
</code></pre> 
  <p>这四段代码的执行过程,首先会以正常的方式进行求值,接着在执行过程中对字符串里的代码进行一次额外的求值运算,这个运算操作的代价,与直接执行字符串中代码的代价相比时巨大的。在开发中应当避免使用Function()与eval()函数,同时切记在使用setTimeout()和setInterval()函数时第一个参数使用字符串。</p> 
  <p><strong>5.5.2 使用位操作</strong><br> 几乎在所有编程语言中,微操作的执行速度都是非常快的,因为位操作通常发生在系统底层。在JavaScript中使用有符号的32位二进制来表示一个数字,位操作就是直接按照二进制方式进行计算的,这要比其他数学运算和布尔操作快得多。</p> 
  <p><strong>5.5.3 使用原生方法</strong><br> 使用位操作来优化数学运算的场景也是比较有限的,面对复杂的数学运算时可以多使用JavaScript的原生方法。<br> 常用的原生方法:</p> 
  <table> 
   <thead> 
    <tr> 
     <th>属性\方法</th> 
     <th>含义</th> 
    </tr> 
   </thead> 
   <tbody> 
    <tr> 
     <td>Math.abs(num)</td> 
     <td>计算num的绝对值</td> 
    </tr> 
    <tr> 
     <td>Math.pow(num,power)</td> 
     <td>计算num的power次幂</td> 
    </tr> 
    <tr> 
     <td>Math.sqrt(num)</td> 
     <td>计算num的平方根</td> 
    </tr> 
    <tr> 
     <td>Math.exp(num)</td> 
     <td>计算自然对数底的指数</td> 
    </tr> 
    <tr> 
     <td>Math.cos(x)</td> 
     <td>计算余弦函数</td> 
    </tr> 
    <tr> 
     <td>Math.PI</td> 
     <td>计算圆周率</td> 
    </tr> 
    <tr> 
     <td>Math.SQRT2</td> 
     <td>计算2的平方根</td> 
    </tr> 
   </tbody> 
  </table> 
  <h3>5.6 小结</h3> 
  <p>从代码书写角度介绍了许多与前端性能相关的内容,包括数据存取、流程控制、字符串处理、不阻塞页面渲染流程的快速响应。以及能让JavaScript执行更快的一些技巧。<br> 同时详细介绍了如何优化正则表达式,从执行过程到回溯失控以及相关的一些优化注意事项都有介绍。</p> 
  <p>前端性能优化系列:<br> 前端性能优化:1.什么是前端性能优化<br> 前端性能优化:2.前端页面的生命周期<br> 前端性能优化:3.图像资源优化<br> 前端性能优化:4.资源加载优化<br> 前端性能优化:5.高性能的JavaScript代码<br> 前端性能优化:6.项目构建优化<br> 前端性能优化:7.页面渲染优化</p> 
 </div> 
</div>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1723007241986060288"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(前端性能优化,javascript,前端,性能优化)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1950232820773351424.htm"
                           title="移动端城市区县二级联动选择功能实现包" target="_blank">移动端城市区县二级联动选择功能实现包</a>
                        <span class="text-muted">good2know</span>

                        <div>本文还有配套的精品资源,点击获取简介:本项目是一套为移动端设计的jQuery实现方案,用于简化用户在选择城市和区县时的流程。它包括所有必需文件:HTML、JavaScript、CSS及图片资源。通过动态更新下拉菜单选项,实现城市到区县的联动效果,支持数据异步加载。开发者可以轻松集成此功能到移动网站或应用,并可基于需求进行扩展和优化。1.jQuery移动端解决方案概述jQuery技术简介jQuery</div>
                    </li>
                    <li><a href="/article/1950232190038110208.htm"
                           title="day15|前端框架学习和算法" target="_blank">day15|前端框架学习和算法</a>
                        <span class="text-muted">universe_01</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a>
                        <div>T22括号生成先把所有情况都画出来,然后(在满足什么情况下)把不符合条件的删除。T78子集要画树状图,把思路清晰。可以用暴力法、回溯法和DFS做这个题DFS深度搜索:每个边都走完,再回溯应用:二叉树搜索,图搜索回溯算法=DFS+剪枝T200岛屿数量(非常经典BFS宽度把树状转化成队列形式,lambda匿名函数“一次性的小函数,没有名字”setup语法糖:让代码更简洁好写的语法ref创建:基本类型的</div>
                    </li>
                    <li><a href="/article/1950226390070652928.htm"
                           title="Flowable 高级扩展:自定义元素与性能优化实战" target="_blank">Flowable 高级扩展:自定义元素与性能优化实战</a>
                        <span class="text-muted">练习时长两年半的程序员小胡</span>
<a class="tag" taget="_blank" href="/search/Flowable/1.htm">Flowable</a><a class="tag" taget="_blank" href="/search/%E6%B5%81%E7%A8%8B%E5%BC%95%E6%93%8E%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97/1.htm">流程引擎实战指南</a><a class="tag" taget="_blank" href="/search/%E6%B5%81%E7%A8%8B%E5%9B%BE/1.htm">流程图</a><a class="tag" taget="_blank" href="/search/flowable/1.htm">flowable</a><a class="tag" taget="_blank" href="/search/BPMN/1.htm">BPMN</a><a class="tag" taget="_blank" href="/search/%E6%B5%81%E7%A8%8B%E5%BC%95%E6%93%8E/1.htm">流程引擎</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>在前五篇文章中,我们从基础概念、流程设计、API实战、SpringBoot集成,到外部系统协同,逐步构建了Flowable的应用体系。但企业级复杂场景中,原生功能往往难以满足定制化需求——比如需要特殊的审批规则网关、与决策引擎联动实现动态路由,或是在高并发场景下优化流程引擎性能。本文将聚焦Flowable的高级扩展能力,详解如何自定义流程元素、集成规则引擎,并掌握大型系统中的性能调优策略。一、自定</div>
                    </li>
                    <li><a href="/article/1950225255079407616.htm"
                           title="企业级区块链平台Hyperchain核心原理剖析" target="_blank">企业级区块链平台Hyperchain核心原理剖析</a>
                        <span class="text-muted">boyedu</span>
<a class="tag" taget="_blank" href="/search/%E5%8C%BA%E5%9D%97%E9%93%BE/1.htm">区块链</a><a class="tag" taget="_blank" href="/search/%E5%8C%BA%E5%9D%97%E9%93%BE/1.htm">区块链</a><a class="tag" taget="_blank" href="/search/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%8C%BA%E5%9D%97%E9%93%BE%E5%B9%B3%E5%8F%B0/1.htm">企业级区块链平台</a><a class="tag" taget="_blank" href="/search/Hyperchain/1.htm">Hyperchain</a>
                        <div>Hyperchain作为国产自主可控的企业级联盟区块链平台,其核心原理围绕高性能共识、隐私保护、智能合约引擎及可扩展架构展开,通过多模块协同实现企业级区块链网络的高效部署与安全运行。以下从核心架构、关键技术、性能优化、安全机制、应用场景五个维度展开剖析:一、核心架构:分层解耦与模块化设计Hyperchain采用分层架构,将区块链功能解耦为独立模块,支持灵活组合与扩展:P2P网络层由验证节点(VP)</div>
                    </li>
                    <li><a href="/article/1950201675771867136.htm"
                           title="分布式链路追踪系统架构设计:从理论到企业级实践" target="_blank">分布式链路追踪系统架构设计:从理论到企业级实践</a>
                        <span class="text-muted">ma451152002</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a><a class="tag" taget="_blank" href="/search/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/1.htm">系统架构</a>
                        <div>分布式链路追踪系统架构设计:从理论到企业级实践本文深入探讨分布式链路追踪系统的架构设计原理、关键技术实现和企业级应用实践,为P7架构师提供完整的技术方案参考。目录引言:分布式链路追踪的重要性核心概念与技术原理系统架构设计数据模型与协议标准核心组件架构设计性能优化与扩展性设计企业级实施策略技术选型与对比分析监控与运维体系未来发展趋势P7架构师面试要点引言:分布式链路追踪的重要性微服务架构下的挑战在现</div>
                    </li>
                    <li><a href="/article/1950194868756213760.htm"
                           title="程序员必备:10 个提升代码质量的工具" target="_blank">程序员必备:10 个提升代码质量的工具</a>
                        <span class="text-muted">大力出奇迹985</span>
<a class="tag" taget="_blank" href="/search/%E5%AE%A0%E7%89%A9/1.htm">宠物</a>
                        <div>在软件开发过程中,代码质量对项目的成功起着决定性作用。高质量的代码不仅易于维护和扩展,还能有效降低成本并提升可靠性。本文精心挑选了10个程序员必备工具,助力提升代码质量。这些工具涵盖代码格式化、静态分析、代码审查、测试、性能优化、安全扫描、版本控制、依赖管理、代码生成以及文档生成等多个关键领域。通过使用它们,开发者能够高效地发现并解决代码中的潜在问题,遵循最佳实践,提升代码的可读性、可维护性与安全</div>
                    </li>
                    <li><a href="/article/1950194742100815872.htm"
                           title="用代码生成艺术字:设计个性化海报的秘密" target="_blank">用代码生成艺术字:设计个性化海报的秘密</a>
                        <span class="text-muted"></span>

                        <div>本文围绕“用代码生成艺术字:设计个性化海报的秘密”展开,先概述代码生成艺术字在海报设计中的独特价值,接着介绍常用的代码工具(如HTML、CSS、JavaScript等),详细阐述从构思到实现的完整流程,包括字体样式设计、动态效果添加等,还分享了提升艺术字质感的技巧及实际案例。最后总结代码生成艺术字的优势,为设计师提供打造个性化海报的实用指南,助力提升海报设计的独特性与吸引力,符合搜索引擎SEO标准</div>
                    </li>
                    <li><a href="/article/1950191208873652224.htm"
                           title="vue element 封装表单" target="_blank">vue element 封装表单</a>
                        <span class="text-muted">影子信息</span>
<a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>背景:在前端系统开发中,系统页面涉及到的表单组件比较多,所以进行了简单的封装。封装的包括一些Form表单组件,如下:input输入框、select下拉框、等实现效果:理论知识:表单组件官方链接:点击跳转封装组件:封装组件的思路:不封装element组件,每一个input组件绑定一个form对象,例如官网。简单封装element组件,利用for循环生成form表单的每一项el-form-item。进</div>
                    </li>
                    <li><a href="/article/1950191165710069760.htm"
                           title="前端面试每日 3+1 —— 第39天" target="_blank">前端面试每日 3+1 —— 第39天</a>
                        <span class="text-muted">浪子神剑</span>

                        <div>今天的面试题(2019.05.25)——第39天[html]title与h1、b与strong、i与em的区别分别是什么?[css]写出你知道的CSS水平和垂直居中的方法[js]说说你对模块化的理解[软技能]公钥加密和私钥加密是什么?《论语》,曾子曰:“吾日三省吾身”(我每天多次反省自己)。前端面试每日3+1题,以面试题来驱动学习,每天进步一点!让努力成为一种习惯,让奋斗成为一种享受!欢迎在Iss</div>
                    </li>
                    <li><a href="/article/1950178477592342528.htm"
                           title="前端数据库:IndexedDB从基础到高级使用指南" target="_blank">前端数据库:IndexedDB从基础到高级使用指南</a>
                        <span class="text-muted"></span>

                        <div>文章目录前端数据库:IndexedDB从基础到高级使用指南引言一、IndexedDB概述1.1什么是IndexedDB1.2与其他存储方案的比较二、基础使用2.1打开/创建数据库2.2基本CRUD操作添加数据读取数据更新数据删除数据三、高级特性3.1复杂查询与游标3.2事务高级用法3.3性能优化技巧四、实战案例:构建离线优先的待办事项应用4.1数据库设计4.2同步策略实现五、常见问题与解决方案5.</div>
                    </li>
                    <li><a href="/article/1950169524384886784.htm"
                           title="【Java Web实战】从零到一打造企业级网上购书网站系统 | 完整开发实录(三)" target="_blank">【Java Web实战】从零到一打造企业级网上购书网站系统 | 完整开发实录(三)</a>
                        <span class="text-muted">笙囧同学</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F/1.htm">状态模式</a>
                        <div>核心功能设计用户管理系统用户管理是整个系统的基础,我设计了完整的用户生命周期管理:用户注册流程验证失败验证通过验证失败验证通过用户名已存在用户名可用失败成功用户访问注册页面填写注册信息前端表单验证显示错误提示提交到后端后端数据验证返回错误信息用户名唯一性检查提示用户名重复密码加密处理保存用户信息保存成功?显示系统错误注册成功跳转登录页面登录认证机制深度解析我实现了一套企业级的多层次安全认证机制:认</div>
                    </li>
                    <li><a href="/article/1950169526440095744.htm"
                           title="从零到一:打造基于GigaChat AI的艺术创作平台 | 笙囧同学的全栈开发实战" target="_blank">从零到一:打造基于GigaChat AI的艺术创作平台 | 笙囧同学的全栈开发实战</a>
                        <span class="text-muted"></span>

                        <div>作者简介:笙囧同学,中科院计算机大模型方向硕士,全栈开发爱好者联系方式:3251736703@qq.com各大平台账号:笙囧同学座右铭:偷懒是人生进步的阶梯前言在AI技术飞速发展的今天,如何将前沿的大模型技术与实际应用相结合,一直是我们开发者关注的焦点。今天,笙囧同学将带大家从零开始,构建一个基于GigaChatAI的艺术创作平台,实现React前端+Django后端的完整全栈解决方案。这不仅仅是</div>
                    </li>
                    <li><a href="/article/1950164483057971200.htm"
                           title="14.tornado操作之应用Websocket协议实现聊天室功能" target="_blank">14.tornado操作之应用Websocket协议实现聊天室功能</a>
                        <span class="text-muted">孤寒者</span>
<a class="tag" taget="_blank" href="/search/Tornado%E6%A1%86%E6%9E%B6%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%AE%9E%E6%88%98/1.htm">Tornado框架从入门到实战</a><a class="tag" taget="_blank" href="/search/websocket/1.htm">websocket</a><a class="tag" taget="_blank" href="/search/tornado/1.htm">tornado</a><a class="tag" taget="_blank" href="/search/%E8%81%8A%E5%A4%A9%E5%AE%A4%E5%8A%9F%E8%83%BD%E5%AE%9E%E7%8E%B0/1.htm">聊天室功能实现</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>目录:每篇前言:1.什么是WebSocket(1)定义(2)优点(3)和HTTP对比(4)适用场景2.WebSocket关键方法3.本tornado项目中使用WebSocket(1)准备一个聊天室的页面:第一步:编写视图:第二步:编写接口:(app.py中加入以下接口!)第三步:编写前端页面:测试接口——响应OK!(2)使用WebSocket:(3)聊天室的聊天功能的最终实现:第一步:战前准备第二</div>
                    </li>
                    <li><a href="/article/1950144218282389504.htm"
                           title="为什么学习Web前端一定要掌握JavaScript?" target="_blank">为什么学习Web前端一定要掌握JavaScript?</a>
                        <span class="text-muted">web前端学习指南</span>

                        <div>为什么学习Web前端一定要掌握JavaScript?在前端的世界里,没有什么是JavaScript实现不了的,关于JS有一句话:凡是可以用JavaScript来写的应用,最终都会用JavaScript,JavaScript可运行在所有主要平台的所有主流浏览器上,也可运行在每一个主流操作系统的服务器端上。现如今我们在为网站写任何一个主要功能的时候都需要有懂能够用JavaScript写前端的开发人员。</div>
                    </li>
                    <li><a href="/article/1950144063588069376.htm"
                           title="Serverless架构下Spring Function的创新实践" target="_blank">Serverless架构下Spring Function的创新实践</a>
                        <span class="text-muted">tmjpz04412</span>
<a class="tag" taget="_blank" href="/search/serverless/1.htm">serverless</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                        <div>引言:Serverless与Spring生态的交汇背景介绍:云计算与Serverless架构的兴起Spring生态的演进与云原生适配性核心问题:传统Spring应用如何融入Serverless范式Serverless架构的核心特征与挑战事件驱动、弹性伸缩与按需计费冷启动问题与性能优化需求Spring应用在Serverless环境中的典型瓶颈(如依赖注入、上下文初始化)SpringFunction的</div>
                    </li>
                    <li><a href="/article/1950143305194991616.htm"
                           title="小架构step系列25:错误码" target="_blank">小架构step系列25:错误码</a>
                        <span class="text-muted">秋千码途</span>
<a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>1概述一个系统中,可能产生各种各样的错误,对这些错误进行编码。当错误发生时,通过这个错误码就有可能快速判断是什么错误,不一定需要查看代码就可以进行处理,提高问题处理效率。有了统一的错误码,还可以标准化错误信息,方便把错误信息纳入文档管理和对错误信息进行国际化等。没有错误码的管理,开发人员就会按自己的理解处理这些错误。有些直接把堆栈直接反馈到前端页面上,使用看不懂这些信息体验很差,也暴露了堆栈信息有</div>
                    </li>
                    <li><a href="/article/1950140903616212992.htm"
                           title="Java朴实无华按天计划从入门到实战(强化速战版-66天)" target="_blank">Java朴实无华按天计划从入门到实战(强化速战版-66天)</a>
                        <span class="text-muted">岫珩</span>
<a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/%E6%97%B6%E9%97%B4%E5%AE%89%E6%8E%92/1.htm">时间安排</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92/1.htm">学习计划</a>
                        <div>致敬读者感谢阅读笑口常开生日快乐⬛早点睡觉博主相关博主信息博客首页专栏推荐活动信息文章目录Java朴实无华按天计划从入门到实战(强化速战版-66天)1.基础(18)1.1JavaSE核心(5天)1.2数据库与SQL(5天)1.3前端基础(8天)2.进阶(17天)2.1JavaWeb核心(5天)2.2Mybatis与Spring全家桶(6天)2.3中间件入门(4天)2.4实践项目(2天)3.高阶(1</div>
                    </li>
                    <li><a href="/article/1950133464426672128.htm"
                           title="OpenCloudOS 城市行·成都站圆满落幕,共探操作系统技术新未来!" target="_blank">OpenCloudOS 城市行·成都站圆满落幕,共探操作系统技术新未来!</a>
                        <span class="text-muted">CSDN资讯</span>
<a class="tag" taget="_blank" href="/search/%E4%B8%9A%E7%95%8C%E8%B5%84%E8%AE%AF/1.htm">业界资讯</a><a class="tag" taget="_blank" href="/search/%E8%B5%84%E8%AE%AF/1.htm">资讯</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
                        <div>7月26日,由OpenCloudOS社区主办,海光信息战略合作的OpenCloudOS城市行・成都站活动在成都欧洲中心圆满举办,线上观看人次超3万。当前,数字化浪潮席卷全球各行各业,在国产化进程加速的背景下,操作系统的技术突破与生态构建不仅是保障信息安全的关键,更是推动产业升级、实现高质量发展的必由之路。然而,国产操作系统在性能优化、安全防护、多场景适配等方面仍面临诸多挑战,亟需产业链上下游协同创</div>
                    </li>
                    <li><a href="/article/1950132582964326400.htm"
                           title="涵盖轻量级锁(SpinLock)与操作系统同步原语(如 CRITICAL_SECTION)的性能优化、Monitor 的原子性和数据竞争防护、Monitor.Wait 和 Pulse 在生产者-消费者" target="_blank">涵盖轻量级锁(SpinLock)与操作系统同步原语(如 CRITICAL_SECTION)的性能优化、Monitor 的原子性和数据竞争防护、Monitor.Wait 和 Pulse 在生产者-消费者</a>
                        <span class="text-muted">zhxup606</span>
<a class="tag" taget="_blank" href="/search/C%23%E5%AE%9E%E6%88%98%E6%95%99%E7%A8%8B/1.htm">C#实战教程</a><a class="tag" taget="_blank" href="/search/%E6%9D%8E%E5%B7%A5%E7%AF%87/1.htm">李工篇</a><a class="tag" taget="_blank" href="/search/wpf/1.htm">wpf</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/C%23/1.htm">C#</a>
                        <div>涵盖轻量级锁(SpinLock)与操作系统同步原语(如CRITICAL_SECTION)的性能优化、Monitor的原子性和数据竞争防护、Monitor.Wait和Pulse在生产者-消费者中的作用、控制线程执行顺序、Thread.Join的含义、避免嵌套锁的锁顺序策略,以及防止伪唤醒的条件检查。每个问题包括核心概念、实现细节、与之前讨论的关联(如线程池、进程间同步、分布式同步)、代码示例、测试用</div>
                    </li>
                    <li><a href="/article/1950132204336115712.htm"
                           title="《跨域资源共享CORS的深层逻辑与前端实践精要》" target="_blank">《跨域资源共享CORS的深层逻辑与前端实践精要》</a>
                        <span class="text-muted"></span>

                        <div>不同源头的资源交互已成为常态,而跨域资源共享(CORS)正是支撑这种交互的隐形架构。现代Web安全体系中平衡开放与防护的精妙设计。理解CORS的深层逻辑,不仅能解决实际开发中的跨域难题,更能触及网络安全与资源流通的核心矛盾,为前端工程师构建稳健的应用提供底层认知支撑。跨域资源共享的诞生,源于网络安全与应用发展的必然冲突。浏览器的同源策略,作为早期网络安全的基石,通过限制不同源文档的交互,有效阻挡了</div>
                    </li>
                    <li><a href="/article/1950131321980383232.htm"
                           title="深入了解 Kubernetes(k8s):从概念到实践" target="_blank">深入了解 Kubernetes(k8s):从概念到实践</a>
                        <span class="text-muted"></span>

                        <div>目录一、k8s核心概念二、k8s的优势三、k8s架构组件控制平面组件节点组件四、k8s+docker运行前后端分离项目的例子1.准备前端项目2.准备后端项目3.创建k8s部署配置文件4.部署应用到k8s集群在当今云计算和容器化技术飞速发展的时代,Kubernetes(简称k8s)已成为容器编排领域的事实标准。无论是互联网巨头、传统企业还是初创公司,都在广泛采用k8s来管理和部署容器化应用。本文将带</div>
                    </li>
                    <li><a href="/article/1950130692448907264.htm"
                           title="Vue CSR 到 Nuxt 3 SSR 迁移:技术实现与问题解决实录" target="_blank">Vue CSR 到 Nuxt 3 SSR 迁移:技术实现与问题解决实录</a>
                        <span class="text-muted">二倍速播放</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a>
                        <div>1.迁移动机与技术选型1.1CSR架构的局限性基于Vue3和Vite构建的客户端渲染(CSR)单页应用(SPA)提供了良好的开发体验和用户交互流畅性。但是其核心局限在于:搜索引擎优化(SEO):初始HTML响应仅包含一个根div元素,实际内容由JavaScript在浏览器端动态生成。虽然主流搜索引擎(如Google)能够执行部分JavaScript,但其抓取效率和稳定性不如直接获取完整HTML。非</div>
                    </li>
                    <li><a href="/article/1950119224630374400.htm"
                           title="大厂都在用的前端缓存策略,你掌握了吗?" target="_blank">大厂都在用的前端缓存策略,你掌握了吗?</a>
                        <span class="text-muted">AI架构全栈开发实战笔记</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E7%BC%93%E5%AD%98/1.htm">缓存</a><a class="tag" taget="_blank" href="/search/ai/1.htm">ai</a>
                        <div>大厂都在用的前端缓存策略,你掌握了吗?关键词:前端缓存、HTTP缓存、ServiceWorker、CDN缓存、缓存策略、性能优化、浏览器缓存摘要:本文将深入探讨前端开发中常用的缓存策略,从浏览器缓存到ServiceWorker,从HTTP缓存头到CDN缓存,全面解析大厂都在使用的高效缓存技术。通过生动的比喻和实际代码示例,帮助开发者理解并掌握这些提升Web应用性能的关键技术。背景介绍目的和范围本文</div>
                    </li>
                    <li><a href="/article/1950118087764275200.htm"
                           title="【服务器知识】nginx配置ipv6支持" target="_blank">【服务器知识】nginx配置ipv6支持</a>
                        <span class="text-muted">问道飞鱼</span>
<a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9B%B8%E5%85%B3/1.htm">服务器相关</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/ipv6/1.htm">ipv6</a>
                        <div>nginx配置ipv6支持Nginx全面支持IPv6配置指南一、基础IPv6配置1.启用IPv6监听2.IPv6地址格式说明二、高级IPv6配置1.双栈配置优化2.IPv6访问控制3.IPv6反向代理三、SSL/TLS配置1.IPv6SSL证书配置2.HSTS包含IPv6四、性能优化1.内核参数调优2.Nginx调优参数五、安全加固1.IPv6DDoS防护2.防止地址欺骗六、IPv6测试与诊断1.</div>
                    </li>
                    <li><a href="/article/1950115062781898752.htm"
                           title="26. 什么是雪碧图,作用和原理了解吗" target="_blank">26. 什么是雪碧图,作用和原理了解吗</a>
                        <span class="text-muted">yqcoder</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95-CSS/1.htm">前端面试-CSS</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a>
                        <div>总结小图合成一张,使用background来使用,减少资源请求一、什么是雪碧图?雪碧图(CSSSprite)是一种前端优化技术,指的是将多个小图标合并成一张大图,通过CSS控制显示其中的某一部分。它常用于网站中图标、按钮等小图较多的场景。二、雪碧图的作用作用说明✅减少HTTP请求次数合并多个图片为一张图,减少请求资源数✅提升页面加载速度尤其在图片较多时效果明显✅避免图片加载闪烁鼠标悬停切换图片时不</div>
                    </li>
                    <li><a href="/article/1950114810557427712.htm"
                           title="12. 什么是事件委托" target="_blank">12. 什么是事件委托</a>
                        <span class="text-muted">yqcoder</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95-CSS/1.htm">前端面试-CSS</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a>
                        <div>总结事件委托(EventDelegation)是JavaScript中一种重要的事件处理机制,它利用了事件冒泡的特性,将事件的处理程序绑定到父元素或祖先元素上,而不是直接绑定到具体的子元素上。什么是事件委托?事件冒泡:在DOM中,事件通常会从触发元素开始,然后逐级向上冒泡到其父元素、祖先元素,直到window对象。核心思想:事件委托的核心思想是利用事件冒泡机制,在父元素上监听事件,而不是在每个子元</div>
                    </li>
                    <li><a href="/article/1950112039502409728.htm"
                           title="H5UI微信小程序前端框架实战指南" target="_blank">H5UI微信小程序前端框架实战指南</a>
                        <span class="text-muted">ai</span>

                        <div>本文还有配套的精品资源,点击获取简介:H5UI是一个为微信小程序开发设计的前端框架,基于H5技术,提供简洁高效的组件库。框架集成了丰富的UI元素,如按钮、表格、导航栏等,简化了界面布局和交互的实现。通过安装、引入、使用组件和事件绑定四个步骤,开发者可以轻松构建功能齐全的应用。了解性能优化等注意事项对于高效开发同样重要。1.微信小程序前端开发框架介绍微信小程序概述微信小程序是微信官方推出的一种无需下</div>
                    </li>
                    <li><a href="/article/1950104854819041280.htm"
                           title="Ubuntu安装LAMP" target="_blank">Ubuntu安装LAMP</a>
                        <span class="text-muted">L_h1</span>
<a class="tag" taget="_blank" href="/search/%E6%B5%8B%E8%AF%95/1.htm">测试</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a>
                        <div>在安装vim时遇到了一个问题:E:无法获得锁/var/lib/dpkg/lock-frontend-open(11:资源暂时不可用)E:无法获取dpkg前端锁(/var/lib/dpkg/lock-frontend),是否有其他进程正占用它?解决办法:强制解锁sudorm/var/lib/dpkg/lock-frontendsudorm/var/cache/apt/archives/locksud</div>
                    </li>
                    <li><a href="/article/1950104727928762368.htm"
                           title="震惊!DOM变化监控神器MutationObserver,前端开发必知的隐藏武器!" target="_blank">震惊!DOM变化监控神器MutationObserver,前端开发必知的隐藏武器!</a>
                        <span class="text-muted">coding随想</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a>
                        <div>一、什么是MutationObserver?如果你是一个前端开发者,一定会遇到这样的场景:页面动态加载内容后,某些操作失效了。比如,你写了一个监听按钮点击的代码,但按钮是通过AJAX动态加载的,你的代码根本无法触发。这个时候,你就需要一个“监控哨兵”——MutationObserver,它能实时监听DOM树的变化,帮你捕获那些“暗中作祟”的节点变动。MutationObserver是HTML5引入</div>
                    </li>
                    <li><a href="/article/1950100186747432960.htm"
                           title="Python 中的 JWT 认证:从生成到验证的完整指南" target="_blank">Python 中的 JWT 认证:从生成到验证的完整指南</a>
                        <span class="text-muted">盛夏绽放</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E6%9C%89%E9%97%AE%E5%BF%85%E7%AD%94/1.htm">有问必答</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a>
                        <div>文章目录Python中的JWT认证:从生成到验证的完整指南一、JWT是什么?为什么需要它?传统session与JWT对比二、JWT的结构解析三、Python中实现JWT1.安装PyJWT包2.生成JWT3.验证JWT4.错误处理大全四、高级应用场景1.双令牌系统(Access+Refresh)详细说明表格:异常处理补充表:2.与FastAPI/Django集成五、安全最佳实践六、性能优化技巧算法性</div>
                    </li>
                                <li><a href="/article/57.htm"
                                       title="多线程编程之join()方法" target="_blank">多线程编程之join()方法</a>
                                    <span class="text-muted">周凡杨</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/JOIN/1.htm">JOIN</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">多线程</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a><a class="tag" taget="_blank" href="/search/%E7%BA%BF%E7%A8%8B/1.htm">线程</a>
                                    <div>现实生活中,有些工作是需要团队中成员依次完成的,这就涉及到了一个顺序问题。现在有T1、T2、T3三个工人,如何保证T2在T1执行完后执行,T3在T2执行完后执行?问题分析:首先问题中有三个实体,T1、T2、T3, 因为是多线程编程,所以都要设计成线程类。关键是怎么保证线程能依次执行完呢?   
Java实现过程如下: 
public class T1 implements Runnabl</div>
                                </li>
                                <li><a href="/article/184.htm"
                                       title="java中switch的使用" target="_blank">java中switch的使用</a>
                                    <span class="text-muted">bingyingao</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/enum/1.htm">enum</a><a class="tag" taget="_blank" href="/search/break/1.htm">break</a><a class="tag" taget="_blank" href="/search/continue/1.htm">continue</a>
                                    <div>java中的switch仅支持case条件仅支持int、enum两种类型。 
用enum的时候,不能直接写下列形式。 
 
 
switch (timeType) {
            case ProdtransTimeTypeEnum.DAILY:

                break;

            default:
                br</div>
                                </li>
                                <li><a href="/article/311.htm"
                                       title="hive having count 不能去重" target="_blank">hive having count 不能去重</a>
                                    <span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/hive/1.htm">hive</a><a class="tag" taget="_blank" href="/search/%E5%8E%BB%E9%87%8D/1.htm">去重</a><a class="tag" taget="_blank" href="/search/having+count/1.htm">having count</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E6%95%B0/1.htm">计数</a>
                                    <div>hive在使用having count()是,不支持去重计数 
  
hive (default)> select imei from t_test_phonenum where ds=20150701 group by imei having count(distinct phone_num)>1 limit 10;  
FAILED: SemanticExcep</div>
                                </li>
                                <li><a href="/article/438.htm"
                                       title="WebSphere对JSP的缓存" target="_blank">WebSphere对JSP的缓存</a>
                                    <span class="text-muted">周凡杨</span>
<a class="tag" taget="_blank" href="/search/WAS+JSP+%E7%BC%93%E5%AD%98/1.htm">WAS JSP 缓存</a>
                                    <div>      对于线网上的工程,更新JSP到WebSphere后,有时会出现修改的jsp没有起作用,特别是改变了某jsp的样式后,在页面中没看到效果,这主要就是由于websphere中缓存的缘故,这就要清除WebSphere中jsp缓存。要清除WebSphere中JSP的缓存,就要找到WAS安装后的根目录。 
       现服务</div>
                                </li>
                                <li><a href="/article/565.htm"
                                       title="设计模式总结" target="_blank">设计模式总结</a>
                                    <span class="text-muted">朱辉辉33</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a>
                                    <div>1.工厂模式 
  1.1 工厂方法模式 (由一个工厂类管理构造方法) 
     1.1.1普通工厂模式(一个工厂类中只有一个方法) 
     1.1.2多工厂模式(一个工厂类中有多个方法) 
     1.1.3静态工厂模式(将工厂类中的方法变成静态方法) 
&n</div>
                                </li>
                                <li><a href="/article/692.htm"
                                       title="实例:供应商管理报表需求调研报告" target="_blank">实例:供应商管理报表需求调研报告</a>
                                    <span class="text-muted">老A不折腾</span>
<a class="tag" taget="_blank" href="/search/finereport/1.htm">finereport</a><a class="tag" taget="_blank" href="/search/%E6%8A%A5%E8%A1%A8%E7%B3%BB%E7%BB%9F/1.htm">报表系统</a><a class="tag" taget="_blank" href="/search/%E6%8A%A5%E8%A1%A8%E8%BD%AF%E4%BB%B6/1.htm">报表软件</a><a class="tag" taget="_blank" href="/search/%E4%BF%A1%E6%81%AF%E5%8C%96%E9%80%89%E5%9E%8B/1.htm">信息化选型</a>
                                    <div>引言 
随着企业集团的生产规模扩张,为支撑全球供应链管理,对于供应商的管理和采购过程的监控已经不局限于简单的交付以及价格的管理,目前采购及供应商管理各个环节的操作分别在不同的系统下进行,而各个数据源都独立存在,无法提供统一的数据支持;因此,为了实现对于数据分析以提供采购决策,建立报表体系成为必须。 业务目标 
1、通过报表为采购决策提供数据分析与支撑 
2、对供应商进行综合评估以及管理,合理管理和</div>
                                </li>
                                <li><a href="/article/819.htm"
                                       title="mysql" target="_blank">mysql</a>
                                    <span class="text-muted">林鹤霄</span>

                                    <div>转载源:http://blog.sina.com.cn/s/blog_4f925fc30100rx5l.html 
mysql -uroot -p 
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) 
  
[root@centos var]# service mysql</div>
                                </li>
                                <li><a href="/article/946.htm"
                                       title="Linux下多线程堆栈查看工具(pstree、ps、pstack)" target="_blank">Linux下多线程堆栈查看工具(pstree、ps、pstack)</a>
                                    <span class="text-muted">aigo</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a>
                                    <div>原文:http://blog.csdn.net/yfkiss/article/details/6729364 
  
1. pstree 
pstree以树结构显示进程$ pstree -p work | grep adsshd(22669)---bash(22670)---ad_preprocess(4551)-+-{ad_preprocess}(4552)  &n</div>
                                </li>
                                <li><a href="/article/1073.htm"
                                       title="html input与textarea 值改变事件" target="_blank">html input与textarea 值改变事件</a>
                                    <span class="text-muted">alxw4616</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div>// 文本输入框(input) 文本域(textarea)值改变事件 
// onpropertychange(IE) oninput(w3c) 
$('input,textarea').on('propertychange input', function(event) { 
     console.log($(this).val()) 
}); 
  </div>
                                </li>
                                <li><a href="/article/1200.htm"
                                       title="String类的基本用法" target="_blank">String类的基本用法</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/String/1.htm">String</a>
                                    <div>  
字符串的用法; 
    // 根据字节数组创建字符串 
byte[] by = { 'a', 'b', 'c', 'd' };
String newByteString = new String(by); 
  
  
    1,length()  获取字符串的长度 
  
  &nbs</div>
                                </li>
                                <li><a href="/article/1327.htm"
                                       title="JDK1.5 Semaphore实例" target="_blank">JDK1.5 Semaphore实例</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/thread/1.htm">thread</a><a class="tag" taget="_blank" href="/search/java%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">java多线程</a><a class="tag" taget="_blank" href="/search/Semaphore/1.htm">Semaphore</a>
                                    <div>Semaphore类 
       一个计数信号量。从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。 
S</div>
                                </li>
                                <li><a href="/article/1454.htm"
                                       title="使用GZip来压缩传输量" target="_blank">使用GZip来压缩传输量</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/GZip/1.htm">GZip</a>
                                    <div>        启动GZip压缩要用到一个开源的Filter:PJL Compressing Filter。这个Filter自1.5.0开始该工程开始构建于JDK5.0,因此在JDK1.4环境下只能使用1.4.6。 
        PJL Compressi</div>
                                </li>
                                <li><a href="/article/1581.htm"
                                       title="【Java范型三】Java范型详解之范型类型通配符" target="_blank">【Java范型三】Java范型详解之范型类型通配符</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>  
  
定义如下一个简单的范型类, 
  
package com.tom.lang.generics;

public class Generics<T> {
    private T value;
    public Generics(T  value) {
        this.value = value;
    }
} </div>
                                </li>
                                <li><a href="/article/1708.htm"
                                       title="【Hadoop十二】HDFS常用命令" target="_blank">【Hadoop十二】HDFS常用命令</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a>
                                    <div>1. 修改日志文件查看器 
  
hdfs oev -i edits_0000000000000000081-0000000000000000089 -o edits.xml
cat edits.xml 
  
修改日志文件转储为xml格式的edits.xml文件,其中每条RECORD就是一个操作事务日志 
  2. fsimage查看HDFS中的块信息等 
&nb</div>
                                </li>
                                <li><a href="/article/1835.htm"
                                       title="怎样区别nginx中rewrite时break和last" target="_blank">怎样区别nginx中rewrite时break和last</a>
                                    <span class="text-muted">ronin47</span>

                                    <div>在使用nginx配置rewrite中经常会遇到有的地方用last并不能工作,换成break就可以,其中的原理是对于根目录的理解有所区别,按我的测试结果大致是这样的。  
 
 location /    
 {     
     proxy_pass http://test; </div>
                                </li>
                                <li><a href="/article/1962.htm"
                                       title="java-21.中兴面试题 输入两个整数 n 和 m ,从数列 1 , 2 , 3.......n 中随意取几个数 , 使其和等于 m" target="_blank">java-21.中兴面试题 输入两个整数 n 和 m ,从数列 1 , 2 , 3.......n 中随意取几个数 , 使其和等于 m</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class CombinationToSum {

	/*
第21 题
2010 年中兴面试题
编程求解:
输入两个整数 n 和 m ,从数列 1 , 2 , 3.......n 中随意取几个数 ,
使其和等</div>
                                </li>
                                <li><a href="/article/2089.htm"
                                       title="eclipse svn 帐号密码修改问题" target="_blank">eclipse svn 帐号密码修改问题</a>
                                    <span class="text-muted">开窍的石头</span>
<a class="tag" taget="_blank" href="/search/eclipse/1.htm">eclipse</a><a class="tag" taget="_blank" href="/search/SVN/1.htm">SVN</a><a class="tag" taget="_blank" href="/search/svn%E5%B8%90%E5%8F%B7%E5%AF%86%E7%A0%81%E4%BF%AE%E6%94%B9/1.htm">svn帐号密码修改</a>
                                    <div>问题描述: 
     Eclipse的SVN插件Subclipse做得很好,在svn操作方面提供了很强大丰富的功能。但到目前为止,该插件对svn用户的概念极为淡薄,不但不能方便地切换用户,而且一旦用户的帐号、密码保存之后,就无法再变更了。 
解决思路: 
     删除subclipse记录的帐号、密码信息,重新输入</div>
                                </li>
                                <li><a href="/article/2216.htm"
                                       title="[电子商务]传统商务活动与互联网的结合" target="_blank">[电子商务]传统商务活动与互联网的结合</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E7%94%B5%E5%AD%90%E5%95%86%E5%8A%A1/1.htm">电子商务</a>
                                    <div> 
 
      某一个传统名牌产品,过去销售的地点就在某些特定的地区和阶层,现在进入互联网之后,用户的数量群突然扩大了无数倍,但是,这种产品潜在的劣势也被放大了无数倍,这种销售利润与经营风险同步放大的效应,在最近几年将会频繁出现。。。。 
 
       如何避免销售量和利润率增加的</div>
                                </li>
                                <li><a href="/article/2343.htm"
                                       title="java 解析 properties-使用 Properties-可以指定配置文件路径" target="_blank">java 解析 properties-使用 Properties-可以指定配置文件路径</a>
                                    <span class="text-muted">cuityang</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/properties/1.htm">properties</a>
                                    <div>#mq 
xdr.mq.url=tcp://192.168.100.15:61618; 
 
import java.io.IOException; 
import java.util.Properties; 
 
 
public class Test { 
 
 String conf = "log4j.properties"; 
 private static final</div>
                                </li>
                                <li><a href="/article/2470.htm"
                                       title="Java核心问题集锦" target="_blank">Java核心问题集锦</a>
                                    <span class="text-muted">darrenzhu</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%9F%BA%E7%A1%80/1.htm">基础</a><a class="tag" taget="_blank" href="/search/%E6%A0%B8%E5%BF%83/1.htm">核心</a><a class="tag" taget="_blank" href="/search/%E9%9A%BE%E7%82%B9/1.htm">难点</a>
                                    <div>注意,这里的参考文章基本来自Effective Java和jdk源码 
 
 
1)ConcurrentModificationException 
当你用for each遍历一个list时,如果你在循环主体代码中修改list中的元素,将会得到这个Exception,解决的办法是: 
1)用listIterator, 它支持在遍历的过程中修改元素, 
2)不用listIterator, new一个</div>
                                </li>
                                <li><a href="/article/2724.htm"
                                       title="1分钟学会Markdown语法" target="_blank">1分钟学会Markdown语法</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/markdown/1.htm">markdown</a>
                                    <div>markdown 简明语法 基本符号 
 
 *,-,+ 3个符号效果都一样,这3个符号被称为 Markdown符号  
 空白行表示另起一个段落 
 `是表示inline代码,tab是用来标记 代码段,分别对应html的code,pre标签 
 换行 
 
 单一段落( <p>) 用一个空白行 
 连续两个空格 会变成一个 <br> 
 连续3个符号,然后是空行</div>
                                </li>
                                <li><a href="/article/2851.htm"
                                       title="Gson使用二(GsonBuilder)" target="_blank">Gson使用二(GsonBuilder)</a>
                                    <span class="text-muted">eksliang</span>
<a class="tag" taget="_blank" href="/search/json/1.htm">json</a><a class="tag" taget="_blank" href="/search/gson/1.htm">gson</a><a class="tag" taget="_blank" href="/search/GsonBuilder/1.htm">GsonBuilder</a>
                                    <div>转载请出自出处:http://eksliang.iteye.com/blog/2175473 一.概述 
    GsonBuilder用来定制java跟json之间的转换格式 
  二.基本使用 
实体测试类: 
温馨提示:默认情况下@Expose注解是不起作用的,除非你用GsonBuilder创建Gson的时候调用了GsonBuilder.excludeField</div>
                                </li>
                                <li><a href="/article/2978.htm"
                                       title="报ClassNotFoundException: Didn't find class "...Activity" on path: DexPathList" target="_blank">报ClassNotFoundException: Didn't find class "...Activity" on path: DexPathList</a>
                                    <span class="text-muted">gundumw100</span>
<a class="tag" taget="_blank" href="/search/android/1.htm">android</a>
                                    <div>有一个工程,本来运行是正常的,我想把它移植到另一台PC上,结果报: 
 
 java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.mobovip.bgr/com.mobovip.bgr.MainActivity}: java.lang.ClassNotFoundException: Didn't f</div>
                                </li>
                                <li><a href="/article/3105.htm"
                                       title="JavaWeb之JSP指令" target="_blank">JavaWeb之JSP指令</a>
                                    <span class="text-muted">ihuning</span>
<a class="tag" taget="_blank" href="/search/javaweb/1.htm">javaweb</a>
                                    <div>  
要点 
  
JSP指令简介  
page指令  
include指令  
  
JSP指令简介  
  
JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。 
JSP指令的基本语法格式: 
<%@ 指令 属性名="</div>
                                </li>
                                <li><a href="/article/3232.htm"
                                       title="mac上编译FFmpeg跑ios" target="_blank">mac上编译FFmpeg跑ios</a>
                                    <span class="text-muted">啸笑天</span>
<a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a>
                                    <div>1、下载文件:https://github.com/libav/gas-preprocessor, 复制gas-preprocessor.pl到/usr/local/bin/下, 修改文件权限:chmod 777 /usr/local/bin/gas-preprocessor.pl 
2、安装yasm-1.2.0 
curl http://www.tortall.net/projects/yasm</div>
                                </li>
                                <li><a href="/article/3359.htm"
                                       title="sql mysql oracle中字符串连接" target="_blank">sql mysql oracle中字符串连接</a>
                                    <span class="text-muted">macroli</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/SQL+Server/1.htm">SQL Server</a>
                                    <div>有的时候,我们有需要将由不同栏位获得的资料串连在一起。每一种资料库都有提供方法来达到这个目的: 
 
 MySQL: CONCAT() 
 Oracle: CONCAT(), || 
 SQL Server: + 
 
CONCAT() 的语法如下: 
Mysql 中 CONCAT(字串1, 字串2, 字串3, ...): 将字串1、字串2、字串3,等字串连在一起。 
请注意,Oracle的CON</div>
                                </li>
                                <li><a href="/article/3486.htm"
                                       title="Git fatal: unab SSL certificate problem: unable to get local issuer ce rtificate" target="_blank">Git fatal: unab SSL certificate problem: unable to get local issuer ce rtificate</a>
                                    <span class="text-muted">qiaolevip</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E6%B0%B8%E6%97%A0%E6%AD%A2%E5%A2%83/1.htm">学习永无止境</a><a class="tag" taget="_blank" href="/search/%E6%AF%8F%E5%A4%A9%E8%BF%9B%E6%AD%A5%E4%B8%80%E7%82%B9%E7%82%B9/1.htm">每天进步一点点</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a><a class="tag" taget="_blank" href="/search/%E7%BA%B5%E8%A7%82%E5%8D%83%E8%B1%A1/1.htm">纵观千象</a>
                                    <div>// 报错如下: 
$ git pull origin master 
fatal: unable to access 'https://git.xxx.com/': SSL certificate problem: unable to get local issuer ce 
rtificate 
  
// 原因: 
由于git最新版默认使用ssl安全验证,但是我们是使用的git未设</div>
                                </li>
                                <li><a href="/article/3613.htm"
                                       title="windows命令行设置wifi" target="_blank">windows命令行设置wifi</a>
                                    <span class="text-muted">surfingll</span>
<a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a><a class="tag" taget="_blank" href="/search/wifi/1.htm">wifi</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0%E6%9C%ACwifi/1.htm">笔记本wifi</a>
                                    <div>还没有讨厌无线wifi的无尽广告么,还在耐心等待它慢慢启动么 
教你命令行设置 笔记本电脑wifi: 
 
1、开启wifi命令 
 

netsh wlan set hostednetwork mode=allow ssid=surf8 key=bb123456
netsh wlan start hostednetwork
pause
 
 其中pause是等待输入,可以去掉 
 
2、</div>
                                </li>
                                <li><a href="/article/3740.htm"
                                       title="Linux(Ubuntu)下安装sysv-rc-conf" target="_blank">Linux(Ubuntu)下安装sysv-rc-conf</a>
                                    <span class="text-muted">wmlJava</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a><a class="tag" taget="_blank" href="/search/sysv-rc-conf/1.htm">sysv-rc-conf</a>
                                    <div>安装:sudo apt-get install sysv-rc-conf 使用:sudo sysv-rc-conf 
操作界面十分简洁,你可以用鼠标点击,也可以用键盘方向键定位,用空格键选择,用Ctrl+N翻下一页,用Ctrl+P翻上一页,用Q退出。 
  
  
背景知识 
sysv-rc-conf是一个强大的服务管理程序,群众的意见是sysv-rc-conf比chkconf</div>
                                </li>
                                <li><a href="/article/3867.htm"
                                       title="svn切换环境,重发布应用多了javaee标签前缀" target="_blank">svn切换环境,重发布应用多了javaee标签前缀</a>
                                    <span class="text-muted">zengshaotao</span>
<a class="tag" taget="_blank" href="/search/javaee/1.htm">javaee</a>
                                    <div>更换了开发环境,从杭州,改变到了上海。svn的地址肯定要切换的,切换之前需要将原svn自带的.svn文件信息删除,可手动删除,也可通过废弃原来的svn位置提示删除.svn时删除。 
  
然后就是按照最新的svn地址和规范建立相关的目录信息,再将原来的纯代码信息上传到新的环境。然后再重新检出,这样每次修改后就可以看到哪些文件被修改过,这对于增量发布的规范特别有用。 
  
检出</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>