zl程序教程

您现在的位置是:首页 >  IT要闻

当前栏目

js面试知识点笔记

2023-02-18 16:41:46 时间

ES6的新语法,说出它们和ES5的区别

const 是一个常量只允许声明一次不可修改(和let有快级作用域) let不存在变量提升机制(变量不允许在声明之前使用) let不允许重复声明 在全局作用域中基于let声明的变量不是window的一个属性,和他没关系 typeof 未被声明的变量 =>不是undefined而是报错(暂时性死区) let会形成块级作用域(类似于私有作用域,大部分大括号都会形成块作用域) 解构赋值 “…” 拓展、剩余、展开运算符 ES6中的模板字符串 箭头函数 和普通函数的区别 没有arguments,但是可以基于…arg获取实参集合(结果是一个数组) 没有自己的this,箭头函数中的this是上下文中的this Promise(async/await) class(ES6中创建类的) interator(for of 循环) Map / Set

请说出你对 “重排(回流)和重绘读写分离” 的理解!

思路: 1.首先说出什么是重排和重绘 (.突出他们耗性能 ) 浏览器渲染一个页面的时候是按照“先创建DOM树->在加载CSS->生成渲染树 RENDER TREE->把渲染树交给浏览器(GPU)进行绘制”,如果后期我们修改了元素的样式(但是没有改变大小和位置),浏览器会把当前元素重新生成渲染树,然后重新渲染,这个机制是重绘,但是一旦元素的位置或者大小等发生改变,浏览器就要从DOM树重新计算渲染,这个机制是回流(重排),无论是重排还是重绘都非常的消耗性能 2。 常用的解决方案: 需要动态向页面追加元素的时候,基于文档碎片或者先把需要增加的所有元素拼接成字符串,最后统一进行增加 读写分离:把统一修改样式都放到一起执行,新版浏览器都有一个自己检测的机制,如果发现下面紧挨着的操作也是修改元素的样式,会把所有修改的事先存起来,直到遇到非修改样式的操作,会把之前存储的统一执行,引发一次回流和重绘

重绘和回流(重排)的区别和关系 重绘:当渲染树中的元素外观(如:颜色)发生改变,不影响布局时,产生重绘 回流:当渲染树中的元素的布局(如:尺寸、位置、隐藏/状态状态)发生改变时,产生重绘回流 注意:JS 获取 Layout 属性值(如:offsetLeft、scrollTop、getComputedStyle 等)也会引起回流。因为浏览器需要通过回流计算最新值 回流必将引起重绘,而重绘不一定会引起回流

谈谈你对面向对象的理解

JS本身就是面向对象编程的 JS本身就是基于面向对象(OOP)编程思想开发出来的语言,我们学习JS就是在学习JS中的类和实例,例如: 数组是Array的实例、对象是Object的实例、函数是Function的实例…,在这些内置类的原型上有很多公共的属性和方法,这些方法可以被实例调用,我们学习JS就是学习这些方法… 面向对象真实项目的应用 平时的业务逻辑开发,我没有刻意使用类的方式来做,只有在一些组件或者插件封装的时候才会基于构造函数和原型链使用类和实例完成,例如:我之前封装过一些 TAB页卡、轮播图、模态框、表单验证等插件,就是这样处理的(我之前看了一些类库和插件的源码,也都是基于面向对象封装的) 面向对象中的一些语法和特点 所谓面向对象就是基于class或者function创建一个类,执行的时候new执行创建一个实例,这样实例就可以调取类上提供的方法,想要基于面向对象进行插件封装,必须掌握关于类的继承、封装和多态,封装就是提取公共的方法、JS中没有严格意义的多态,不能进行方法的重写,常用的继承方式有很多,例如:原型继承、call继承、寄生组合继承、es6中的继承等,有些方式会存在一些问题,我项目中后来都是基于class中的extend实现继承的

谈一下你对作用域链和原型链的理解

作用域链 函数执行会形成一个私有的作用域,形参和在当前私有作用域中声明的变量都是私有变量,当前的私有作用域有自我保护机制,私有变量和外界是没有关系的,但是如果私有作用域中遇到一个非私有的变量,则向它的上级作用域找,如果还不是上级作用域私有的,则继续向上查找,一直找到window为止。这种变量一层层向上查找的机制就是“作用域链机制”

