LocalStorage

localStorage的优势

  1. localStorage拓展了cookie的4K限制

  2. localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数据库,相比于cookie可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的

localStorage的局限

  1. 浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性
  2. 目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换
  3. localStorage在浏览器的隐私模式下面是不可读取的
  4. ocalStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
  5. localStorage不能被爬虫抓取到
  6. localStorage与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,sessionStorage中的键值对会被清空这里我们以localStorage来分析。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(!window.localStorage){
alert("浏览器支持localstorage");
return false;
}else{
var storage=window.localStorage;
//写入a字段
storage["a"]=1;
//写入b字段
storage.b=2;
//写入c字段
storage.setItem("c",3);
console.log(typeof storage["a"]);
console.log(typeof storage["b"]);
console.log(typeof storage["c"]);
}

运行后的结果如下:img

这里要特别说明一下localStorage的使用也是遵循同源策略的,所以不同的网站直接是不能共用相同的localStorage

最后在控制台上面打印出来的结果是:img

不知道各位读者有没有注意到,刚刚存储进去的是int类型,但是打印出来却是string类型,这个与localStorage本身的特点有关,localStorage只支持string类型的存储。

localStorage的读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if(!window.localStorage){
alert("浏览器支持localstorage");
}else{
var storage=window.localStorage;
//写入a字段
storage["a"]=1;
//写入b字段
storage.b=2;
//写入c字段
storage.setItem("c",3);
console.log(typeof storage["a"]);
console.log(typeof storage["b"]);
console.log(typeof storage["c"]);
//第一种方法读取
var a=storage.a;
console.log(a);
//第二种方法读取
var b=storage["b"];
console.log(b);
//第三种方法读取
var c=storage.getItem("c");
console.log(c);
}

localStorage就是相当于一个前端的数据库的东西,数据库主要是增删查改这四个步骤,这里的读取和写入就相当于增、查的这两个步骤

下面我们就来说一说localStorage的删、改这两个步骤

改这个步骤比较好理解,思路跟重新更改全局变量的值一样,这里我们就以一个为例来简单的说明一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if(!window.localStorage){
alert("浏览器支持localstorage");
}else{
var storage=window.localStorage;
//写入a字段
storage["a"]=1;
//写入b字段
storage.b=1;
//写入c字段
storage.setItem("c",3);
console.log(storage.a);
// console.log(typeof storage["a"]);
// console.log(typeof storage["b"]);
// console.log(typeof storage["c"]);
/*分割线*/
storage.a=4;
console.log(storage.a);
}

删除localStorage

  1. 将localStorage的所有内容清除

    1
    2
    3
    4
    5
    6
    var storage=window.localStorage;
    storage.a=1;
    storage.setItem("c",3);
    console.log(storage);
    storage.clear();
    console.log(storage);
  2. 将localStorage中的某个键值对删除

    1
    2
    3
    4
    5
    6
    var storage=window.localStorage;
    storage.a=1;
    storage.setItem("c",3);
    console.log(storage);
    storage.removeItem("a");
    console.log(storage.a);

    控制台查看结果

img

localStorage的键获取

1
2
3
4
5
6
7
var storage=window.localStorage;
storage.a=1;
storage.setItem("c",3);
for(var i=0;i<storage.length;i++){
var key=storage.key(i);
console.log(key);
}

参考Window localStorage 属性 | 菜鸟教程

cookie

2. 什么是cookie

HTTP协议本身是无状态的。什么是无状态呢,即服务器无法判断用户身份。Cookie实际上是一小段的文本信息(key-value格式)。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。

打个比方,我们去银行办理储蓄业务,第一次给你办了张银行卡,里面存放了身份证、密码、手机等个人信息。当你下次再来这个银行时,银行机器能识别你的卡,从而能够直接办理业务。

3. cookie机制

当用户第一次访问并登陆一个网站的时候,cookie的设置以及发送会经历以下4个步骤:

客户端发送一个请求到服务器 –》 服务器发送一个HttpResponse响应到客户端,其中包含Set-Cookie的头部 –》 客户端保存cookie,之后向服务器发送请求时,HttpRequest请求中会包含一个Cookie的头部 –》服务器返回响应数据

acac

image

