JS 基础知识
关于 JS 的一些总结, 持续更新中……
基础知识
defer 与 async 的区别
按照惯例, 所有 script 元素都应 该放在页面的 head 元素中。这种做法的目的就是把所有外部文件(CSS 文件和 JavaScript 文件)的引用都放在相同的地方。可是, 在文档的 head 元素中包含所有 JavaScript 文件, 意味着必须等到全部 JavaScript 代码都被下载、解析和执行完成以后, 才能开始呈现页面的内容(浏览器在遇到 body 标签时才开始呈现内容)。
对于那些需要很多 JavaScript 代码的页面来说, 这无疑会导致浏览器在呈现页面时出现明显的延迟, 而延迟期间的浏览器窗口中将是一片空白。为了避免这个问题, 现在 Web 应用程序一般都全部 JavaScript 引用放在 body 元素中页面的内容后面。这样一来, 在解析包含的 JavaScript 代码之前, 页面的内容将完全呈现在浏览器中。而用户也会因为浏览器窗口显示空白页面的时间缩短而感到打开页面的速度加快了。 有了 defer 和 async 后, 这种局面得到了改善。
defer (延迟脚本) 延迟脚本:defer 属性只适用于外部脚本文件。 如果给 script 标签定义了 defer 属性, 这个属性的作用是表明脚本在执行时不会影响页面的构造。也就是说, 脚本会被延迟到整个页面都解析完毕后再运行。因此, 如果 script 元素中设置了 defer 属性, 相当于告诉浏览器立即下载, 但延迟执行。
async(异步脚本) 异步脚本:async 属性也只适用于外部脚本文件, 并告诉浏览器立即下载文件。 但与 defer 不同的是:标记为 async 的脚本并不保证按照指定它们的先后顺序执行。 所以总结起来, 两者之间最大的差异就是在于脚本下载完之后何时执行, 显然 defer 是最接近我们对于应用脚本加载和执行的要求的。
defer 是立即下载但延迟执行, 加载后续文档元素的过程将和脚本的加载并 行进行(异步), 但是脚本的执行要在所有元素解析完成之后, DOMContentLoaded 事件触发之前完成。async 是立即下载并执行, 加载和渲染后续文档元素的过程将和 js 脚本的加载与执行并行进行(异步)。
什么是闭包?闭包有什么作用?
一个函数和对其周围状态(lexical environment, 词法环境)的引用捆绑在一起(或者说函数被引用包围), 这样的组合就是闭包(closure)。也就是说, 闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中, 每当创建一个函数, 闭包就会在函数创建的同时被创建出来。
闭包的用处:
- 匿名自执行函数
- 结果缓存
- 封装
- 实现类和继承
事件委托以及冒泡原理
事件委托, 又被称之为事件代理。在 JavaScript 中, 添加到页面上的事件处理程序数量将直接关系到页面整体的运行性能。导致这一问题的原因是多方面的。 首先, 每个函数都是对象, 都会占用内存。内存中的对象越多, 性能就越差。其次, 必须事先指定所有事件处理程序而导致的 DOM 访问次数, 会延迟整个页面的交互就绪时间。 对事件处理程序过多问题的解决方案就是事件委托。 事件委托利用了事件冒泡, 只指定一个事件处理程序, 就可以管理某一类型的所有事件。例如, click 事件会一直冒泡到 document 层次。也就是说, 我们可以为整个页面指定一个 onclick 事件处理程序, 而不必给每个可单击的元素分别添加事件处理程序。 事件冒泡(event bubbling), 是指事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收, 然后逐级向上传播到较为不具体的节点(文档)。
作用域, 作用域链
- 作用域: 规定了如何查找变量, 也就是确定当前执行代码对变量的访问权限。换句话说, 作用域决定了代码区块中变量和其他资源的可见性。(全局作用域、函数作用域、块级作用域)
- 作用域链: 从当前作用域开始一层层往上找某个变量, 如果找到全局作用域还没找到, 就放弃寻找 。这种层级关系就是作用域链。(由多个执行上下文的变量对象构成的链表就叫做作用域链, 学习下面的内容之后再考虑这句话)
bind, call, apply
这三个方法, 都是为了操作 this, 改变 this 指向, 那么问题来了, 剪头函数中可以使用他们吗?
可以使用, 但是并没有作用, 箭头函数中的 this 和函数的 this 有本质区别, 箭头函数中的 this 是函数外最近的那个 this, 因此, apply、call 方法其实并不会起作用。
bind
fn.bind(obj, param1, param2, ...)
bind 方法, 是创建一个新的函数, 调用他, 第一个参数用来提供设置 this 的值, 如果不提供该值则 this 指向 window, 返回的也是一个函数, 只有执行该函数, 才会使得 this 指向发生改变, 这点是个 call、 apply的区别。例子:
function addFun(a, b) {
return a + b;
}
function subFun(a, b) {
return a - b;
}
addFun.bind(subFun, 3, 7); // 这时, 并不会返回 10
addFun.bind(subFun, 3, 7)(); // 调用后, 返回 10
call
fn.call(obj, param1, param2, ...)
call 方法
apply
fn.apply(obj, [param1,param2,...])
调用 call 和 apply 的对象, 必须是一个函数 Function, 它们的区别, 主要体现在参数的写法上, 前者从第二个参数开始可以传递 n 个参数, 后者只能传递两个参数, 不过第二个参数为一个参数的数组。