原型链 它也是一种查找机制,实例首先在自己的私有属性中进行属性的查找,如果不是私有属性,基于__proto__ 向所属类的原型上进行查找,如果在找不到,则继续基于__proto__ 向上查找,一直找到Object.prototype为止,例如:obj.hasOwnProperty() 这里调取的hasOwnProperty这个属性就是找到Object.prototype才找到的

实现数组去重你都有哪些办法?

对象键值对处理

    // 在数组这个类的原型上增加一个方法,那么以后数组的实例就可以使用这个方法了
    Array.prototype.unique=function(){
           // 克隆原有数组, 使去重后不影响现有数组
           let result=[].concat(this)
           //声明一个对象的属性为数组的值,如果存在这个属性说明数组已经有重复的值了,我们把删除即可
           let obj={}   
           for(let i=0;i<result.length;i++){
            let item=result[i]
            if(obj[item]){
            result.splice(i,1) 
          // 删除了数组的一个位数字,那么数组的长度最会往前减一,所以i要减1 
            i--             
            continue
            }
            obj[item]=true
           }
          return result
       }

双循环(性能最弱)

  // 获取当前的数组项,和后面数组的每一项比较,如果存在相同就把他删除
   Array.prototype.unique = function () {
            let result = [].concat(this)
            for (let i = 0, iLen = result.length; i < iLen; i++) {
                let item = result[i]
                for (let j = i + 1, jLen = result.length; j < jLen; j++) {
                    if (item === result[j]) {
                        result.splice(i, 1)
                          // 这种删除数组方式性能上更好,因为splice每删除一次都要往前计算他的长度
                         // result[i]=result[result.length-1]
                        // result.length--
                        i--
                    }
                }
            }
            return result
        }

indexOf 原理和双循环差不多

// 利用slice获取i+1到最后 和 i 项,用indexOf 查找后面是否有当前项 i项,有就删除
 Array.prototype.unique = function () {
            let result = [].concat(this)
            for (let i = 0, iLen = result.length; i < iLen; i++) {
                let item = result[i]
                let nextItemAll=result.slice(i+1)
                if(nextItemAll.indexOf(item)>-1){
                    result.splice(i,1)
                    i--
                }
            }
            return result
        }

sort将数组排序,然后相邻的比较

  Array.prototype.unique = function () {
            let result =[]
            let ary=this.sort((a,b)=>{a-b}) //数组升序
            for (let i = 0, iLen = ary.length; i < iLen; i++) {
             let item=ary[i]
             let itemNext=ary[i+1]
             if(item!==itemNext){
                 result.push(item)
             }
            }
            return result
        }

写出你掌握的JS继承方式,项目中什么时候你用到了继承?

[封装]

把实现一个功能的JS代码进行封装,主要目的:“低耦合高内聚”

[多态]

重载:方法名相同,参数的个数或者类型不同,此时名字相同的方法叫做方法的重载(后台语言中的重载),JS中不存在重载的
重写:子类重写父类的方法

[继承]

子类继承父类的属性和方法
1. 原型继承
2. call继承
3. 寄生组合继承
4. ES6中class类实现继承
//=>根据传递参数的不同执行不同的方法(后台语言)
public void fn(int n,int m){

}
public void fn(string n,string m){

}
public void fn(int n,int m,int z){

}

原型继承

 // 原型继承:让子类的原型指向父类的一个实例
 function Parent(){
   this.name="chimpanzee"
}
Parent.prototype={
   constructor:Parent,
   getName:function(){
   return this.name   
} 
}

function Son(){
 this.age=18
}
Son.prototype=new Parent()
let son=new Son()
   // Son {
        //     age: 18
        // }
        // age: 18
        // __proto__: Parent
        // name: "chimpanzee"
        // __proto__:
        //     constructor: ƒ Parent()
        // getName: ƒ()
        // __proto__: Object

原型继承通过 Son.prototype=new Parent() 用儿子的原型指向父亲的实例,父亲的实例本身具备了父类的私有属性和共有方法,子类的原型指向它,那么子类Son的实例就可以找到这些属性和方法了 存在的问题 子类可以重写父类原型上的方法,子类和父类还有联系

 Son.prototype._proto_.getName=""

CALL继承

function Parent() {
            this.name = "chimpanzee"
        }
        Parent.prototype = {
            constructor: Parent,
            getName: function () {
                return this.name
            }
        }

        function Son() {
            Parent.call(this)
            this.age = 18
        }
        let son=new Son()
        console.log(son.name) // chimpanzee 