为了探究这个过程,写了代码进行测试,如下:

我在doGet方法中,new了一个Cookie对象并将其加入到了HttpResponse对象中

1
2
3
4
5
6
7
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

Cookie cookie = new Cookie("mcrwayfun",System.currentTimeMillis()+"");
// 设置生命周期为MAX_VALUE
cookie.setMaxAge(Integer.MAX_VALUE);
resp.addCookie(cookie);
}

浏览器输入地址进行访问,结果如图所示:

13949989-b77c840aa9b23d28

可见Response Headers中包含Set-Cookie头部,而Request Headers中包含了Cookie头部。name和value正是上述设置的。

4. cookie属性项

属性项 属性项介绍
NAME=VALUE 键值对,可以设置要保存的 Key/Value,注意这里的 NAME 不能和其他属性项的名字一样
Expires 过期时间,在设置的某个时间点后该 Cookie 就会失效
Domain 生成该 Cookie 的域名,如 domain=”www.baidu.com
Path 该 Cookie 是在当前的哪个路径下生成的,如 path=/wp-admin/
Secure 如果设置了这个属性,那么只会在 SSH 连接时才会回传该 Cookie

Expires

该属性用来设置Cookie的有效期。Cookie中的maxAge用来表示该属性,单位为秒。Cookie中通过getMaxAge()和setMaxAge(int maxAge)来读写该属性。maxAge有3种值,分别为正数,负数和0。

如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中(每个浏览器存储的位置不一致)。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。

1
2
3
4
Cookie cookie = new Cookie("mcrwayfun",System.currentTimeMillis()+"");
// 设置生命周期为MAX_VALUE,永久有效
cookie.setMaxAge(Integer.MAX_VALUE);
resp.addCookie(cookie);

当maxAge属性为负数,则表示该Cookie只是一个临时Cookie,不会被持久化,仅在本浏览器窗口或者本窗口打开的子窗口中有效,关闭浏览器后该Cookie立即失效。

1
2
3
4
Cookie cookie = new Cookie("mcrwayfun",System.currentTimeMillis()+"");
// MaxAge为负数,是一个临时Cookie,不会持久化
cookie.setMaxAge(-1);
resp.addCookie(cookie);

可以看到,当MaxAge为-1时,时间已经过期

13949989-814a3d1a0186142e

当maxAge为0时,表示立即删除Cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Cookie[] cookies = req.getCookies();
Cookie cookie = null;

// get Cookie
for (Cookie ck : cookies) {

if ("mcrwayfun".equals(ck.getName())) {
cookie = ck;
break;
}
}

if (null != cookie) {
// 删除一个cookie
cookie.setMaxAge(0);
resp.addCookie(cookie);
}

那么maxAge设置为负值和0到底有什么区别呢?

maxAge设置为0表示立即删除该Cookie,如果在debug的模式下,执行上述方法,可以看见cookie立即被删除了。

13949989-c9c87d4c36f89010

maxAge设置为负数,能看到Expires属性改变了,但Cookie仍然会存在一段时间直到关闭浏览器或者重新打开浏览器。

13949989-fec1803cf239e607

修改或者删除Cookie

HttpServletResponse提供的Cookie操作只有一个addCookie(Cookie cookie),所以想要修改Cookie只能使用一个同名的Cookie来覆盖原先的Cookie。如果要删除某个Cookie,则只需要新建一个同名的Cookie,并将maxAge设置为0,并覆盖原来的Cookie即可。

新建的Cookie,除了value、maxAge之外的属性,比如name、path、domain都必须与原来的一致才能达到修改或者删除的效果。否则,浏览器将视为两个不同的Cookie不予覆盖。

值得注意的是,从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name和value属性,maxAge属性只被浏览器用来判断Cookie是否过期,而不能用服务端来判断。

13949989-50e022d8bd3d4256

我们无法在服务端通过cookie.getMaxAge()来判断该cookie是否过期,maxAge只是一个只读属性,值永远为-1。当cookie过期时,浏览器在与后台交互时会自动筛选过期cookie,过期了的cookie就不会被携带了。

Cookie的域名

Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的Cookie。

