js中提前声明
- 变量声明提升:声明(只是声明,但不涉及赋值)都被提前到函数的顶部,而变量赋值操作留在原来的位置
- 函数声明提升:函数的声明在执行前会被提升到该作用域顶部,所以可以把函数声明放在调用它的语句后面。
- 将变量声明提升,只会提升变量,⽽不会提升变量所拥有的值。
- 将函数声明提升,会把函数声明连同函数内的内容⼀块提升。
事件委托
事件代理(Event Delegation),又称之为事件委托。是JavaScript中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown......)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡
事件冒泡
- 点击子元素的事件 事件会传递给父元素 继续执行 事件触发包含响应,
- 事件源:触发事件的对象
- 事件名:click,mouseover等
- 事件处理函数: onclick,onmouseover 事件触发的三个阶段
- 捕获阶段:从window对象传导到目标节点(上层传到底层)称为捕获阶段,捕获阶段不会响应任何事件
- 目标阶段:从目标阶段触发,被称为目标阶段
- 冒泡阶段:从目标节点传导回window对象(从底层传到上次)称为冒泡阶段
- 阻止事件冒泡: e.stopPropagation();
函数节流(防抖)
概念
- 函数节流: 指定时间间隔内只会执行一次任务;
- 预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期
- 函数防抖: 任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。
- 当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间
- 函数节流(throttle)与 函数防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。
- window对象的resize、scroll事件
- 拖拽时的mousemove事件
- 文字输入、自动完成的keyup事件 区别
- 函数防抖:如果有人进电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这时如果又有人进电梯了(在10秒内再次触发该事件),我们又得等10秒再出发(重新计时)。
- 函数节流 :保证如果电梯第一个人进来后,10秒后准时运送一次,这个时间从第一个人上电梯开始计时,不等待,如果没有人,则不运行
轮循监听 & websocket
Ajax轮询
- 设置定时器 setInterval() 让浏览器每隔几秒就发送一次请求,询问服务器是否有新的信息。也就是说,客户端发起连接后,如果没有消息,就一直不返回response给客户端,直到有消息才返回,客户端再次建立连接,周而复始(太过被动,服务器不能主动联系客户端,只能由客户端发起)
WebSocket
当服务器完成协议升级后(HTTP-WebSocket), 服务端就可以主动推送消息给客户端,只需要经过一次HTTP请求,就可以做到源源不断的信息传送,这样的协议就解决了上面同步有延迟,而且还非常消耗资源的这种情况
WebSocket的事件触发机制
WebSocket编程遵循异步编程模式,也就是说只要WebSocket连接打开,应用程序就简单的监听事件,客户端不需要主动轮询服务器得到信息,要开始监听事件,只要为WebSocket对象添加回调函数,也可以使用addEventListener()DOM方法为WebSocket对象添加事件监听器
WebSocket对象4个不同的事件
- let websocket = new WebSocket('ws:URL');
- open
- 一旦服务器建立了WebSocket连接请求,open事件触发并建立一个连接,open事件对应的回调函数称作onopen,到open事件触发时,协议握手完成,WebSocket已经准备好发送和接受数据。如果应用程序接收到一个open事件,那么可以确定WebSocket服务器成功处理了连接请求,并同意与应用程序通信。websocket.onopen() => {}
- message
- message 事件在接受消息时触发,该事件的回调函数是onmessage
- error
- error事件在响应意外故障时触发,与该事件对应的回调函数是onerror,错误还是导致WebSocket关闭,接收一个error事件,可以预期很快就会触发close事件,close事件中的代码和原因有可能会返回错误的根源,error事件处理程序是服务器重新连接逻辑以及处理来自WebSocket对象异常的最近位置
- close
- close事件在WebSocket连接关闭时触发,对应于close事件函数是onclose,一旦连接关闭,客户端和服务器不再能接收或者发送信息。
- close有三个属性 可以进行错误处理与恢复,wasClean,code,error。
- wasClean 属性是一个布尔属性,表示连接是否顺利关闭,如果WebSocket的关闭时对来自服务器的一个close帧的响应,则该属性为true,如果连接是因为其他原因关闭,则该属性为false,code和error属性表示服务器发送的关闭握手状态,这些属性和WebSocket.close()方法中的code和error一致
响应式编程
- 其基于数据流和变化传播
函数式编程
- 将电脑运算视为函数的运算,函数为第一等公民
构建工具
- Webpack gulp等
回调地狱
- 异步请求容易构成回调地狱
-
拆解function:将各步拆解为单个的function
-
事件发布/监听模式
- 一方面监听某一事件,当事件发生时,进行相应回调操作,另一方面当某些操作完成之后,通过发布事件触发函数,这样就可以将原本捆绑在一起的代码解耦
-
Promise
readFile('./sample.txt').then(content => { let keyword = content.substring(0, 5); return queryDB(keyword); }).then(res => { return getData(res.length); }).then(data => { console.log(data); }).catch(err => { console.warn(err); }); -
generator
- generator是es6中的一个新的语法。在function关键字后添加*即可将函数变为generator。
const generator = function* () { yield 11; yield 22; return 33; }- 执行generator将会返回一个遍历器对象,用于遍历generator内部的状态。
let g = generator(); g.next(); // { value: 11, done: false } g.next(); // { value: 22, done: false } g.next(); // { value: 33, done: true } g.next(); // { value: undefined, done: true -
async/await
- 任何一个函数都可以成为async函数
async function foo () {}; const foo = async function () {}; const foo = async () => {}; 在async函数中可以使用await语句。await后一般是一个Promise对象。 async function foo () { console.log('开始'); let res = await post(data); console.log(`post已完成,结果为:${res}`); };- 当上面的函数执行到await时,可以简单理解为,函数挂起,等待await后的Promise返回,再执行下面的语句
前后端分离
- 前端负责view和controller层
- 后端只负责model层,业务处理与数据持久化等
媒体查询
- @media
高阶函数
- 参数是一个函数
- 函数返回值是一个函数 满足其中一点 就被称为高阶函数
尾调用优化
什么是尾调用
- 指某个函数的最后一步是调用另一个函数
优化
- 尾调用之所以与其他调用不同,就在于它的特殊的调用位置。我们知道,函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用记录上方,还会形成一个B的调用记录。等到B运行结束,将结果返回到A,B的调用记录才会消失。如果函数B内部还调用函数C,那就还有一个C的调用记录栈,以此类推。所有的调用记录,就形成一个"调用栈"(call stack)。
- 尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用记录,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就可以了。
function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();
// 等同于
function f() {
return g(3);
}
f();
// 等同于
g(3);
- 上面代码中,如果函数g不是尾调用,函数f就需要保存内部变量m和n的值、g的调用位置等信息。但由于调用g之后,函数f就结束了,所以执行到最后一步,完全可以删除 f() 的调用记录,只保留 g(3) 的调用记录
- 这就叫做"尾调用优化"(Tail call optimization),即只保留内层函数的调用记录。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用记录只有一项,这将大大节省内存。这就是"尾调用优化"的意义。
尾递归
-函数调用自身,称为递归。如果尾调用自身,就称为尾递归。 -递归非常耗费内存,因为需要同时保存成千上百个调用记录,很容易发生"栈溢出"错误(stack overflow)。但对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误。
- 尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数
闭包
- 闭包就是能够读取其他函数内部变量的函数
闭包的用途
- 一个是前面提到的可以读取函数内部的变量,
- 另一个就是让这些变量的值始终保持在内存中。
使用闭包的注意点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
重绘与重排
重排
- 当render tree中的一部分或全部因为元素的规模尺寸、布局、隐藏等改变时,浏览器重新渲染部分DOM或全部DOM的过程。回流也被称为重排,其实从字面上来看,重排更容易让人形象易懂(即重新排版整个页面)。
- 回流必将引起重绘,而重绘不一定会引起回流。
- 重排也叫回流,实际上,reflow的字面意思也是回流,之所以有的叫做重排,也许是因为重排更好理解,更符合中国人的思维。标准文档之所以叫做回流(Reflow),是因为浏览器渲染是基于“流式布局”的模型,流实际就使我们常说的文档流,当dom或者css几何属性发生改变的时候,文档流会受到波动联动的去更改
- 引起重排的操作有
- 浏览器窗口大小发生改变。
- 元素尺寸或位置发生改变。
- 元素内容变化(文字数量或图片大小等等)。
- 元素字体大小变化。
- 添加或者删除可见的DOM元素。
- 激活CSS伪类(例如::hover)。
- 设置style属性
- 查询某些属性或调用某些方法。
- 页面首次渲染。
重绘
- 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
- 当页面元素样式改变不影响元素在文档流中的位置时(如background-color,border-color,visibility),浏览器只会将新样式赋予元素并进行重新绘制操作
如何减少回流,重绘?
CSS中避免回流、重绘
- 尽可能在DOM树的最末端改变class
- 避免设置多层内联样式
- 动画效果应用到position属性为absolute或fixed的元素上
- 避免使用table布局
- 使用css3硬件加速,可以让transform、opacity、filters等动画效果不会引起回流重绘.
JS操作避免回流、重绘
- 避免使用JS一个样式修改完接着改下一个样式,最好一次性更改CSS样式,或者将样式列表定义为class的名称
- 避免频繁操作DOM,使用文档片段创建一个子树,然后再拷贝到文档中
- 先隐藏元素,进行修改后再显示该元素,因为display:none上的DOM操作不会引发回流和重绘
- 避免循环读取offsetLeft等属性,在循环之前把它们存起来
- 对于复杂动画效果,使用绝对定位让其脱离文档流,否则会引起父元素及后续元素大量的回流
总结
回流在浏览器中属于一种用户主导的操作,所以知道如何去改进回流时间以及知道各种文档属性(DOM节点深度,css的渲染效率,各种各样的样式改变)对回流时间的影响对于前端开发来讲是很有帮助的。有时即便是回流一个单一的元素,也可能要求它的父元素以及任何跟随它的元素也产生回流。例如需要改变某个元素的背景,这就不涉及该元素的属性,所以只发生重绘。
Web Worker
Worker 接口是 Web Workers API 的一部分,指的是一种可由脚本创建的后台任务,任务执行中可以向其创建者收发信息。要创建一个 Worker ,只须调用 Worker(URL) 构造函数,函数参数 URL 为指定的脚本。
递归
- 一种计算过程,如果其中每一步都要用到前一步或前几步的结果,称为递归的。用递归过程定义的函数,称为递归函数,例如连加、连乘及阶乘等。凡是递归的函数,都是可计算的,即能行的
- 函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数
- 在程序中,递归就是函数直接或间接的调用自己
- 递归而言最重要的就是跳出结构,跳出了才会有结果(一般通过判断条件跳出)
- 所谓的递归就是化归思想