把父类Parent做为普通函数执行,让Parent中的this变为Son的实例,相当于给Son的实例增加一些属性和方法

寄生组合继承
function Parent() {
            this.name = "chimpanzee"
        }
        Parent.prototype = {
            constructor: Parent,
            getName: function () {
                return this.name
            }
        }

        function Son() {
            this.age = 18
            Parent.call(this)
        }
        Son.prototype = {
            constructor: Son,
            getAge: function () {
                return this.name
            }
        }
       Son.prototype=Parent.prototype //这种方式可以轻易修改父类Parent原型的方法和属性
       // Object.create:内置Object类天生自带的方法
          创建一个空对象
          让新创建空对象的__proto__指向第一个传递进来的对象 
       Son.prototype=Object.create(Parent.prototype)
       Son.prototype.__proto__.getName=""
       let son=new Son()
       let parent=new Parent()
       console.log(parent.getName) // ""
       // index.html:56 Uncaught TypeError: son.getName is not a function 
       console.log(son.getName()) 
       console.log(Son.prototype.getName())
       console.log(Parent.prototype.getName()) //这个还是报错,老师讲的我理解的有误差,后面查资料解决

ES6中的类和继承

  class Woman {
            constructor() {
               this.name="linjiyou"
               this.age=18
            }
            getName() {
               return this.name
            }
            //设置静态方法和属性,static和实例是没有关系,调用只能是 Woman.[key],只能设置方法不能设置属性
            static getAge(){
             return this.age
            }
        }
        // 这样写和往类里面添加方法一样实例可以使用
         Woman.prototype.getSex=function(){
            return this.sex
        }
        // 这种要类名才可以使用
        Woman.getMoney=function(){
            return "no money"
        }
        // 这个也是要类名才可以使用
        Woman.annualSalary=120000
 //

继承

 class Preson extends Woman { //=>extends类似于实现了原型继承
          constructor() {
              super(); //=>类似于CALL继承:在这里SUPER相当于把A的CONSTRUCTOR给执行了,并且让方法中的THIS是B的实例,SUPER当中传递的实参都是在给A的CONSTRUCTOR传递
              this.y = 200;
           }
          getY() {
             console.log(this.y);
           }
      }

JS中有一个insertBefore方法,目的是实现把新元素插入到指定元素之前,现在你实现一个 InsertAfter 方法,把新元素插入到指定元素之后!

 function insertAfter(newEle, originEle) {
            let parent = originEle.parentNode
            let originEleNext = originEle.nextElementSibling
            if (originEle) {
                parent.insertBefore(newEle, originEleNext)
            } else {
                parent.appendChild(newEle)
            }

        }
        let originEle = app.querySelector(".a")
        let newEle = document.createElement("div")
        newEle.innerText = "3"
        insertAfter(newEle, originEle)

实现 一个$attr(domId,name,value)遍历id是domId的,内部属性为name且值为value的元素?

 let $attr = (domID, name, value) => {
            //1.先获取当前页面中所有的标签
            let tagList = document.getElementsByTagName('*');
            //2.在获取的所有标签中按照ID/NAME/VALUE进行筛选(数组内置方法:filter)
            tagList = [].slice.call(tagList); //=>把类数组转换为数组
            //=> tagList=[...tagList] 基于ES6中的展开运算符完成,让TAG-LIST等于一个数组,数组中的每一项是把之前的类数组展开后得到的
            tagList = tagList.filter(item => {
                //=>item.name:只有表单元素这样才可以获取到值,普通元素需要基于getAttribute获取值
                return item.id === domID && item.getAttribute(name) === value;
            });
            return tagList;
        };

说出你所掌握的算法

  • 常用的算法
  • 递归
  • 去重
  • 冒泡排序
  • 插入排序
  • 快速排序
  • 时间复杂度
  • 空间复杂度
  • KMP
  • … 递归:函数自己调用自己执行就是递归 (递归是基于条件判断的:因为我们不能形成死递归,在某个条件下我们需要结束递归操作) 需求:在1~100之间获取即是3也是5的倍数(也就是15的倍数)的和
let total = 0;
for (let i = 1; i <= 100; i++) {
    if (i % 15 === 0) {
        total += i;
    }
}
function fn(n) {
    if (n > 100) return 0;
    if (n % 15 === 0) {
        return n + fn(n + 1);
    }
    return fn(n + 1);
}
数组扁平化
let result = [],
    fn = function (ary) {
        if (ary.length === 0) return;
        for (let i = 0; i < ary.length; i++) {
            let item = ary[i];
            if (typeof item === 'object') {
                fn(item);
            } else {
                result.push(item);
            }
        }
    };