正常情况下,同一个一级域名下的两个二级域名也不能交互使用Cookie,比如test1.mcrwayfun.com和test2.mcrwayfun.com,因为二者的域名不完全相同。如果想要mcrwayfun.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为**.mcrwayfun.com**,这样使用test1.mcrwayfun.com和test2.mcrwayfun.com就能访问同一个cookie

一级域名又称为顶级域名,一般由字符串+后缀组成。熟悉的一级域名有baidu.com,qq.com。com,cn,net等均是常见的后缀。
二级域名是在一级域名下衍生的,比如有个一级域名为mcrfun.com,则blog.mcrfun.comwww.mcrfun.com均是其衍生出来的二级域名。

Cookie的路径

path属性决定允许访问Cookie的路径。比如,设置为”/“表示允许所有路径都可以使用Cookie

sessionStroage

localStorage 和 sessionStorage 属性允许在浏览器中存储 key/value 对的数据。

sessionStorage 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。

提示: 如果你想在浏览器窗口关闭后还保留数据,可以使用 localStorage 属性, 该数据对象没有过期时间,今天、下周、明年都能用,除非你手动去删除。

image-20211126231526770

this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<body>
<!-- 解析器在调用函数每次都会向函数内部传递一个隐含的参数
这个函数的参数就是this,this指向的是一个对象,
这个对象成为函数执行的上下文对象
根据函数调用方式不同,this会指向不同的对象
1.this以函数的形式调用时,this永远都是window
2.this以方法的形式调用时,this就是调用发放的那个对象
-->
<script>
function fun(){
console.log(this.name)
}
var obj= {
name:"孙悟空",
sayName: fun
};
var obj2= {
name:"猪八戒",
sayName: fun
};
// console.log(obj.sayName== fun)
fun();
obj.sayName();
obj2.sayName();
</script>
</body>

使用工厂方法创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    <script>
function xiyouji(name,gender,age){
var per= new Object()
per.name=name
per.gender=gender
per.age= age
per.sayname= function e(){
alert(this.name)
}
return per;
}
var per1 = xiyouji("孙悟空","男","18");
var per2= xiyouji("猪八戒","男","18");
console.log(per1)
console.log(per2)
per2.sayname();
// function createdog(name,age){
// var dog =new Object()
// dog.name = name
// dog.age= age
// dog.sayhello=function e(){
// alert("汪汪汪")
// }
// return dog;

// }
// var dog1=createdog("gg","6")
// console.log(dog1)
// dog1.sayhello()
// console.log(per1)
</script>

构造函数

为了区分不同的类(人,狗),可以通过构造函数的方式来实现

构造函数执行流程:

  1. 立即创建一个新的对象

  2. 将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象

  3. 逐行执行函数中的代码

  4. 将新建的对象作为返回值返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    <script>
function Person(name,gender,age){
this.name=name
this.gender=gender
this.age= age
this.sayname=function e(){
alert('你好')
}

}
var per =new Person("ch","男","21")
console.log(per)
per.sayname()
</script>

所有的对象都是Object()的后代

原型对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<script type="text/javascript">

/*
* 创建一个构造函数
*/
function MyClass(){

}

//向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";

var mc = new MyClass();
mc.age = 18;

//console.log(mc.name);

//使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
//console.log("name" in mc);

//可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性
//使用该方法只有当对象自身中含有属性时,才会返回true
//console.log(mc.hasOwnProperty("age"));

//console.log(mc.hasOwnProperty("hasOwnProperty"));

/*
* 原型对象也是对象,所以它也有原型,
* 当我们使用一个对象的属性或方法时,会现在自身中寻找,
* 自身中如果有,则直接使用,
* 如果没有则去原型对象中寻找,如果原型对象中有,则使用,
* 如果没有则去原型的原型中寻找,直到找到Object对象的原型,
* Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined
*/

//console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));

//console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));

//console.log(mc.__proto__.__proto__.__proto__);

//console.log(mc.hello);

//console.log(mc.__proto__.__proto__.__proto__)

总而言之,MyClass()可以当爸爸,通过MyClass()创建的对象是儿子,MyClass()则是爷爷,爷爷或者爸爸的基因从儿子身上可以显现,但不是本身创造的基因,如果要追根溯源,可以从爷爷或者爸爸身上找到这个基因,这就是构成了原型链。im