描述cookie和localStorage和sessionStorage的区别
从容量和API易用性角度。
cookie
-
cookie
本身用于浏览器和server
通讯,被“借用”到本地存储 - api为
document.cookie
,不够友好 - 容量最大为4kB
- http请求时需要一同发送到服务器,增加了请求的数据量
localStorage和sessionStorage
- 专为本地存储而设计的,最大为5MB
- API简单易用,
setItem
,getItem
- 不会随http请求发送出去
- localStorage数据永久存储,除非被动删除
- sessionStorage中存在当前会话,浏览器关闭后就会清空
从输入URL到渲染出页面的整个过程
- DNS解析:域名 -> IP地址
- 浏览器根据IP地址向服务器发送http请求
- 服务器处理http请求,并返回给浏览器
- 根据html代码生成DOM树
- 根据CSS代码生成CSSOM
- 将DOM树和CSSOM整合成RenderTree
- 根据RenderTree渲染页面,遇到
script
标签则暂停渲染,优先加载脚本并执行js代码,完成后再继续。暂停是因为JS进程和渲染进程是公用一个线程的,js可能会改变DOM结构 - 直到渲染完成
其中link要放在head中,因为放在后面可能会造成再次渲染问题,js放在最后,是因为JS进程和渲染进程是公用一个线程的,而且有可能js加载时间很长,然后就会造成页面卡顿现象,img标签不会暂停渲染,不会阻塞DOM渲染,不会造成重排现象。
window.onload和DOMContentLoaded的区别
window.addEventListener('load', function() {
// 页面全部资源加载完才会执行,包括图片、视频
console.log('load'); // 后输出
})
document.addEventListener('DOMContentLoaded', function() {
// DOM渲染完成即可执行,此时图片、视频可能还未加载完成
console.log('DOMContentLoaded'); // 先输出,所以操作 js 最好在这个方法里面
})
性能优化
原则:
- 多使用内存、缓存
- 减少CPU计算,减少网络加载耗时
- 以空间换取时间
从何入手
1. 让加载更快
1.1 减少资源的体积:压缩代码
1.2 减少访问次数:合并代码、SSR渲染、缓存
1.3 使用更快的网络
2. 让渲染更快
2.1 css放在head部分,js放在body最后
2.2 尽早执行js,用DOMContentLoaded触发
2.3 懒加载:图片懒加载、上滑加载更多
2.4 对DOM查询缓存
2.5 频繁DOM操作,合并到一起插入DOM结构
2.6 节流和防抖
防抖debounce
场景:监听一个输入框的文字,变化后触发change事件,直接用keyup事件,则会频繁触发change事件,使用防抖,只有在用户输入结束或暂停的时候,才会触发change事件
<input type="text" id="input1" />
<script>
function debounce(fn, delay = 300) {
let timer = null;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.call(this, arguments[0]);
timer = null;
}, delay);
};
}
const input = document.getElementById("input1");
input.addEventListener(
"keyup",
debounce(function(e) {
console.log(this.value, e);
})
);
</script>
节流throttle
场景:拖拽一个元素师,要随时拿到该元素的拖拽位置,直接用drag事件,则会频繁触发,很容易造成卡顿,此时使用节流的话,无论拖拽速度多快,都快每隔一段时间触发一次
<div
id="div1"
draggable="true"
style="width: 200px;height: 100px;background: rebeccapurple;"
/>
<script>
function throttle(fn, delay = 100) {
let timer = null;
return function() {
if (timer) { // timer不为空就什么都不做
return;
}
timer = setTimeout(() => {
fn.call(this, arguments[0]);
timer = null;
}, delay);
};
}
const div = document.getElementById("div1");
div.addEventListener(
"drag",
throttle(function(e) {
console.log(e.offsetX, e.offsetY);
})
);
</script>
如何捕获异常
try...catch
window.onerror = funtion(message, source, lineNumber, colNumber, error) {}
var 和let、const的区别
- var是ES5语法,let和const是ES6语法,var有变量提升
- var和let是变量,可修改,const是常量,不可改变
- let和const有块级作用域,var没有
手写一个深度比较
// 判断是否是对象
function isObject(target) {
return typeof target === "object" && target !== null;
}
function isEqual(source, target) {
// 只要其中有一个不是对象,那就直接比较
// 如 isEqual(1, { a: 2 })
// 如 isEqual(1, 1)
if (!isObject(source) || !isObject(target)) {
return source === target;
}
// 如过两个都引用的是一个对象,直接比较
// 如 isEqual(a, a)
if (source === target) {
return true;
}
// 两个都是对象或数组,而且不相等
// 1. 先取出 source 和 target 的keys, 比较个数
const sourceKeys = Object.keys(source);
const targetKeys = Object.keys(target);
if (sourceKeys.length !== targetKeys.length) {
return false;
}
// 以 source 为基准,和 target 依次递归比较
for (let key in sourceKeys) {
const res = isEqual(source[key], target[key]);
// 如果 res 中有一个为false,那就直接返回false
if (!res) {
return false;
}
}
// 如果res都为true
return true;
}
const a = { a: 1, b: 2, c: { x: 3, y: 4 } };
const b = { a: 1, b: 2, c: { x: 3, y: 4 } };
const c = { a: 1, b: 2, c: { x: 3, y: 4 }, d: 5 };
const d = { a: 1, b: 2, c: { x: 3, y: 44 } };
console.log(isEqual("aa", "aa")); // true
console.log(isEqual(11, 1)); // false
console.log(isEqual(a, b)); // true
console.log(isEqual(a, c)); // false
console.log(isEqual(a, d)); // false
获取URL参数
// 传统方法
function getURLQueryParams(_url) {
const params = {};
const url = _url || location.href; // _url 存在则用 _url, 不存在则用location.href
const search = url.split("?");
if (!search[1]) {
return params;
}
const searches = search[1].split("&");
searches.map(item => {
const items = item.split("=");
params[items[0]] = items[1];
});
return params;
}
console.log(getURLQueryParams("http:www.baidu.com?name=xxx&age=20")); // {name: "xxx", age: "20"}