谈谈你对闭包的理解

=>谈谈你对闭包的理解 闭包是JS中一个非常重要的机制,我们很多编程思想、业务逻辑、设计模式都是基于闭包完成的,先说一下我对闭包的理解:闭包就是函数执行产生一个私有的作用域(不销毁),在这个作用域中的私有变量和外界互不干扰,而且作用域(栈)不销毁,这些私有变量存储的值也都保存下来了,所有整体来说闭包就是为了保护和保存变量的 实际项目开发中,很多地方使用到了闭包,例如: 1.循环事件绑定,由于事件绑定是异步编程的,我们此时在循环的时候把索引存储起来(可以基于自定义属性存储,也可以基于闭包存储),后期需要使用的时候,向上级作用域查找使用即可 2.平时做业务逻辑的时候,我一般都是基于单例模式来管理代码的,这种单例的构建就应用到了闭包

let xxxRender=(function(){
    return {
        init:function(){

        }
    }
})();

3.我之前在学习资料上了解了柯理化函数思想,它其实也是基于闭包完成的

Function.prototype.bind = function bind(context, ...arg) {
    return () => {
        fn.call(context, ...arg);
    }
};
document.onclick=fn.bind(obj, 10, 20);

还有很多地方也应用了闭包,但是闭包比较占内存,我会尽量减少对它的使用,但是有些需求必须要用

如下一个字符串 “54389”,要求将字符串中的阿拉伯数字替换成我们的中文大写数字”伍肆叁捌玖”,请使用正则的方式进行处理

let str = '54389',
    ary = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
str = str.replace(/\d/g, item => {
    //=>item =>arg[0] =>正则每一次捕获的内容 5/4/3/8/9
    //=>把捕获的数字做为索引,到ARY中找到对应的汉字,用找到的结果替换当前捕获的内容
    return ary[item];
});
console.log(str);

在javascript对象上定义一个repeatify函数,这个函数接受一个整数参数,来明确子字符串需要重复几次,这个函数要求字符串重复指定的次数,比如:’abc’.repeatify(3);//”abcabcabc”

String.prototype.repeatify = function repeatify(n = 1) {
    //=>this:需要处理的字符串
    let result = '';
    for (let i = 0; i < n; i++) {
        result += this;
    }
    return result;
};
console.log('abc'.repeatify());

call、apply、bind的区别

1.改变函数中的THIS(并且让函数执行) 2.可以基于CALL让类数组借用数组原型上的方法(例如:借用SLICE实现把类数组转换为数组) 3.可以基于CALL实现继承 4.可以基于APPLY获取数组中的最大值和最小值

有两个升序数组,然后将他们合为 一个数组并进行升序排序?

let ary1 = [1, 2, 3, 4, 5],
    ary2 = [2, 3, 4, 5, 6];
// let ary = ary1.concat(ary2).sort((a, b) => a - b);
let ary = [...ary1, ...ary2].sort((a, b) => a - b);

瀑布流的实现原理

1.并排排列三列,三列没有具体的高度,靠内容撑开 2.通过API接口地址,基于AJAX,从服务器端获取数据,拿出数据的前三项依次插入到三列中(数据绑定) 3.计算目前三列的高度,按照高度由小到大把三列进行排序,再次拿出获取数据中的三条,按照排好序的LI依次插入…一直基于这个规律插入完成即可 4.当用户下拉到页面底部,加载更多的数据即可

使用jquery实现点击按钮弹出一个对话框(对话框在整个页面正中间,并且最初页面中没有任何的HTML标签)?

$(function () {
    /*//=>当页面结构加载完成执行函数
    $('#link').on('click', function () {
        $('<div class="center"></div>').appendTo(document.body);
    });*/
});

$('#nav>li>a').on('click', function (ev) {
    //=>this:当前点击的A
    //=>$(this):当前点击的A(JQ对象)

    //=>阻止点击A标签页面跳转的行为
    ev.preventDefault();

    //=>准备数据
    let $this = $(this),
        $p = $this.parent();
    let obj = {
        index: $p.index() + 1,
        name: $this.text(),
        link: $this.attr('href')
    };
    console.log(obj);
});

