使用模块化能够给咱们带来如下好处css
解决命名冲突html
提供复用性前端
提升代码可维护性node
Proxy 来替换本来的 Object.defineProperty 来实现数据响应式。 Proxy 是 ES6 中新增的功能,它能够用来自定义对象中的操做。面试
let p = new Proxy(target, handler)
复制代码
target 表明须要添加代理的对象 ,handler 用来自定义对象中的操做,好比能够用来自定义 set 或者 get 函数。算法
接下来咱们经过 Proxy 来实现一个数据响应式后端
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver)
},
set(target, property, value, receiver) {
setBind(value, property)
return Reflect.set(target, property, value)
}
}
return new Proxy(obj, handler)
}
let obj = { a: 1 }
let p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`)
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`)
}
)
p.a = 2 // 监听到属性a改变
p.a // 'a' = 2
复制代码
在上述代码中,咱们经过自定义 set 和 get 函数的方式,在本来的逻辑中插入了咱们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。数组
固然这是简单版的响应式实现,若是须要实现一个 Vue 中的响应式,须要咱们在 get 中收集依赖,在 set 派发更新,之因此 Vue3.0 要使用 Proxy 替换本来的 API 缘由在于 Proxy 无需一层层递归为每一个属性添加代理,一次便可完成以上操做,性能上更好,而且本来的实现有一些数据更新不能监听到,可是 Proxy 能够完美监听到任何方式的数据改变,惟一缺陷可能就是浏览器的兼容性很差了。浏览器
const arr = [1, 2, 3]
const sum = arr.reduce((acc, current) => acc + current, 0)
console.log(sum)
复制代码
对于 reduce 来讲,它接受两个参数,分别是回调函数和初始值,接下来咱们来分解上述代码中 reduce 的过程缓存
首先初始值为 0,该值会在执行第一次回调函数时做为第一个参数传入 回调函数接受四个参数,分别为累计值、当前元素、当前索引、原数组
在一次执行回调函数时,当前值和初始值相加得出结果1,
该结果会在第二次执行回调函数时当作第一个参数传入。
因此在第二次执行回调函数时,相加的值就分别是 1 和2,以此类推,循环结束后获得结果 6
就经过 reduce 来实现 map和filter 函数
const numbers = [10, 20, 30, 40];
const go = numbers.reduce((finalList, num) => {
//实现map方法,映射的做用
num = num * 2;
if (num > 50) {
//实现filter方法过滤
finalList.push(num);
}
return finalList;
}, []);
alert(go); // [60, 80]
复制代码
其实这个函数做用和 setTimeout 基本一致,只是该函数是每隔一段时间执行一次回调函数。
一般来讲不建议使用 setInterval。第一,它和 setTimeout 同样,不能保证在预期的时间执行任务。第二,它存在执行累积的问题,请看如下伪代码
function demo() {
setInterval(function(){
console.log(2)
},1000)
sleep(2000)
}
demo()
复制代码
以上代码在浏览器环境中,若是定时器执行过程当中出现了耗时操做,多个回调函数会在耗时操做结束之后同时执行,这样可能就会带来性能上的问题。
若是你有循环定时器的需求,其实彻底能够经过 requestAnimationFrame 来实现
特色
【1】requestAnimationFrame会把每一帧中的全部DOM操做集中起来,在一次重绘或回流中就完成,而且重绘或回流的时间间隔牢牢跟随浏览器的刷新频率
【2】在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这固然就意味着更少的CPU、GPU和内存使用量
【3】requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,而且若是页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
function setInterval(callback, interval) {
let timer
const now = Date.now
let startTime = now()
let endTime = startTime
const loop = () => {
timer = window.requestAnimationFrame(loop)
endTime = now()
if (endTime - startTime >= interval) {
startTime = endTime = now()
callback(timer)
}
}
timer = window.requestAnimationFrame(loop)
return timer
}
let a = 0
setInterval(timer => {
console.log(1)
a++
if (a === 3) cancelAnimationFrame(timer)
}, 1000)
复制代码
首先 requestAnimationFrame 自带函数节流功能,基本能够保证在 16.6 毫秒内只执行一次(不掉帧的状况下),而且该函数的延时效果是精确的,没有其余定时器时间不许的问题,固然你也能够经过该函数来实现 setTimeout。
进程和线程都是一个时间段的描述,是CPU工做时间段的描述。放在应用上来讲就表明了一个程序。线程是进程中的更小单位,描述了执行一段指令所需的时间。
把这些概念拿到浏览器中来讲,当你打开一个 Tab 页时,其实就是建立了一个进程,一个进程中能够有多个线程,好比渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时,其实就是建立了一个线程,当请求结束后,该线程可能就会被销毁。
和call很类似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。
bind返回值是函数
var obj = {
name: 'Dot'
}
function printName() {
console.log(this.name)
}
var dot = printName.bind(obj)
console.log(dot) // function () { … }
dot() // Dot
复制代码
bind 方法不会当即执行,而是 返回一个改变了上下文 this 后的函数。 而原函数 printName 中的 this 并无被改变,依旧指向全局对象 window。
参数的使用
function fn(a, b, c) {
console.log(a, b, c);
}
var fn1 = fn.bind(null, 'Dot');
fn('A', 'B', 'C'); // A B C
fn1('A', 'B', 'C'); // Dot A B
fn1('B', 'C'); // Dot B C
fn.call(null, 'Dot'); // Dot undefined undefined
复制代码
call 是把第二个及之后的参数做为 fn 方法的实参传进去,而 fn1 方法的实参实则是在 bind 中参数的基础上再日后排。
有时候咱们也用bind方法实现函数珂里化,如下是一个简单的示例:
var add = function(x) {
return function(y) {
return x + y;
};
};
var increment = add(1);
var addTen = add(10);
increment(2);
// 3
addTen(2);
// 12
复制代码
在低版本浏览器没有 bind 方法,咱们也能够本身实现一个。
if (!Function.prototype.bind) {
Function.prototype.bind = function () {
var self = this, // 保存原函数
context = [].shift.call(arguments), // 保存须要绑定的this上下文
args = [].slice.call(arguments); // 剩余的参数转为数组
return function () { // 返回一个新函数
self.apply(context, [].concat.call(args, [].slice.call(arguments)));
}
}
}
复制代码
求数组中的最大和最小值
var arr = [1,2,3,89,46]
var max = Math.max.apply(null,arr)//89
var min = Math.min.apply(null,arr)//1
复制代码
将类数组转化为数组
var trueArr = Array.prototype.slice.call(arrayLike)
复制代码
数组追加
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var total = [].push.apply(arr1, arr2);//6
// arr1 [1, 2, 3, 4, 5, 6]
// arr2 [4,5,6]
复制代码
判断变量类型
function isArray(obj){
return Object.prototype.toString.call(obj) == '[object Array]';
}
isArray([]) // true
isArray('dot') // false
复制代码
利用call和apply作继承
function Person(name,age){
// 这里的this都指向实例
this.name = name
this.age = age
this.sayAge = function(){
console.log(this.age)
}
}
function Female(){
Person.apply(this,arguments)//将父元素全部方法在这里执行一遍就继承了
}
var dot = new Female('Dot',2)
复制代码
使用 log 代理 console.log
function log(){
console.log.apply(console, arguments);
}
// 固然也有更方便的 var log = console.log()
复制代码
总结
call、apply和bind函数存在的区别:
bind返回对应函数, 便于稍后调用; apply, call则是当即调用。 除此外, 在 ES6 的箭头函数下, call 和 apply 将失效, 对于箭头函数来讲:
箭头函数体内的 this 对象, 就是定义时所在的对象,而不是使用时所在的对象;因此不须要相似于var _this = this这种丑陋的写法
箭头函数不能够看成构造函数,也就是说不可使用 new 命令, 不然会抛出一个错误
箭头函数不可使用 arguments 对象,,该对象在函数体内不存在. 若是要用, 能够用 Rest 参数代替
不可使用 yield 命令, 所以箭头函数不能用做 Generator 函数
由于 JS 采用 IEEE 754 双精度版本(64位),0.1 在二进制中是无限循环的一些数字,其实不仅是 0.1,其实不少十进制小数用二进制表示都是无限循环的。 JS 采用的浮点数标准却会裁剪掉咱们的数字。 那么这些循环的数字被裁剪了,就会出现精度丢失的问题,也就形成了 0.1 再也不是 0.1 了,而是变成了 0.100000000000000002
解决的办法有不少,这里咱们选用原生提供的方式来最简单的解决问题
parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true
复制代码
cookie,localStorage,sessionStorage,indexDB
Service Worker 是运行在浏览器背后的独立线程,通常能够用来实现缓存功能。 使用 Service Worker的话,传输协议必须为 HTTPS。由于 Service Worker 中涉及到请求拦截,因此必须使用 HTTPS 协议来保障安全。
Service Worker 实现缓存功能通常分为三个步骤:首先须要先注册 Service Worker,而后监听到 install 事件之后就能够缓存须要的文件,那么在下次用户访问的时候就能够经过拦截请求的方式查询是否存在缓存,存在缓存的话就能够直接读取缓存文件,不然就去请求数据。
缓存能够说是性能优化中简单高效的一种优化方式了,它能够显著减小网络传输所带来的损耗。
对于一个数据请求来讲,能够分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存能够帮助咱们在第一和第三步骤中优化性能。好比说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减小了响应数据。
缓存位置
Service Worker
Memory Cache
Disk Cache
Push Cache
网络请求
缓存策略
一般浏览器缓存策略分为两种:强缓存和协商缓存,而且 缓存策略都是经过设置 HTTP Header 来实现的。
强缓存
强缓存能够经过设置两种 HTTP Header 实现:Expires 和 Cache-Control 。
强缓存表示在缓存期间不须要请求,state code 为 200。
Expires
Expires: Wed, 22 Oct 2018 08:41:00 GMT Expires 是 HTTP/1 的产物,表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT 后过时,须要再次请求。而且 Expires 受限于本地时间,若是修改了本地时间,可能会形成缓存失效。
Cache-control
Cache-control: max-age=30 Cache-Control 出现于 HTTP/1.1,优先级高于 Expires 。该属性值表示资源会在 30 秒后过时,须要再次请求。
协商缓存
若是缓存过时了,就须要发起请求验证资源是否有更新。协商缓存能够经过设置两种 HTTP Header 实现 :Last-Modified 和 ETag 。
当浏览器发起请求验证资源时,若是资源没有作改变,那么服务端就会返回 304 状态码,而且更新浏览器缓存有效期。
Last-Modified 和 If-Modified-Since
Last-Modified 表示本地文件最后修改日期,
If-Modified-Since 会将 Last-Modified 的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来,不然返回 304 状态码。
可是 Last-Modified 存在一些弊端:
若是本地打开缓存文件,即便没有对文件进行修改,但仍是会形成 Last-Modified 被修改,服务端不能命中缓存致使发送相同的资源 由于 Last-Modified 只能以秒计时,若是在不可感知的时间内修改完成文件,那么服务端会认为资源仍是命中了,不会返回正确的资源
由于以上这些弊端,因此在 HTTP / 1.1 出现了 ETag 。
ETag 和 If-None-Match
ETag 相似于文件指纹,If-None-Match 会将当前 ETag 发送给服务器,询问该资源 ETag 是否变更,有变更的话就将新的资源发送回来。而且 ETag 优先级比 Last-Modified 高。
以上就是缓存策略的全部内容了,看到这里,不知道你是否存在这样一个疑问。若是什么缓存策略都没设置,那么浏览器会怎么处理?
对于这种状况,浏览器会采用一个启发式的算法,一般会取响应头中的 Date 减去 Last-Modified 值的 10% 做为缓存时间。
实际场景应用缓存策略
频繁变更的资源
对于频繁变更的资源,首先须要使用 Cache-Control: no-cache
使浏览器每次都请求服务器,而后配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的作法虽然不能节省请求数量,可是能显著减小响应数据大小。
代码文件
这里特指除了 HTML 外的代码文件,由于 HTML 文件通常不缓存或者缓存时间很短。
通常来讲,如今都会使用工具来打包代码,那么咱们就能够对文件名进行哈希处理,只有当代码修改后才会生成新的文件名。基于此,咱们就能够给代码文件设置缓存有效期一年 Cache-Control: max-age=31536000,这样只有当 HTML 文件中引入的文件名发生了改变才会去下载最新的代码文件,不然就一直使用缓存。
浏览器从网络中接收到 HTML 文件而后一系列的转换过程。
为何操做 DOM 慢
由于 DOM 是属于渲染引擎中的东西,而 JS 又是 JS 引擎中的东西。当咱们经过 JS 操做 DOM 的时候,其实 这个操做涉及到了两个线程之间的通讯,那么势必会带来一些性能上的损耗。 操做 DOM 次数一多,也就等同于一直在进行线程之间的通讯,而且操做 DOM 可能还会带来重绘回流的状况,因此也就致使了性能上的问题。
大部分人应该能够想到经过 requestAnimationFrame (requestAnimationFrame会把每一帧中的全部DOM操做集中起来,在一次重绘或回流中就完成,而且重绘或回流的时间间隔牢牢跟随浏览器的刷新频率)的方式去循环的插入 DOM。
其实还有种方式去解决这个问题:虚拟滚动(virtualized scroller)。
这种技术的原理就是只渲染可视区域内的内容,非可见区域的那就彻底不渲染了,当用户在滚动的时候就实时去替换渲染的内容。
首先渲染的前提是生成渲染树,因此 HTML 和 CSS 确定会阻塞渲染。若是你想渲染的越快,你越应该下降一开始须要渲染的文件大小,而且扁平层级,优化选择器。
而后当浏览器在解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方从新开始。也就是说,若是你想首屏渲染的越快,就越不该该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的缘由。
固然在当下,并非说 script 标签必须放在底部,由于你能够给 script 标签添加 defer 或者 async 属性。
当 script 标签加上 defer 属性之后,表示该 JS 文件会并行下载,可是会放到 HTML 解析完成后顺序执行,因此对于这种状况你能够把 script 标签放在任意位置。
对于没有任何依赖的 JS 文件能够加上 async 属性,表示 JS 文件下载和解析不会阻塞渲染。
使用 transform 替代 top
<div class="test"></div>
<style>
.test {
position: absolute;
top: 10px;
width: 100px;
height: 100px;
background: red;
}
</style>
<script>
setTimeout(() => {
// 引发回流
document.querySelector('.test').style.top = '100px'
}, 1000)
</script>
复制代码
使用 visibility 替换 display: none ,由于前者只会引发重绘,后者会引起回流(改变了布局)
不要把节点的属性值放在一个循环里当成循环里的变量
for(let i = 0; i < 1000; i++) {
// 获取 offsetTop 会致使回流,由于须要去获取正确的值
console.log(document.querySelector('.test').style.offsetTop)
}
复制代码
不要使用 table 布局,可能很小的一个小改动会形成整个 table 的从新布局
动画实现的速度的选择,动画速度越快,回流次数越多,也能够选择使用 requestAnimationFrame
CSS 选择符从右往左匹配查找,因此要避免节点层级过多
将频繁重绘或者回流的节点设置为图层,图层可以阻止该节点的渲染行为影响别的节点。好比对于 video 标签来讲,浏览器会自动将该节点变为图层。
设置节点为图层的方式有不少,咱们能够经过如下几个经常使用属性能够生成新图层
will-change
video、iframe 标签
提示如何加速:
从文件大小考虑
从 script 标签使用上来考虑
从 CSS、HTML 的代码书写上来考虑
从须要下载的内容是否须要在首屏使用上来考虑
计算图片大小
对于一张 100 * 100 像素的图片来讲,图像上有 10000 个像素点,若是每一个像素的值是 RGBA 存储的话,那么也就是说每一个像素有 4 个通道,每一个通道 1 个字节(8 位 = 1个字节),因此该图片大小大概为 39KB(10000 * 1 * 4 / 1024)。
减小像素点
减小每一个像素点可以显示的颜色
一、修饰图片彻底能够用 CSS 去代替。
二、没有必要去加载原图浪费带宽。通常图片都用 CDN加载,能够计算出适配屏幕的宽度,而后去请求相应裁剪好的图片。
三、小图使用 base64 格式
四、将多个图标文件整合到一张图片中(雪碧图)
5.选择正确的图片格式:
对于可以显示 WebP 格式的浏览器尽可能使用 WebP 格式。由于 WebP 格式具备更好的图像数据压缩算法,能带来更小的图片体积, 并且拥有肉眼识别无差别的图像质量,缺点就是兼容性并很差 小图使用 PNG,其实对于大部分图标这类图片,彻底可使用 SVG 代替 照片使用 JPEG
DNS 解析也是须要时间的,能够经过预解析的方式来预先得到域名所对应的 IP。
<link rel="dns-prefetch" href="//yuchengkai.cn">
复制代码
有些资源不须要立刻用到,可是但愿尽早获取,这时候就可使用预加载。
预加载其实 是声明式的 fetch ,强制浏览器请求资源,而且不会阻塞 onload 事件,可使用如下代码开启预加载
<link rel="preload" href="http://example.com">
复制代码
预加载能够必定程度上下降首屏的加载时间,由于能够将一些不影响首屏但重要的文件延后加载,惟一缺点就是兼容性很差。
能够经过预渲染将下载的文件预先在后台渲染,可使用如下代码开启预渲染
<link rel="prerender" href="http://example.com">
复制代码
预渲染虽然能够提升页面的加载速度,可是要确保该页面大几率会被用户在以后打开,不然就是白白浪费资源去渲染。
懒加载的原理就是只加载可视区域,但也能够是即将进入可视区域)内须要加载的东西。对于图片来讲,先设置图片标签的 src 属性为一张占位图,将真实的图片资源放入一个自定义属性中,当进入可视区域时,就将自定义属性替换为 src 属性,这样图片就会去下载资源,实现了图片懒加载。
CDN 的原理是尽量的在各个地方分布机房缓存数据,这样即便咱们的根服务器远在国外,在国内的用户也能够经过国内的机房迅速加载资源。
所以,咱们能够将静态资源尽可能使用 CDN 加载,因为浏览器对于单个域名有并发请求上限,能够考虑使用多个 CDN 域名。而且对于 CDN 加载静态资源须要注意 CDN 域名要与主站不一样,不然每次请求都会带上主站的 Cookie,平白消耗流量。
优化 Loader
对于 Loader 来讲,影响打包效率首当其冲必属 Babel 了。由于 Babel 会将代码转为字符串生成 AST,而后对 AST 继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。
优化方法:
一、首先咱们能够优化 Loader 的文件搜索范围,只做用在 JS 代码上的,而后 node_modules 中使用的代码都是编译过的,因此咱们也彻底没有必要再去处理一遍
二、将 Babel 编译过的文件缓存起来(下次只须要编译更改过的代码文件便可,这样能够大幅度加快打包时间)
首先先申明一点,无论是 React 仍是 Vue,它们都不是 MVVM 框架,只是有借鉴 MVVM 的思路。文中拿 Vue 举例也是为了更好地理解 MVVM 的概念。
传统的 MVC 架构一般是使用控制器更新模型,视图从模型中获取数据去渲染。当用户有输入时,会经过控制器去更新模型,而且通知视图进行更新。
可是MVC 有一个巨大的缺陷就是控制器承担的责任太大了,随着项目越发复杂,控制器中的代码会愈来愈臃肿,致使出现不利于维护的状况。
在 MVVM 架构中,引入了 ViewModel 的概念。ViewModel 只关心数据和业务的处理,不关心 View 如何处理数据,在这种状况下,View 和 Model 均可以独立出来,任何一方改变了也不必定须要改变另外一方,而且能够将一些可复用的逻辑放在一个 ViewModel 中,让多个 View 复用这个 ViewModel。
除了以上三个部分,其实在 MVVM 中还引入了一个隐式的 Binder 层,实现了 View 和 ViewModel 的绑定。
对于 MVVM 来讲,其实最重要的并非经过双向绑定或者其余的方式将 View 与 ViewModel 绑定起来,而是经过 ViewModel 将视图中的状态和用户的行为分离出一个抽象,这才是 MVVM 的精髓。
前端路由实现起来其实很简单,本质就是监听 URL 的变化,而后匹配路由规则,显示相应的页面,而且无须刷新页面。目前前端使用的路由就只有两种实现方式
Hash 模式
History 模式
www.test.com/#/ 就是 Hash URL,当 # 后面的哈希值发生变化时,能够经过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面,而且不管哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com。
window.addEventListener('hashchange', () => {
// ... 具体逻辑
})
复制代码
Hash 模式相对来讲更简单,而且兼容性也更好。
History 模式是 HTML5 新推出的功能,主要使用 history.pushState 和 history.replaceState 改变 URL。
经过 History 模式改变 URL 一样不会引发页面的刷新,只会更新浏览器的历史记录。
// 新增历史记录
history.pushState(stateObject, title, URL)
// 替换当前历史记录
history.replaceState(stateObject, title, URL)
复制代码
当用户作出浏览器动做时,好比点击后退按钮时会触发 popState 事件
window.addEventListener('popstate', e => {
// e.state 就是 pushState(stateObject) 中的 stateObject
console.log(e.state)
})
复制代码
两种模式对比
Hash 模式只能够更改 # 后面的内容,History 模式能够经过 API 设置任意的同源 URL
History 模式能够经过 API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串
Hash 模式无需后端配置,而且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端须要配置 index.html 页面用于匹配不到静态资源的时候
computed 是计算属性,依赖其余属性计算值,来动态得到值而且 computed 的值有缓存,只有当计算值变化才会返回内容。
watch 监听到值的变化就会执行回调,在回调中能够进行一些复杂业务逻辑操做。
若是你须要在组件切换的时候,保存一些组件的状态防止屡次渲染,就可使用 keep-alive 组件包裹须要保存的组件。
对于 keep-alive 组件来讲,它拥有两个独有的生命周期钩子函数,分别为 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
若是通过下标方式修改数组数据或者给对象新增属性并不会触发组件的从新渲染,由于 Object.defineProperty 不能拦截到这些操做,更精确的来讲,对于数组而言,大部分操做都是拦截不到的
因此 Vue 内部经过重写函数的方式解决了这个问题。
将模板解析为 AST
优化 AST
将 AST 转换为 render 函数
nextTick 可让咱们在下次 DOM 更新循环结束以后执行延迟回调,用于得到更新后的 DOM。
mili();
function mili() {
console.log('mili');
}
mogu();
var mogu = function () {
console.log('mogu');
};
复制代码
打印结果是:mili Typeerror:mogu is not a function
缘由 :由于经过function a ()这种方式是函数声明和赋值都提早了。而经过var a = function 则只是函数声明提早了,而赋值要到执行到var a = function这步才会赋值。
最基本区别
1.执行时间
window.onload必须等到页面内包括图片的全部元素加载完毕后再去执行。
$(document).ready()时DOM结构回执完毕后就执行,没必要等到加载完毕。
2.编写个数不一样
window.onload不一样同时编写多个,若是有多个window.onload方法,只会执行一个
$(document).ready()能够同时编写多个,而且能够获得执行
document.onDOMContentLoaded在页面中触发[DOMContentLoaded]事件时触发。此时,文档被加载和解析,而且DOM被彻底构造,但连接的资源(例如图像,样式表和子帧)可能还没有被加载。