文章目录
- 1. 三个等号和两个等号的区别
- 2.slice splice区别
- ⑴ slice
- ⑵ splice
- 3.setTimeout和setInterval区别,如何互相实现
- 4.childNodes和children的区别
- 5.var a=function(){}和function a(){}有什么区别
- 6.JS的数据类型
- ⑴ 概述
- ⑵ typeof操作符
- ⑶ 三大引用类型
- ①Object类型
- ②Array类型
- ③Function类型
- ⑶ 值类型和引用类型的区别
- 7.闭包
- ⑴ 什么是闭包
- ⑵ 立即执行函数
- ⑶ 闭包的作用
- 8.事件捕获和时间冒泡
- ⑴ 一 js时间传播过程
- ⑵ AddEventlistener
- ⑶ 阻止事件冒泡
- 9.arguments
- ⑴ 类数组
- ⑵ arguments
- ⑶ arguments操作
- ①arguments转数组
- ②修改 arguments 值
- ③将参数从一个函数传递到另一个函数
- ④扩展操作符
- ⑤默认参数
- ⑷ 注意事项
- ①JS没有重载
- ⑥不能将函数的 arguments 泄露或者传递出去
- 10.Ajax原理
- ⑴什么是Ajax
- ⑵Ajax的原理
- 11.fetch和Ajax的区别
- ⑴Ajax
- ⑵fetch
- ⑶两者区别
- 12.虚拟DOM
- ⑴概述
- ⑵主要分为下面几个步骤
- 13.宏观任务和微观任务
- 13.setTimeout
- 14.回调
- 15.Generators
- 16.Promise
- 17.Async/Await
- ⑴特点
- ⑵async/await 的优势在于处理 then 链
- 18.防抖
- 19.节流
- 20.Set、Map、WeakSet 和 WeakMap
- ⑴Set
- ⑵WeakSet
- ⑶Map
- ⑷WeakMap
- 21.parseInt(string, radix)
- 22.原型链
- ⑴__proto__和prototype
- ⑵普通对象和函数对象
- ⑶作用
- ⑷例子:
- 23.js继承
- 24.jsonp原理
- ⑴同源策略
- ⑵jsonp解决跨域
- ①什么是jsonp
- ②jsonp跨域的原理
- 25.call、apply和bind
- ⑴概述
- ⑵call、apply和bind
- ①call
- ②apply
- ③bind
- 26.webStorage
- ⑴概述
- ⑵相同点
- ⑶不同点
1. 三个等号和两个等号的区别
两个等号和三个等号是不同的,两个是判断值是否相等,三个是判断值及类型是否完全相等。
2.slice splice区别
⑴ slice
从已有数组中返回选定的元素,返回一个新数组(不会改变原数组),包含从start到end(不包含该元素)的数组元素。
slice(start,end)
- start 必须,规定从何处开始选取(第一位是0),如果为负数,规定从数组尾部算起的位置,-1是指最后一个元素。
- end 可选(如果该参数没有指定,那么切分的数组包含从start倒数组结束的所有元素,如果这个参数为负数,那么规定是从数组尾部开始算起的元素)。
⑵ splice
该方法向或者从数组中添加或者删除项目,返回被删除的项目。(该方法会改变原数组)
splice(index,howmany,item1,...itemX)
- index参数:必须,整数,规定添加或者删除的位置,使用负数,从数组尾部规定位置。
- howmany参数:必须,要删除的数量,如果为0,则不删除项目。
- tem1,…itemX参数:可选,向数组添加的新项目。
例:
var arr = [1,2,3,4,5];
console.log(arr.splice(2,1,"hello"));//[3] 返回的新数组
console.log(arr);//[1, 2, "hello", 4, 5] 改变了原数组
3.setTimeout和setInterval区别,如何互相实现
- setTimeout(表达式,延时时间)在执行时,是在载入后延迟指定时间后,去执行一次表达式,记住,次数是一次
- setInterval(表达式,交互时间)它从载入后,每隔指定的时间就执行一次表达式
4.childNodes和children的区别
childNodes会计算上包括空格的所有节点。而children是属于非标准型,不会计算空格。·
<script>
window.onload = function(){
var oUl = document.getElementsByTagName('ul')[0];
alert(oUl.childNodes.length);
//上面弹出语句的结果为:在IE 6-8 浏览器中是2,但在ie9以上或者其他浏览器,如谷歌,火狐等结果都是5,因为这些浏览器都把ul到li之间的空白节点也算上了,所以结果为5。
}
</script>
<body>
<ul>
<li></li>
<li></li>
</ul>
</body>
<script>
window.onload = function(){
var oUl = document.getElementsByTagName('ul')[0];
for(var i=0;i<oUl.children.length;i++){
//alert(oUl.children.length) 结果为2;
oUl.children[i].style.backgroundColor = 'yellow';
}
}
</script>
<body>
<ul>
<li></li>
<li></li>
</ul>
</body>
5.var a=function(){}和function a(){}有什么区别
b();
a();
function b(){
document.write("aa");
}
var a=function(){
document.write("123");
}
//function b(){} 为函数声明,程序运行前就已存在;var a = function(){} 为函数表达式,属于按顺序执行,所以a为undefined
6.JS的数据类型
⑴ 概述
js中有六种数据类型,包括五种基本数据类型(Number,String,Boolean,Undefined,Null),和一种复杂数据类型(Object)。
- Number类型 包含整数和浮点数(浮点数数值必须包含一个小数点,且小数点后面至少有一位数字)两种值。
- String类型
- Boolean类型
- Undefined类型 只有一个值,即undefined值。使用var声明了变量,但未给变量初始化值,那么这个变量的值就是undefined。
- Null类型
- Object类型
⑵ typeof操作符
typeof 123 //Number
typeof 'abc' //String
typeof true //Boolean
typeof undefined //Undefined
typeof null //Object
typeof { } //Object
typeof [ ] //Object
typeof console.log() //Function
⑶ 三大引用类型
①Object类型
②Array类型
③Function类型
⑶ 值类型和引用类型的区别
题目1: var a = 100;
var b = a;
a = 200;
console.log (b); //100
题目2: var a = {age : 20};
var b = a;
b.age = 21;
console.log (a.age); //21
7.闭包
⑴ 什么是闭包
「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
我们举个例子:
var func = (function foo(){
var local = 1
function bar(){
local++
return local
}
return bar
}())
func()
这里面local 变量和 bar 函数就组成了一个闭包(Closure)。
为什么要函数套函数呢?
是因为需要局部变量,所以才把 local 放在一个函数里,如果不把 local 放在一个函数里,local 就是一个全局变量了,达不到使用闭包的目的——隐藏变量(等会会讲)。函数套函数只是为了造出一个局部变量,跟闭包无关。
为什么要 return bar 呢?
所以 return bar 只是为了 bar 能被使用,也跟闭包无关。
另外一点:
这个foo()函数是立即执行的函数。
⑵ 立即执行函数
立即执行函数就是在声明函的时候立刻执行,防止变量被全局污染。
var car = (function () {
……
}());
⑶ 闭包的作用
闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。上面的例子我们就可以通过bar来操作local.
闭包不会造成内存泄漏,因为闭包的变量是我们所需要的变量。
8.事件捕获和时间冒泡
⑴ 一 js时间传播过程
- 事件捕获
当一个DOM元素上的事件被触发的时候(如:按钮点击事件),这个元素的所有父元素 中,如果也绑定有该相同事件,则也会被触发, 触发的顺序就是先从 : … 父元素 ==> 临近父元素 ==> 当前元素的事件 - 事件冒泡
当一个DOM元素上的事件被触发的时候(如:按钮点击事件),这个元素的所有父元素 中,如果也绑定有该相同事件,则也会被触发,一直到document
⑵ AddEventlistener
W3C为我们提供了addEventListener()函数用来为指定的dom元素动态绑定事件,语法
element.addEventListener(event, function, useCapture)
- event 指定事件名,不要用on做前缀
- function 执行函数
- useCapture 可选值,boolean类型,指定事件是否在捕获或冒泡阶段执行
- ture 事件句柄在捕获阶段执行
- false 默认,事件句柄在冒泡阶段执行
⑶ 阻止事件冒泡
stopPropagation可以阻止冒泡
9.arguments
⑴ 类数组
包含一组数据以及拥有一个 length 属性,但是没有任何 Array 的方法。类数组对象的 length 值无法自动改变。
⑵ arguments
arguments 是一个类数组对象。代表传给一个function的参数列表。
function printArgs() {
console.log(arguments);
}
printArgs("A", "a", 0, { foo: "Hello, arguments" }); //["A", "a", 0, Object]
再看看 arguments 表示的内容,其表示了函数执行时传入函数的所有参数。
arguments.length 表示传入的个数
⑶ arguments操作
①arguments转数组
- Array.prototype.slice.call(arguments);
- [].slice.call(arguments);
- Array.from()
②修改 arguments 值
在严格模式下,函数中的参数与 arguments 对象没有联系,修改一个值不会改变另一个值。而在非严格模式下,两个会互相影响。
严格模式下:
function foo(a) {
"use strict";
console.log(a, arguments[0]); //1 1
a = 10;
console.log(a, arguments[0]); //10 1
arguments[0] = 20;
console.log(a, arguments[0]); //10 20
}
foo(1);
非严格模式下:
function foo(a) {
console.log(a, arguments[0]); //1 1
a = 10;
console.log(a, arguments[0]); //10 10
arguments[0] = 20;
console.log(a, arguments[0]); //20 20
}
foo(1);
③将参数从一个函数传递到另一个函数
function foo() {
bar.apply(this, arguments);
}
function bar(a, b, c) {
// logic
}
④扩展操作符
扩展操作符可以将 arguments 展开成独立的参数。
function func() {
console.log(...arguments);
}
func(1, 2, 3); // 1 2 3
⑤默认参数
默认参数对 arguments 没有影响
function func(firstArg = 0, secondArg = 1) {
console.log(arguments[0], arguments[1]); // 99 undefined
console.log(firstArg, secondArg); // 99 1
}
func(99);
⑷ 注意事项
①JS没有重载
function add(num1, num2) {
console.log("Method one");
return num1 + num2;
}
function add(num1, num2, num3) {
console.log("Method two");
return num1 + num2 + num3;
}
add(1, 2); // Method two
add(1, 2, 3); // Method two
⑥不能将函数的 arguments 泄露或者传递出去
将函数的 arguments 对象泄露出去了,最终的结果就是 V8 引擎将会跳过优化,导致相当大的性能损失。
下面的例子就是把arguments泄漏了:
// Leaking arguments example2:
function getArgs() {
const args = [].slice.call(arguments);
return args;
}
我们可以这么做:
function getArgs() {
const args = new Array(arguments.length);
for(let i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
return args;
}
10.Ajax原理
⑴什么是Ajax
Asynchronous JavaScript and XML是一种异步请求数据,不需要重新刷新页面的web开发技术,它是由以下几个部分组成
使用CSS和XHTML来表示
使用DOM模型来交互和动态显示
使用XMLHttpRequest来和服务器进行异步通信
使用javascript来绑定和调用
⑵Ajax的原理
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。
XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。
11.fetch和Ajax的区别
⑴Ajax
Ajax的本质是使用XMLHttpRequest对象来请求数据,下面简单贴下原生js实现:
function ajax(url, fnSucc, fnFaild)
{
//1.创建Ajax对象 if(window.XMLHttpRequest){
var oAjax=new XMLHttpRequest();
}else{
var oAjax=new ActiveXObject("Microsoft.XMLHTTP");
}
//2.连接服务器(打开和服务器的连接)
oAjax.open('GET', url, true);
//3.发送
oAjax.send();
//4.接收
oAjax.onreadystatechange=function (){
if(oAjax.readyState==4){
if(oAjax.status==200){
//alert('成功了:'+oAjax.responseText);
fnSucc(oAjax.responseText);
}else{
//alert('失败了'); if(fnFaild){
fnFaild();
}
}
}
};
}
⑵fetch
fetch 是全局量 window 的一个方法,它的主要特点有:
- 第一个参数是URL
- 第二个是可选参数,可以控制不同配置的 init 对象
- 使用了 JavaScript Promises 来处理结果/回调
⑶两者区别
- 从 fetch()返回的 Promise 将不会拒绝HTTP错误状态, 即使响应是一个 HTTP 404 或 500。相反,它会正常解决 (其中ok状态设置为false), 并且仅在网络故障时或任何阻止请求完成时,它才会拒绝。
- 默认情况下, fetch在服务端不会发送或接收任何 cookies, 如果站点依赖于维护一个用户会话,则导致未经认证的请求(要发送 cookies,必须发送凭据头)。如果想要在同域中自动发送cookie,加上 credentials 的 same-origin 选项,对于cors请求,使用credentials的include选项允许将凭据发送到其他域。
12.虚拟DOM
⑴概述
即js模拟dom的树形结构,因为相对于dom对象,javascript对象更简单,处理速度更快。
⑵主要分为下面几个步骤
- 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
- 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了,无差异就不作为
13.宏观任务和微观任务
- 一个任务中,微观任务是先于宏观任务执行的。
- 宏观任务队列是一个事件循环,重复执行等待执行-执行任务两个任务
- 宏观任务传递的是Promise异步代码,Promise包含的所需要执行的异步代码是微观任务
13.setTimeout
console.log('script start') //1. 打印 script start
setTimeout(function(){
console.log('settimeout') // 4. 打印 settimeout
}) // 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end') //3. 打印 script start
// 输出顺序:script start->script end->settimeout
14.回调
回调函数是一个作为变量传递给另外一个函数的函数,它在主体函数执行完之后执行。
我们都知道JS的执行机制是从上到下一条线执行,如果我们需要等到一个操作结束时再执行另一个操作,这是回调函数就派上了用场。
function getAjaxData(callback){
$.post('xx',function(data){
if(data.status==1){ //当Ajax完成时,执行
callback(data);
}else{
console.error('数据不对啊:',data);
}
},'json')
}
15.Generators
generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。
generator和函数不同的是,generator由function定义(注意多出的号),并且,除了return语句,还可以用yield返回多次。
例子:
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 0;
while (n < max) {
yield a;
[a, b] = [b, a + b];
n ++;
}
return;
}
我们没执行一次next执行一次yield:
var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: undefined, done: true}
16.Promise
- Promise().then().then…catch() 多任务串行执行,有错误catch
- Promise.all([p1,p2,…]) 多任务并行执行,都要成功才进入then
- Promise.race([p1,p2,…]) 多任务赛跑,then()和catch(),谁先调用算谁的,其它任务中断
17.Async/Await
⑴特点
ES7出现,是 Generator 函数的语法糖
- async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成
- await 只能出现在 async 函数中
- async 函数返回的是一个 Promise 对象,所以在最外层不能用 await 获取其返回值的情况下,我们当然应该用原来的方式:then() 链来处理这个 Promise 对象
⑵async/await 的优势在于处理 then 链
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了。
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果,假设用Promise 方式来实现这三个步骤的处理
function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => step2(time2))
.then(time3 => step3(time3))
.then(result => {
console.log(`result is ${result}`);
console.timeEnd("doIt");
});
}
doIt();
用async/await 来实现:
async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time2);
const result = await step3(time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
}
doIt();
18.防抖
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟
function debounce(func,wait) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
}
触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。
19.节流
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数,比如射击游戏,一秒钟点N次鼠标他就开一枪。数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。
function throttle(func, wait) {
let previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
20.Set、Map、WeakSet 和 WeakMap
⑴Set
- 成员不能重复
- 只有健值,没有健名,有点类似数组。
- 可以遍历,方法有add, delete,has
⑵WeakSet
- 成员都是对象
- 成员都是弱引用,随时可以消失。 可以用来保存DOM节点,不容易造成内存泄漏
- 不能遍历,方法有add, delete,has
⑶Map
- 本质上是键值对的集合,类似集合
- 可以遍历,方法很多,可以干跟各种数据格式转换
⑷WeakMap
- 直接受对象作为健名(null除外),不接受其他类型的值作为健名
- 键名所指向的对象,不计入垃圾回收机制
- 不能遍历,方法同get,set,has,delete
21.parseInt(string, radix)
是用来解析字符串的,使字符串成为指定基数的整数
parseInt('1', 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
parseInt('2', 1) //基数为1(1进制)表示的数中,最大值小于2,所以无法解析,返回NaN
parseInt('3', 2) //基数为2(2进制)表示的数中,最大值小于3,所以无法解析,返回NaN
22.原型链
⑴__proto__和prototype
- prototype 指向一块内存,这个内存里面有共用属性
- __proto__ 指向同一块内存
- prototype 和 __proto__ 的不同点在于prototype 是构造函数的属性,而 __proto__ 是对象的属性
⑵普通对象和函数对象
通过New Function()创建的对象都是函数对象,其他的都是普通对象
普通对象:
//普通对象
var o1 = new F1();
var o2 = {};
var o3 = new Object();
console.log(typeof o1); //Object
console.log(typeof o2); //Object
console.log(typeof o3); //Object
函数对象:
//函数对象
function F1(){};
var F2 = function(){};
var F3 = function("n1","n2","return n1+n2");
console.log(typeof F1); //function
console.log(typeof F2); //function
console.log(typeof F3); //function
console.log(typeof Object); //function
console.log(typeof Array); //function
console.log(typeof String); //function
console.log(typeof Date); //function
console.log(typeof Function); //function
- 普通对象有属性__proto__,指向该对象的构造函数的原型对象。
- 函数对象除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。
⑶作用
一个对象并没有toString方法,就会往上找直到null,我感觉就是实现了继承
⑷例子:
function Animal(){
this.type = "animal";
}
Animal.prototype.getType = function(){
return this.type;
}
function Dog(){
this.name = "dog";
}
Dog.prototype = new Animal();
Dog.prototype.getName = function(){
return this.name;
}
var xiaohuang = new Dog();
xiaohuang.__proto__ === Dog.prototype
Dog.prototype.__proto__ === Animal.prototype
Animal.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
23.js继承
ES6之前js没有类的概念。
- ⑴原型链继承
- ⑵类式继承
- ⑶组合式继承:前两种方式的结合
- ⑷寄生组合式继承
- ⑸ES6通过extends继承(推荐)
24.jsonp原理
⑴同源策略
同源策略,它是由Netscape提出的一个著名的安全策略。
现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。
当一个浏览器的两个tab页中分别打开百度和谷歌的页面
当一个百度浏览器执行一个脚本的时候会检查这个脚本是属于哪个页面的
即检查是否同源,只有和百度同源的脚本才会被执行。
⑵jsonp解决跨域
①什么是jsonp
JSONP是JSON with Padding的略称。它是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)
②jsonp跨域的原理
由于同源策略,XMLHttpRequest()对象无法跨域,但是在页面上js脚本,css样式文件,图片是可以跨域的
1)<script type="text/javascript" src="某个cdn地址" ></script>
2)<link type="text/css" rel="stylesheet" href="某个cdn地址" />
3)<img src="某个cdn地址" alt=""/>
jsonp是将请求通过动态创建一个
25.call、apply和bind
⑴概述
call、apply、bind的作用是改变函数运行时this的指向。
⑵call、apply和bind
①call
fun.call(thisArg, arg1, arg2, ...)
- thisArg 指定的this值,并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
- arg1, arg2, … 指定的参数列表
②apply
fun.apply(thisArg, [argsArray])
- thisArg 指定的this值,并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
- 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。
③bind
fun.bind(thisArg[, arg1[, arg2[, ...]]])
- thisArg 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
- arg1, arg2, … 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
26.webStorage
⑴概述
webStorage包含两种存储方式sessionStorage 和 localStorage
因此sessionStorage 和 localStorage 的主要区别在于他们存储数据的生命周期,sessionStorage 存储的数据的生命周期是一个会话,只有在同一个会话中的页面才能访问,会话结束数据也随之销毁,而 localStorage 存储的数据的生命周期是永久,直到被主动删除,否则数据永远不会过期的。
⑵相同点
- 它们都可以用于存储用户数据
- 它们存储数据的格式都是字符串形式
- 它们存储的数据都有大小限制
⑶不同点
它们的生命周期不同。sessionStorage 的生命周期是一个会话,localStorage的生命周期是永久,cookie 的生命周期可以自定义,cookie 可以设置过期时间,数据在过期时间之前可以访问。
它们的存储大小限制不同。大部分现代浏览器 Storage 的存储限制大小为 5M,cookie 的存储大小限制 为 4K。
浏览器支持不同,API 调用方式不同。
相比 cookie ,Web Storage 的优点主要表现在存储空间更大,可存储的内容更大。cookie每次都随请求数据发送到服务器端,Web Storage不会和请求数据一同发送到服务器端,占用带宽更少。缺点主要表现在,现在所有浏览器都支持 cookie 操作,而只有现在浏览器才支持 Web Storage 操作,如果需要兼容老旧浏览器,就不能使用 Web Storage。