打开一个浏览器,在地址栏输入一个网址,按下ENTER键,到看到整个页面,中间都经历了哪些事情?

【HTTP请求阶段:向服务器发送请求】 1.浏览器首先向DNS域名解析服务器发送请求 2.DNS反解析:根据浏览器请求地址中的域名,到DNS服务器中找到对应的服务器外网IP地址 3.通过找到的外网IP,向对应的服务器发送请求(首先访问的是服务器的WEB站点管理工具:准确来说是我们先基于工具在服务器上创建很多服务,当有客户端访问的时候,服务器会匹配出具体是请求哪个服务) 4.通过URL地址中携带的端口号,找到服务器上对应的服务,以及服务所管理的项目源文件

【HTTP响应阶段:服务器把客户端需要的内容准备好,并且返回给客户端】 5.服务器端根据请求地址中的路径名称、问号传参或者哈希值,把客户端需要的内容进行准备和处理 6.把准备的内容响应给客户端(如果请求的是HTML或者CSS等这样的资源文件,服务器返回的是资源文件中的源代码[不是文件本身])

【浏览器渲染阶段】 7.客户端浏览器接受到服务器返回的源代码,基于自己内部的渲染引擎(内核)开始进行页面的绘制和渲染 ->首先计算DOM结构,生成DOM TREE ->自上而下运行代码,加载CSS等资源内容 ->根据获取的CSS生成带样式的RENDER TREE ->开始渲染和绘制

前端开发性能优化方案

减少HTTP请求次数和请求大小 代码优化 ->有利于SEO ->有利于扩展维护 ->有利于减少性能消耗 [JS代码优化的108条建议] [雅虎CSS优化的36条建议] … DNS及HTTP通信方式的优化

1.在JS中尽量减少闭包的使用(原因:闭包会产生不释放的栈内存) A:循环给元素做事件绑定的时候,尽可能的把后期需要的信息(例如索引)存储到元素的自定义属性上,而不是创建闭包存储 B:可以在最外层形成一个闭包,把一些后续需要的公共信息进行存储,而不是每一个方法都创建闭包(例如单例模式) C:尽可能的手动释放不被占用的内存 …

2.尽量合并CSS和JS文件(把需要引入的CSS合并为一个,JS也是合并为一个),原理是在减少HTTP请求次数,尽可能的把合并后的代码进行压缩,减小HTTP请求资源的大小 A:webpack这种自动化构建工具,可以帮我们实现代码的合并和压缩(工程化开发) B:在移动开发(或者追求高性能的PC端开发[例如百度首页]),如果CSS或者JS不是需要很多,我们可以选择把css和js编程内嵌式(也就是代码直接写在HTML中)

3.尽量使用字体图标或者SVG图标,来代替传统的PNG等格式的图片(因为字体图标等是矢量图(基于代码编写出来的),放大不会变形,而且渲染速度快,相对比位图要小一些)

4.减少对DOM的操作(主要是减少DOM的重绘和回流(重排)) A:关于重排的分离读写 B:使用文档碎片或者字符串拼接做数据绑定(DOM的动态创建)

5.在JS中避免“嵌套循环”(这种会额外增加很多循环次数)和“死循环”(一旦遇到死循环浏览器就卡壳了)

6.采用图片的“懒加载”(延迟加载) 目的是为了减少页面“第一次加载”过程中HTTP的请求次数,让页面打开速度变快 步骤:开始加载页面的时候,所有的真实图片都不去发送HTTP请求加载,而是给一张占位的背景图,当页面加载完,并且图片在可视区域内我们再去做图片加载

7.利用浏览器和服务器端的缓存技术(304缓存),把一些不经常更新的静态资源文件做缓存处理(例如:JS、CSS、静态图片等都可以做缓存) 原理是为了减少HTTP请求大小,让获取速度更快

8.尽可能使用事件委托(事件代理)来处理事件绑定的操作,减少DOM的频繁操作,其中包括给每一个DOM元素做事件绑定

9.尽量减少CSS表达式的使用(expression)

#myDiv {
  position: absolute;
  width: 100px;
  height: 100px;
  left: expression(document.body.offsetWidth - 110 + "px");
  top: expression(document.body.offsetHeight - 110 + "px");
  background: red;
}

10.CSS选择器解析规则是从右向左解析

 .container .link a{
    先找到所有的A,再筛选是在.link样式类中的,再次筛选是在.container样式类中的... 先找到的是所有的A,操作起来是消耗性能的,我们在使用CSS选择器的时候尽可能减少对标签选择器的使用
 }

