JS性能优化必读

  • 晋升就是将新生代对象移动至老生代

  • 一轮GC还存活的新生代需要晋升

  • 当前在拷贝的一个过程中发现To空间的使用率超过了25%,那么在这时我们也需要将这一次的活动对象移动至老生代

  • 为什么是25%?

  • 因为我们在将来进行回收操作的时候需要把Form空间和To空间进行一个交换,也就是说以前的To会变成From,以前的From会变为To,这也就意味着我们的To使用率如果达到了80%,那么最终他变成活动对象的存储空间后,新的对象就存不进来了

[](()V8如何回收老生代对象

=====================================================================

[](()概述


  • 老生代对象存放在右侧老生代区域

  • 针对于老生代区域,在V8当中同样有一个内存大小的限制

  • 64位操作系统1.4G,32位操作系统700MB

  • 老生代对象其实指的就是当前存活时间较长的对象

  • 例如:全局对象下的变量,闭包内的变量数据

[](()回收流程


  • 主要采用标记清除,标记整理,增量标记算法

  • 首先使用标记清除完成垃圾空间的回收

  • 存在空间碎片化的问题

  • 如果我们发现当他去想把我们新生代区域的内容,往老生代进行移动的时候,而且这个时间节点上我们老生代存储区域的共建又不足以来存放新生代存储区所以移动过来的对象,也就是我们之前提到过的晋升,这种情况就会触发标记整理,将之前的碎片化空间整理为地址上是连贯的

  • 采用增量标记进行效率优化

[](()回收细节对比


  • 新生代区域垃圾回收使用空间换时间

  • 采用的是复制算法,意味着每时每刻内部都会有一个空闲空间的存在,但是由于新生代存储区,本身的空间就很小,所以分出来的空间就更小,所以这部分空间的浪费,相对于带来的时间上的提升是微不足道的

  • 老生代区域垃圾回收不适合复制算法

  • 老生代空间很大,一分为二的话就有几百MB的空间是浪费不用的,这样就太过于浪费

  • 老生代空间存储的数据其实比较多,所以在复制时消耗的时间也会更多

[](()V8垃圾回收总结

==================================================================

  • V8是一款主流的JS执行引擎

  • V8内存设置上限

  • 本身是针对与浏览器设置的,所以在webAPP上,这个内存是充足的

  • 内部的垃圾回收机制来决定,如果内存再放大,那垃圾回收时间就可能会超过用户的感知,影响体验

  • V8采用分代回收思想实现垃圾回收

  • 新生代

  • 老生代

  • V8垃圾回收常见的GC算法

  • 新生代

  • 复制算法 + 标记整理算法

  • 老生代

  • 标记清除算法 + 标记整理算法 + 增量标记算法

[](()Performance工具介绍

=========================================================================

  • 关于GC的目的就是为了实现内存空间的良性循环

  • 良性循环的基础是对内存空间有一个合理的使用

  • 时刻关注才能确定是否合理

  • Preformance提供多种监控方式

总结:

  • 通过Performance时刻监控内存,有这样一个操作后,我们就可以在程序的内存出现一些问题之后,直接想办法定位到当前问题所在的代码块

基本使用步骤

  • 打开浏览器输入目标网址

  • 进入开发人员工具面板,选择性能

  • 开启录制功能,访问具体界面

  • 执行用户行为,一段时间后定制录制

  • 分析界面中记录的内存信息

[](()内存问题的体现

=================================================================

  • 页面出现延迟加载或者经常性暂停 — 频繁的垃圾回收

  • 页面持续性出现糟糕的性能 — 内存膨胀

  • 页面的性能随时间延长越来越差 — 内存泄漏

[](()监控内存

==============================================================

[](()界定内存问题的标准


  • 内存泄漏 — 内存使用持续升高

  • 内存膨胀 — 在多数设备上都存在性能问题

  • 频繁的垃圾回收 — 通过内存变化图进行分析

[](()监控内存的几种方式


  • 浏览器任务管理器

  • 可以直接以数值的形式,将我们当前应用程序在执行过程中内存的变化体现出来

  • Timeline时序图记录

  • 直接把我们应用程序执行过程中所有内存的走势以时间点的方式呈现出来,有了这张图很容易做判断

  • 堆快照查找分离DOM

  • 有针对性的查找我们当前的界面对象中,是否存在分离的DOM,因为分离DOM的存在就是一种内存泄漏

[](()任务管理器监控内存

===================================================================

  • 如果我们最后一列小括号内的数值一直增大,那就意味着这个内存是有问题的

  • 具体来说是什么问题当前这个工具就显得不是特别好用了,因为它只能帮助我们发现这个地方有没有问题,如果说我们想定位问题时,他就不太好用了

  • 在这个地方我们可以直接通过shift + esc调出任务管理器

  • 找到我们想要去监控的具体脚本,也就是说web页面

  • 选中之后如果说没有JS这一列我们可以直接右键然后勾选

  • 调整完后我们只需要关注两列

  • 第一列为当前DOM节点占用的内存,一般情况也是不变为好,如果要变的话就证明我们当前界面存在频繁的DOM操作

  • 第二列为最后的JS内存,在这里我们要关注的就是小括号内的数值,得出的结论就是如果小括号里的数值一直增加而没有变小的过程,就意味着我们的内存就一直往上走,而没有GC消耗,所以这个时候就有问题了

[](()Timeline记录内存

======================================================================

  • 任务管理器可以帮助我们发现问题,但是具体定位的话就显得不是很方便

  • Timeline — 通过时间线记录内存变化的方式 — 更精确的定位到我们当前内存的问题与那一块代码是相关的,或者说在什么时间节点上发生的

[](()堆快照查找分离DOM

====================================================================

  • 什么是分离DOM

  • 界面元素存活在DOM树上

  • 垃圾对象时的DOM节点

  • 分离状态的DOM节点

  • 总结:

  • 我们可以利用浏览器当中提供的堆快照的功能,然后把我们当前的堆进行性拍照,拍照结束后找这里边是否存在一些分离DOM,因为这个分离DOM在界面中不体现,但是在内存中的确存在,所以这个时候它是一种内存的浪费,我们要做的就是定位到我们的代码里的那些分离DOM所在的位置,然后想办法给清除掉

[](()判断是否存在频繁GC

====================================================================

  • 为什么需要知道是否存在频繁的垃圾回收

  • 当GC工作时应用程序是停止的

  • GC频繁工作而且时间过长,对web应用很不友好

  • 因为它会处于一个假死的状态,对于用户来说就会感觉到整个应用有点卡顿

  • 所以就要想办法确定当前应用中是否存在着频繁的回收

  • Timeline中频繁的上升下降

  • 任务管理器中数据频繁的增加减小

[](()Performance总结

=======================================================================

  • 谷歌浏览器提供的一个性能工具

  • Performance使用流程

  • 内存问题相关分析

  • 内存泄漏

  • 内存膨胀

  • 频繁的垃圾回收

  • Performance 时序图监控内存变化

  • 任务管理器监控内存变化

  • 堆快照查找分离DOM

[](()代码优化介绍

================================================================

[](()如何精准测试JS性能


  • 本质上就是采集大量的执行样本进行数学统计和分析,从而得到一个比对的结果,什么样的脚本执行效率更高,这样一个过程对于我们编码者来说显得有些麻烦,因为我们可能更多地只是关注该如何使用脚本实现某一个功能,而不是去做大量的数学统计

  • 所以在这里我们采用 基于 Benchmark.jshttps://jsperf.com/ 完成

[](()Jsperf 使用流程


  • 使用GitHub账 《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】 号登录

  • 填写个人信息(非必填)

  • 填写详细的测试用例信息(title、slug)

  • sulg — 必须是唯一的,因为他要生成一个空间来利于我们去访问自己的测试用例

  • 填写准备代码(DOM操作时经常使用)

  • 填写必要有setup与teardown代码

  • 填写测试代码片段

[](()慎用全局变量

================================================================

  • 在程序执行过程中,如果针对于某些数据需要进行存储,那我们可以尽可能的把它放置在局部作用域中变成一个局部变量

  • 当我们在一个全局作用域内定义一个变量后,他其实就是存在于全局的执行上下文当中,是所有作用域链的顶端

  • 如果按照这种层级向上查找的一个过程来说,那下边某些局部作用域没有找到的变量,最终都会去查找到最顶端的全局执行上下文,所以这种情况下,我们的时间消耗是非常大的,这样一来就降低了我们当前代码的执行效率

  • 在当前全局上下文当中去定义的变量,他一直是存活于上下文执行栈,这个栈是直到我们当前程序退出之后才会消失的

  • 这对我们当前的GC工作来说也是非常不利的,因为只要我们的GC发现这样一个变量,属于一个存活的状态我们就不会把他当做垃圾对象进行回收,因此这样的做法也会降低我们当前的程序运行过程中对于内存的一个使用

  • 如果某个局部作用域当中出现了同名全局变量则有可能会遮蔽或污染全局

  • 总体来说,我们使用全局变量时需要考虑更多的事情,否则就会给我们带来一些意想不到的情况

[](()缓存全局变量

================================================================

  • 在我们程序的执行过程中,有些地方,我们针对于这样的全局变量使用,是无法避免的,例如:查找DOM时,我们必须要使用document,而这个document并不是由我们自己直接定义的,而是存在于当前顶层对象下边,内置好了,可以直接用

  • 这时我们可以选择把大量需要重复使用的一个全局变量放置到一个局部作用域当中,从而达成一种缓存的效果

[](()通过原型对象添加附加方法

======================================================================

  • 在JS当中存在三种概念

  • 构造函数

  • 原型对象

  • 实例对象

  • 在这个过程当中,我们的实例对象和构造函数都是可以指向原型对象的,所以这个过程中如果某一个构造函数的内部局有一个成员方法,让我们后续的实例对象都需要频繁的去进行调用,那么在这里可以直接把他添加在原型对象上,而不需要把他放在我们的构造函数内部,而这样两种实现方式呢,在进程上也会有所差异。

[](()避开闭包陷阱

================================================================

  • 外部具有指向内部的引用

  • 在 外 部作用域内访问 内 部作用域的数据

function foo () {

var name = ‘xl’

function fn () {

console.log(name)

}

return fn

}

var a = foo()

a()

  • 关于闭包

  • 闭包是一种强大的语法

  • 闭包使用不当很容易出现内存泄露

  • 不要为了闭包而闭包

// function foo() {

// var el = document.getElementById(‘btn’)

// el.onclick = function () {

// console.log(el.id)

// }

// }

// foo()

/**

  • 标准闭包

    1. 外部对内部有引用
    1. 突破作用域的限制,在一个作用域中用到另一个作用于的数据
  • 因为它是闭包,所以会产生一个不好的影响,就是当前这样一个内存是会泄露的

  • 由于闭包语法的存在,导致我们函数中的一些变量是没有办法被回收

  • 这种情况下类似于这样的操作越来越多的时候,对我们的内存来说是非常不友好的

  • 当我们的内存一直被消耗,我们程序的性能肯定是越来越差

  • */

function foo() {

var el = document.getElementById(‘btn’)

el.onclick = function () {

console.log(el)

}

el = null

}

foo()

/**

  • 关于btn这个元素其实本身就已经存在于我们的DOM当中或者说在body里

  • 所以就算我们这里边不通过el再去引用这样的一个必填的DOM节点,那他本身也会有一个引用的存在

  • 而我们在这个地方是相当于在它的引用之上,又去让一个el也引用了这样一个DOM节点

  • 所以我们也可以理解为是这样一个DOM节点呗我们引用了两次,如果说在将来某一个时间点下

  • 我们这个DOM节点从我们的界面中消失了,也就是说删除或者清除的行为

  • 这是我们要想明白,我们在界面把元素删除,也就意味着在DOM上的引用就不存在了

  • 但是会有一个问题:代码里的el对我们之前的DOM还是有引用的,所以根据我们之前所提到的引用计数这个原则

  • 我们这个DOM对象其实只是减少了一个引用,而代码里边仍在引用这他,所以我们的垃圾回收机制在工作时无法回收

  • 因此我们的泄露依然存在,而如果说加了 el = null 时,当我们在界面当中去把页面的元素清除掉之后,DOM对它的引用就消失了

  • 而代码里边对他的引用也消失了,这个时候我们这个样一个内存空间得以释放,这也是这行代码存在的意义

  • */

  • 这就是如何避开闭包的陷阱,而提高整个程序的性能,不是我们两段代码在执行速度上的一个对比,指的是整个内存层面上的性能对比

[](()避免属性访问方法使用

====================================================================

  • 关于属性访问语法,这是和面向对象相关的,为了更好地实现这样一个封装性,所以在更多的时候我们可能会讲一些对象的基本属性和方法,放在一个函数的内部,然后在外部暴露一个方法对当前这个属性进行增删改查,但是这个特性在JS特效中其实并不是那么的适用,因为在JS中不需要属性的访问方法,所有的属性在外部都是可见的

  • 而且在使用属性访问方法的时候,他就相当于是增加了一层重定义,对当前的访问控制是没有太多的一个意义,所以不去推荐

function Person1() {

this.name = ‘xl’

this.age = 18

this.getAge = function () {

return this. age

}

}

const p1 = new Person1()

const a = p1.getAge() // 属性访问方法

function Person2() {

this.name = ‘xl’

this.age = 18

}

const p2 = new Person2()

const b = p2.age // 直接访问

[](()For循环优化

=================================================================

  • for循环是我们经常会去使用的语法结构,每当我们遇到一个数组结构或者类数组时,我们想要把其中一些值进行拿出使用时,都可以使用for循环对其进行遍历

for (var i = 0; i < btns.length; i++) {

console.log(i)

}

for (var i = 0, len = btns.length; i < len; i++) {

console.log(i)

}

[](()选择最优的循环方法

===================================================================

  • for、foeEach、for…in…

var arrList = new Array(1, 2, 3, 4, 5, 6, 7, 8, 9)

arrList.forEach(function (item) {

console.log(item)

})

for (var i = arrList.length; i; i–) {

console.log(arrList[i])

}

for (var i in arrList) {

console.log(i)

}

// forEach > for > for…in…

[](()文档碎片优化节点添加

====================================================================

  • 针对于web应用的开发来说,DOM节点的操作是很频繁的,而针对于DOM的交互操作又很消耗性能,特别是创建一个新的节点,将它添加至界面中时,这个过程会伴随着回流和重绘的出现

  • 回流和重绘对于性能的消耗又很大

for (var i = 0; i < 10; i++) {

var op = document.createElement(‘p’)

op.innerHTML = i

document.body.appendChild(op)

}

const fragEle = document.createDocumentFragment()

for (var i = 0; i < 10; i++) {

var op = document.createElement(‘p’)

op.innerHTML = i

fragEle.appendChild(op)

}

document.body.appendChild(fragEle)

[](()克隆优化节点操作

==================================================================

for (var i = 0; i < 3; i++) {

var op = document.createElement(‘p’)

op.innerHTML = i

document.body.appendChild(op)

}

var oldP = document.getElementById(‘box’)

for (var i = 0; i < 3; i++) {

var newP = oldP.cloneNode(false)

op.innerHTML = i

document.body.appendChild(op)

}

[](()直接量替换new Object

=========================================================================

var a = [1, 2, 3]

var a1 = new Array(3)

a1[0] = 1

a1[1] = 2

a1[2] = 3

// a > a1

[](()JSBench使用

===================================================================

  • 一个可以在线测试JS代码执行效率的网站

  • JSBench.me

  • 为什么要使用JSBench

  • 因为我们一直使用jsperf这个网站已经不再维护了,所以没有办法使用了

  • 我们有一些JS相关的内容需要去分享,而通过整个网站可以很方便的去在我们去测试部分代码时直接去进行使用

  • 我们需要说明的是,其实测试我们这种代码的执行效率的网站其实有很多,比如我们可以直接使用代码进行时间统计,还有其他的在线网站

  • 选择这个网站也是综合考虑了他的打开速度以及在使用的快捷方面

  • 注意点:

  • 当前这个工具在做测试是尽量只保留一个标签页

  • 在执行代码测试时,尽可能让这个页面停留在这里,或者说当前页面不要关掉

  • 不能执行一遍得出的结论和我们想要的结果一样或者不一样时就认为它是合理的或者是最终的答案,我们应该让当前脚本多执行几次,然后取几率更高的结果

  • 性能测试过程中,我们不应该去纠结于这个代码的执行时间,我们当前去使用这个工具去运行的代码其实主要的还是跑一下代码执行的速度,但是对于我们的性能测试来说,我们关注的他并不是只有时间,他只是众多性能指标当中的一个,一段代码的执行速度快并不意味着他就很健壮

  • 对于我们的代码在执行的过程中所涉及到的性能,其实就是两个方面,要么拿空间换时间,要么拿时间换空间

[](()堆栈中的JS执行过程

====================================================================

  • 这段我们来看一下关于代码在执行过程中偏底层发生的一些事情,这样的操作可以更加具象的表达出一段代码在栈内存和堆内存里是如何执行的,同样的也有利于我们理解GC回收内存的工作过程

let a = 10;

function foo (b) {

let a = 2

function baz © {

console.log(a + b + c)

}

return baz

}

let fn = foo(2)

fn(3)

[](()减少判断层级

================================================================

  • 具体来说就是在我们编写代码的过程中,有可能会去出现判断条件嵌套的场景,往往再出现if…else多层嵌套的时候,我们都可以去通过提前return掉无效的条件来达到嵌套层级的优化效果

// function dys (part, chapter) {

// const parts = [‘ES2016’, ‘ES2017’, ‘ES2018’, ‘ES2019’, ‘ES2020’]

// if (part) {

// if (parts.includes(part)) {

// console.log(‘当前属于这个模块’)

// if (chapter > 5) {

// console.log(‘您需要提供VIP’)

// }

// }

// } else {

// console.log(“请确认模块信息”)

// }

// }

// dys(‘ES2016’, 6)

function dys (part, chapter) {

const parts = [‘ES2016’, ‘ES2017’, ‘ES2018’, ‘ES2019’, ‘ES2020’]

if (!part) {

console.log(“请确认模块信息”)

return

}

if (!parts.includes(part)) return

console.log(“当前属于这个模块”)

if (chapter > 5) {

console.log(“您需要提供VIP”)

}

}

dys(‘ES2016’, 6)

/**

  • 优化的核心思想和作用域链的查找和内存的空间的使用并不是那么的有关系或者直观

  • 因为我们是从代码的整体的一个写法进行的改变

  • 首先就是代码量

  • 再有就是上边的代码就是一层一层的套,下边的直接去判断一层够了

  • 相当于我们判断问题的算法得到一个改变

  • 明确类条件的分支建议使用 switch…case…

  • if…else…一般适合于区间的条件判断,如果我们明确了几个枚举值的时候建议使用switch…case…

  • 这样代码会更加清晰,易于维护

  • 不过易于维护的代码并不代表执行速度就很快,这点对于性能优化来说还是要看我们需要什么来决定的

  • */

[](()减少作用域链查找层级

====================================================================

  • 每当一个函数执行的时候会产生一个执行上下文,例如我们当前代码中的foo函数,他的上下文就定义了函数执行的环境,当这个该函数执行结束这个上下文就会被销毁掉,这也取决于是否有闭包的存在,因此我们多次调用函数时就会多次创建上下文,这些上下文他都是有自己的作用域的,这些作用域之间呢又可以去通过作用域链进行连接,所以在函数执行过程中,就会先去搜索他自己的作用域,例如fn函数中需要访问age和name,他首先会在自己内部找,发现有age那就用自己的就可以,没有name,这个时候会沿着作用域链向上查找,他上层就是父作用域,如果在父作用域内没有找到,就继续往上找,父的上层在我们的代码中其实就是全局,这样一层一层的查找,基于这样的查找过程,如果说变量存在于离我们当前fn更近的地方,那查找所消耗的时间就会更快一些,那代码的执行效率会更高

// var name = ‘xl’

// function foo () {

// name = ‘xl666’ // name属于全局 — 修改了全局变量

// function fn () {

// var age = 38

// console.log(age)

// console.log(name)

// }

// fn()

// }

// foo()

var name = ‘xl’

function foo () {

var name = ‘xl666’ // name属于局局 — 修改了局部变量

function fn () {

var age = 38

console.log(age)

console.log(name)

}

fn()

}

foo()

[](()减少数据读取次数

==================================================================

  • 这种的快,是建立在我们空间的消耗上的提速,后续主要看我们的需求是看重速度还是空间

// var box = document.getElementById(‘skip’)

// function hasbox (ele, cls) {

// return ele.className === cls

// }

// console.log(hasbox(box, ‘skip’))

var box = document.getElementById(‘skip’)

function hasbox (ele, cls) {

var clsname = ele.className

return clsname === cls

}

console.log(hasbox(box, ‘skip’))

[](()字面量与构造式

=================================================================

  • 不同的数据声明方式和性能的关系,我们肯定都听过在编码是使用字面量来代替构造的方式创建数据,但是这种方式是有一些特定场景的,例如我们在处理日期,数组类型的数据的时候,无论我们是采用字面量还是构造的方式,我们最终得到的数据他们都是引用类型的

// let test = () => {

// let obj = new Object()

// obj.name = ‘xl’

// obj.age = 18

// obj.slogan = ‘你好小鹿’

// return obj

// }

// console.log(test())

/**

  • 上边的方式就好像是在调用一个函数,而下边在创建时其实就是直接去开辟空间在里边存东西

  • 这个过程又涉及到了一个函数的调用,所以就相当于他做的事情更多一些

  • */

let test = () => {

let obj = {

name : ‘xl’,

age : 18,

slogan : ‘你好小鹿’

}

return obj

}

console.log(test())

var str1 = ‘xl说我为XX学代码’

/**

  • 上边的代码其实就是一行字符串,下边的是一个对象

  • */

var str2 = new String(‘xl说我为XX学代码’)

[](()减少循环体中活动

==================================================================

  • 主要讨论循环的这样一个功能,而不是现在要去采用那种结构实现一种循环,所以这里就只是采用了for结构

  • 放在循环体中的,往往都是一些我们想要去重复执行的事情,再循环次数固定的情况下,循环体做的事情越多,那执行效率就会越慢,反之效率就会越高

  • 所以第一个思路就是把每次循环都要去用到的或者说都要操作的数据值不变的都抽离到外部去完成,这个道理类似于数据缓存

// var test = () => {

// var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

// for (var i = 0; i < arr.length; i++) {

// console.log(arr[i])

// }

// }

// test()

// var test = () => {

// var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],

// i ,

// len = arr.length

// for (i = 0; i < len; i++) {

// console.log(arr[i])

// }

// }

// test()

var test = () => {

var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],

len = arr.length

while (len–) {

console.log(arr[len])

}

}

test()

[](()减少声明及语句数

==================================================================

  • 代码编写中的小细节与性能的关系

  • 变量的声明

  • 语句数的多少,其实就是表达式的多少

  • 我们都知道,程序员是可以记录、保存以及修改状态的,而正是这些状态让我们可以利用这些语言完成很多复杂的功能,很明显,如果我们要去记录这些功能,我们就应该消耗更多的空间,因此对于那些我们后续不需要轮番使用的数据,我们应该去使用的时候直接进行获取,而不是提前去做缓存

  • 目的在于可以降低程序在执行时当前对内存的消耗

var box = document.getElementById(‘box’)

var test = (ele) => {

let w = ele.offsetWidth

let h = ele.offsetHeight

return w * h

}

console.log(test(box))

var box = document.getElementById(‘box’)

var test = (ele) => {

return ele.offsetWidth * ele.offsetHeight

}

console.log(test(box))

var test = () => {

var name = ‘xl’

var age = 18

var slogan = ‘xl666lx’

return name + ’ ’ + age + ’ ’ + slogan

}

console.log(test())

var test = () => {

var name = ‘xl’,

age = 18,

slogan = ‘xl666lx’

return name + ’ ’ + age + ’ ’ + slogan

}

console.log(test())

[](()惰性函数与性能

=================================================================

  • 我们在使用的时候往往会把惰性函数看成高阶函数

var box = document.getElementById(‘box’)

function foo () {

console.log(this)

}

// function addEvent (obj, type, fn) {

// if (obj.addEventListener) {

// obj.addEventListener(type, fn, false)

// }else if (obj.attachEvent) {

// obj.attachEvent(‘on’ + type, fn)

// } else {

// obj[‘on’ + type] = fn

// }

// }

// addEvent(box, ‘click’, foo)

function addEvent (obj, type, fn) {

if (obj.addEventListener) {

addEvent = obj.addEventListener(type, fn, false)

}else if (obj.attachEvent) {

addEvent = obj.attachEvent(‘on’ + type, fn)

} else {

addEvent = obj[‘on’ + type] = fn

}

return addEvent

}

addEvent(box, ‘click’, foo)

[](()采用事件绑定

================================================================

  • 事件委托 — 利用事件冒泡把原本需要绑定在子元素上的响应时间委托给了父元素,让父元素去完成时间的监听,好处是减少了大量内存的占用,从而也可以去减少事件的注册,针对他的考点呢,我们一般给去一堆li的列表结构,要求不用考虑兼容性的问题,在这种情况下给每一个li添加一个事件处理,然后最终要达到的一个效果就是性能最优,这个时候可以直接采用事件委托

// var list = document.querySelectorAll(‘li’)

// function showTxt(ev) {

// console.log(ev.target.innerHTML)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值