11.CSS雪碧图技术(css sprite / css 图片精灵) 把所有相对较小资源图片汇总到一张大图上,后期我们只需要把大图加载下来,用背景定位的方式展示对应的小图即可

.bg{
  background:url('xxx.png');
}
.box1{
   background-position:xx xx;
}
.box2{
   background-position:xx xx;
}

<div class='bg box1'></div>

13.减少对于COOKIE的使用(最主要的是减少本地COOKIE存储内容的大小),因为客户端操作COOKIE的时候,这些信息总是在客户端和服务器端传来传去

14.页面中的数据获取采用异步编程和延迟分批加载 使用异步获取数据,是为了降低HTTP通道的堵塞,不会因为数据没有请求回来耽误下面信息的渲染,提高页面的打开速度(我们可以这样处理:需要动态绑定数据的区域先隐藏,等数据返回并且绑定完成后在让其显示) 延迟分批加载类似于图片懒加载,是为了减少第一次页面加载时候的HTTP请求次数

15.页面中出现音视频标签,我们不让页面加载的时候就去加载这些资源(要不然页面加载速度会变慢)(方案:只需要设置 preload=‘none’ 即可),等待页面加载完成,音视频播放的时候我们在去加载音视频资源

16.在客户端和服务器端进行信息交互的时候,对于多项数据我们尽可能基于JSON格式来进行传送(JSON格式的数据处理方便,资源偏小) ==>相对于XML格式的传输才会有这个优势

17.尽可能实现JS的封装(低耦合高内聚),减少页面中的冗余代码(减少HTTP请求资源的大小)

20.CSS中设置定位后,最好使用Z-INDEX改变盒子的层级,让所有的盒子不在相同的平面上,这样后续处理的时候,性能有那么一丢丢的提高

21.在基于AJAX的GET请求进行数据交互的时候,根据需求可以让其产生缓存(这个缓存不是304缓存),这样下一次从相同地址获取的数据是上一次缓存的数据(但是很少用,项目中一般刻意清除这个缓存的时候偏多)

22.尽量减少对于filter滤镜属性的使用(这个属性消耗性能较大一些)

23.在CSS导入的时候尽量减少使用@import导入式,因为@import是同步操作,只有把这个对应的CSS导入,才会向下加载,而link是异步操作

24.配置ETag(有点类似于304缓存)

25.使用window.requestAnimationFrame(JS中的帧动画)代替传统的定时器动画

26.减少递归的使用,避免死递归,避免由于递归导致的栈内存嵌套(建议使用尾递归)

27.避免使用iframe(不仅不好管控样式,而且相当于在A页面中加载了其它页面,消耗较大)

28.利用H5中提供的localstorage本地存储或者是manifest离线缓存,做一些信息的本地存储,下一次加载页面的时候直接从本地获取,减少HTTP请求次数

29.基于SCRIPT调取JS的时候,可已使用 defer或者async 来异步加载

重量级优化:做CDN加速(烧钱机器)

=额外技巧= 1.我们一般都把CSS放到BODY上,把JS放到BODY下面(原因:让其先加载CSS在加载JS,先加载CSS是为了保证页面渲染的过程中,元素是带着样式渲染的,而JS一般都是用来操作DOM元素的,需要等到元素加载完再操作)

2.能用CSS搞定的绝对不用JS,能用原生JS搞定的绝对不用插件,绝对不使用FLASH(除了音视频的低版本浏览器播放) =>CSS处理动画等功能的性能优于JS,而且CSS中的transform变形还开起了硬件加速

3.JS中尽量减少对EVAL的使用,因为JS合并压缩的时候,可能出现由于符号不完善,导致的代码执行优先级错乱问题,EVAL处理起来消耗的性能也是偏大一点的

4.使用keep-alive实现客户端和服务器端的长连接

5.尽量使用设计模式来管理我们的代码(单例、构造、Promise、发布订阅),方便后期的升级和维护

6.开启服务器端的gzip压缩(这个压缩可以有效减少请求资源文件的大小),其实客户端的图片等资源也是可以进行压缩的(但是对于24位的位图,压缩后可能会变模糊)

7.页面中不要出现无效的链接(利于SEO优化),还有其它技巧:提高关键字曝光率、img需要加alt、设置meta标签、标签语义化…

8.避免使用with语句(非常耗性能)