目录
window.innerHeight,window.innerWidth
window.outerHeight,window.outerWidth
window.alert(),window.prompt(),window.confirm()
JavaScript_浏览器环境概述(BOM)
JavaScript 是浏览器的内置脚本语言,一旦网页内嵌了 JavaScript 脚本,浏览器加载网页,就会去执行脚本,从而达到操作浏览器的目的,实现网页的各种动态效果
代码嵌入网页的方法
<script>
元素直接嵌入代码。
<script>
var x = 1 + 5;
console.log(x);
</script>
<script>
标签加载外部脚本
<script src="https://www.example.com/script.js"></script>
加载使用的协议
http协议
<script src="http://example.js"></script>
https协议
<script src="https://example.js"></script>
但是有时我们会希望,根据页面本身的协议来决定加载协议,这时可以采用下面的写法
<script src="//example.js"></script>
JavaScript_script元素工作原理
工作原理
浏览器加载 JavaScript 脚本,主要通过<script>
元素完成。正常的网页加载流程是这样的
- 浏览器一边下载 HTML 网页,一边开始解析。也就是说,不等到下载完,就开始解析
- 解析过程中,浏览器发现
<script>
元素,就暂停解析,把网页渲染的控制权转交给 JavaScript 引擎 - 如果
<script>
元素引用了外部脚本,就下载该脚本再执行,否则就直接执行代码 - JavaScript 引擎执行完毕,控制权交还渲染引擎,恢复往下解析 HTML 网页
加载外部脚本时,浏览器会暂停页面渲染,等待脚本下载并执行完成后,再继续渲染。原因是 JavaScript 代码可以修改 DOM,所以必须把控制权让给它,否则会导致复杂的线程竞赛的问题。
如果外部脚本加载时间很长(一直无法完成下载),那么浏览器就会一直等待脚本下载完成,造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”。
为了避免这种情况,较好的做法是将<script>
标签都放在页面底部,而不是头部。这样即使遇到脚本失去响应,网页主体的渲染也已经完成了,用户至少可以看到内容,而不是一张空白的页面。
defer 属性
为了解决脚本文件下载阻塞网页渲染的问题,一个方法是对<script>
元素加入defer
属性。它的作用是延迟脚本的执行,等到 DOM 加载生成后,再执行脚本
<script src="./js/index.js" defer></script>
defer
属性的运行流程如下
- 浏览器开始解析 HTML 网页
- 解析过程中,发现带有
defer
属性的<script>
元素 - 浏览器继续往下解析 HTML 网页,同时并行下载
<script>
元素加载的外部脚本 - 浏览器完成解析 HTML 网页,此时再回过头执行已经下载完成的脚本
有了defer
属性,浏览器下载脚本文件的时候,不会阻塞页面渲染
async 属性
解决“阻塞效应”的另一个方法是对<script>
元素加入async
属性
<script src="./js/index1.js" async></script>
<script src="./js/index.js" async></script>
async
属性的作用是,使用另一个进程下载脚本,下载时不会阻塞渲染
- 浏览器开始解析 HTML 网页
- 解析过程中,发现带有
async
属性的script
标签 - 浏览器继续往下解析 HTML 网页,同时并行下载
<script>
标签中的外部脚本 - 脚本下载完成,浏览器暂停解析 HTML 网页,开始执行下载的脚本
- 脚本执行完毕,浏览器恢复解析 HTML 网页
async
属性可以保证脚本下载的同时,浏览器继续渲染。需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本
一般来说,如果脚本之间没有依赖关系,就使用async
属性,如果脚本之间有依赖关系,就使用defer
属性。如果同时使用async
和defer
属性,后者不起作用,浏览器行为由async
属性决定
JavaScript_回流和重绘
渲染树转换为网页布局,称为“布局流”(flow);布局显示到页面的这个过程,称为“绘制”(paint)。它们都具有阻塞效应,并且会耗费很多时间和计算资源。
页面生成以后,脚本操作和样式表操作,都会触发“回流”(reflow)和“重绘”(repaint)。
什么是回流和重绘
回流:当节点树中的一部分因为元素的规模尺寸,布局,隐藏等改变而需要重新构建
重绘:当节点数中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的
温馨提示
回流和重绘并不一定一起发生,回流必然导致重绘,重绘不一定需要回流。比如改变元素颜色,只会导致重绘,而不会导致回流;改变元素的布局,则会导致重绘和回流
什么时候会造成回流和重绘
回流
- 页面初始加载
- 改变字体,元素尺寸(width,height,border,position),改变元素内容
- 添加或删除元素,如果元素本身被display:none会回流,visibility:hidden则会发生重绘
- 定位为fixed的元素,滚动条拖动时会回流
- 调整窗口大小
重绘
color | border-style | visibility | background |
---|---|---|---|
text-decoration | backgtound-image | background-repeat | background-position |
outline-color | outline | outline-style | border-radius |
outline-width | box-shadow | background-size |
优化技巧
- 读取 DOM 或者写入 DOM,尽量写在一起,不要混杂。不要读取一个 DOM 节点,然后立刻写入,接着再读取一个 DOM 节点
- 缓存 DOM 信息
- 不要一项一项地改变样式,而是使用 CSS class 一次性改变样式
- 使用documentFragment操作 DOM
- 动画使用absolute定位或fixed定位,这样可以减少对其他元素的影响
- 只在必要时才显示隐藏元素
- 使用虚拟DOM(virtual DOM)库
// 引发两次回流
box.style.top = '100px';
console.log(box.style.top);//=>'100px'
box.style.left = '100px';
// 引发一次回流
box.style.top = '100px';
box.style.left = '100px';
console.log(box.style.top);//=>'100px'
JavaScript_定时器之setTimeout
JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()
和setInterval()
这两个函数来完成。它们向任务队列添加定时任务
setTimeout
函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。
var timerId = setTimeout(func|code, delay);
setTimeout
函数接受两个参数,第一个参数func|code
是将要推迟执行的函数名或者一段代码,第二个参数delay
是推迟执行的毫秒数
setTimeout(function(){
console.log("定时器")
},1000)
温馨提示
还有一个需要注意的地方,如果回调函数是对象的方法,那么
setTimeout
使得方法内部的this
关键字指向全局环境,而不是定义时所在的那个对象
var name = "sxt";
var user = {
name: "itbaizhan",
getName: function () {
setTimeout(function(){
console.log(this.name);
},1000)
}
};
user.getName();
解决方案
var name = "sxt";
var user = {
name: "itbaizhan",
getName: function () {
var that = this;
setTimeout(function(){
console.log(that.name);
},1000)
}
};
user.getName();
定时器可以进行取消
var id = setTimeout(f, 1000);
clearTimeout(id);
<script>
// var timer = setTimeout(function(){
// console.log("hello");
// },5000)
// // this在不被改变的情况下,在定时器中指向window
// var username = "sxt";
// var user = {
// username:"itbaizhan",
// getName:function(){
// setTimeout(function(){
// // this指向window
// console.log(this.username);
// },1000)
// }
// }
// user.getName(); // sxt
// function logHello(){
// console.log("hello");
// }
// var timer = setTimeout(logHello,3000)
// var username = "sxt";
// var user = {
// username: "itbaizhan",
// getName: function () {
// var that = this;
// setTimeout(function () {
// // this指向window
// console.log(that.username);
// }, 1000)
// }
// }
// user.getName(); // itbaizhan
// var timer = setTimeout(function(){
// console.log("hello");
// },3000)
// clearTimeout(timer);
</script>
JavaScript_定时器之setInterval
setInterval
函数的用法与setTimeout
完全一致,区别仅仅在于setInterval
指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行
var timer = setInterval(function() {
console.log(2);
}, 1000)
通过setInterval方法实现网页动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
setInterval(function () {
console.log("hello");
}, 1000)
</script>
<script>
var box = document.getElementById('box');
var opacity = 1;
var timer = setInterval(function () {
opacity -= 0.05;
if (opacity <= 0) {
// 清除定时器
clearInterval(timer)
} else {
box.style.opacity = opacity
}
}, 30)
setInterval(function () {
console.log(new Date().toLocaleTimeString());
}, 1000)
</script>
</body>
</html>
定时器实操
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: gray;
}
.container {
width: 300px;
height: 200px;
background-color: #fff;
margin: 100px auto;
}
.container .up {
height: 50px;
width: 100%;
}
.container .up ul {
list-style-type: none;
text-align: center;
}
.container .up ul li {
float: left;
width: 60px;
height: 50px;
line-height: 50px;
}
.container .up ul li a {
text-decoration: none;
font-size: 20px;
}
.select {
border-bottom: 2px solid orange;
}
.container .down {
height: 100px;
width: 100%;
position: relative;
text-align: center;
}
.container .down div {
position: absolute;
line-height: 100px;
height: 100%;
width: 100%;
display: none;
}
.container .down div.div-select {
display: block;
}
</style>
</head>
<body>
<div class="container">
<div class="up">
<ul>
<li class="select"><a href="#">百度</a></li>
<li><a href="#">新浪</a></li>
<li><a href="#">淘宝</a></li>
<li><a href="#">京东</a></li>
<li><a href="#">网易</a></li>
</ul>
</div>
<div class="down">
<div class="div-select">百度内容</div>
<div>新浪内容</div>
<div>淘宝内容</div>
<div>京东内容</div>
<div>网易内容</div>
</div>
</div>
<script>
// 1. 获取到页面所有的元素
var lis = document.querySelector(".up").querySelectorAll("li");
var divs = document.querySelector(".down").querySelectorAll("div");
var timer = null;
var delay = 300;
// 闭包帮我们保存变量到内存中
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].onmouseenter = function () {
timer = setTimeout(function () {
for (var j = 0; j < lis.length; j++) {
lis[j].removeAttribute("class");
divs[j].removeAttribute("class")
}
lis[i].setAttribute("class", "select")
divs[i].setAttribute("class", "div-select")
}, delay)
}
}(i))
lis[i].onmouseleave = function () {
clearTimeout(timer)
}
}
/**
* 1. 获取元素
* 2. 闭包
* 3. 事件:鼠标滑动事件
* 4. 属性的操作:添加、删除
*/
</script>
</body>
</html>
JavaScript_防抖(debounce)
防抖严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死。
从滚动条监听的例子说起
先说一个常见的功能,很多网站会提供这么一个按钮:用于返回顶部。
这个按钮只会在滚动到距离顶部一定位置之后才出现,那么我们现在抽象出这个功能需求-- 监听浏览器滚动事件,返回当前滚条与顶部的距离这个需求很简单,直接写
function showTop () {
var scrollTop = document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
}
window.onscroll = showTop
在运行的时候会发现存在一个问题:这个函数的默认执行频率,太!高!了!。 高到什么程度呢?以chrome为例,我们可以点击选中一个页面的滚动条,然后点击一次键盘的【向下方向键】,会发现函数执行了8-9次!
然而实际上我们并不需要如此高频的反馈,毕竟浏览器的性能是有限的,不应该浪费在这里,所以接着讨论如何优化这种场景。
基于上述场景,首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后
- 如果在200ms内没有再次触发滚动事件,那么就执行函数
- 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
效果:如果短时间内大量触发同一事件,只会执行一次函数
实现:既然前面都提到了计时,那实现的关键就在于setTimeout这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现
function debounce(fn,delay){
let timer = null //借助闭包
return function() {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fn,delay) // 简化写法
}
}
// 然后是旧代码
function showTop () {
var scrollTop = document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
}
window.onscroll = debounce(showTop,300)
到这里,已经把防抖实现了
防抖定义
对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
height: 300px;
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<script>
// var timer = null;
// function debounce() {
// if (timer) {
// clearTimeout(timer)
// }
// timer = setTimeout(function () {
// var scollTop = document.documentElement.scrollTop;
// console.log(scollTop);
// },300)
// }
// window.onscroll = function () {
// debounce();
// }
/**
* 1. 定时器
* 2. 闭包
* 3. 事件机制
* 事件在注册的时候,只执行一次,后续的不间断的触发事件,是在执行事件里面的内容
* 不是在一直触发事件函数
*/
function debounce(fn, delay) {
console.log(111);
var timer = null; // 局部作用域,不污染全局命名空间
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(fn, delay)
}
}
function backTop() {
var scollTop = document.documentElement.scrollTop;
console.log(scollTop);
}
window.onscroll = debounce(backTop, 300)
window.onscroll = debounce//会打印111
// window.onscroll = function () {
// var result = debounce(backTop, 300);
// result();
// }
// window.onscroll = function () {
// var result = debounce(backTop, 300)();
//
// }
// // 只有第一次注册的时候执行一次,不间断执行的是里面的函数体
// div.onclick = function(){
// console.log("111");
// }
</script>
</body>
</html>
JavaScript_节流(throttle)
节流严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死
继续思考,使用上面的防抖方案来处理问题的结果是
如果在限定时间段内,不断触发滚动事件(比如某个用户闲着无聊,按住滚动不断的拖来拖去),只要不停止触发,理论上就永远不会输出当前距离顶部的距离
但是如果产品同学的期望处理方案是:即使用户不断拖动滚动条,也能在某个时间间隔之后给出反馈呢?
其实很简单:我们可以设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)
效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效
实现
这里借助setTimeout来做一个简单的实现,加上一个状态位valid来表示当前函数是否处于工作状态
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
height: 300px;
}
</style>
</head>
<body>
<div class="box">内哦给你</div>
<div class="box">内哦给你</div>
<div class="box">内哦给你</div>
<div class="box">内哦给你</div>
<div class="box">内哦给你</div>
<div class="box">内哦给你</div>
<script>
function throttle(fn, delay) {
var valid = true;
return function () {
if(!valid){
// 暂不接客
return false; // 后面的代码不在执行
}
valid = false;
setTimeout(function () {
fn();
valid = true;
},delay)
}
}
function backTop() {
var scollHeight = document.documentElement.scrollTop;
console.log(scollHeight);
}
window.onscroll = throttle(backTop,1000);
</script>
</body>
</html>
如果一直拖着滚动条进行滚动,那么会以1000ms的时间间隔,持续输出当前位置和顶部的距离
讲完了这两个技巧,下面介绍一下平时开发中常遇到的场景:
- 搜索框input事件,例如要支持输入实时搜索可以使用节流方案(间隔一段时间就必须查询相关内容),或者实现输入间隔大于某个值(如500ms),就当做用户输入完成,然后开始搜索,具体使用哪种方案要看业务需求
- 页面resize事件,常见于需要做页面适配的时候。需要根据最终呈现的页面情况进行dom渲染(这种情形一般是使用防抖,因为只需要判断最后一次的变化情况)
JavaScript_window对象_属性
浏览器里面,window
对象(注意,w
为小写)指当前的浏览器窗口。它也是当前页面的顶层对象,即最高一层的对象,所有其他对象都是它的下属
a = 1;
window.a // 1
window.screenX,window.screenY
window.screenX
和window.screenY
属性,返回浏览器窗口左上角相对于当前屏幕左上角的水平距离和垂直距离(单位像素)。这两个属性只读
console.log(window.screenX);
console.log(window.screenY);
window.innerHeight,window.innerWidth
window.innerHeight
和window.innerWidth
属性,返回网页在当前窗口中可见部分的高度和宽度,即“视口”(viewport)的大小(单位像素)。这两个属性只读
console.log(window.innerHeight);
console.log(window.innerWidth);
温馨提示
这两个属性值包括滚动条的高度和宽度
window.outerHeight,window.outerWidth
window.outerHeight
和window.outerWidth
属性返回浏览器窗口的高度和宽度,包括浏览器菜单和边框(单位像素)。这两个属性只读
console.log(window.outerHeight);
console.log(window.outerWidth);
window.scrollX,window.scrollY
window.scrollX
属性返回页面的水平滚动距离,window.scrollY
属性返回页面的垂直滚动距离,单位都为像素。这两个属性只读
console.log(window.scrollX);
console.log(window.scrollY);
window.pageXOffset,window.pageYOffset
window.pageXOffset
属性和window.pageYOffset
属性,是window.scrollX
和window.scrollY
别名
JavaScript_window对象_方法
window.alert(),window.prompt(),window.confirm()
window.alert()
、window.prompt()
、window.confirm()
都是浏览器与用户互动的全局方法。它们会弹出不同的对话框,要求用户做出回应。注意,这三个方法弹出的对话框,都是浏览器统一规定的式样,无法定制
window.alert()
window.alert()
方法弹出的对话框,只有一个“确定”按钮,往往用来通知用户某些信息
window.alert('Hello World');
window.prompt()
window.prompt()
方法弹出的对话框,提示文字的下方,还有一个输入框,要求用户输入信息,并有“确定”和“取消”两个按钮。它往往用来获取用户输入的数据
var result = prompt('您的年龄?', 25)
console.log(result);
window.confirm()
window.confirm()
方法弹出的对话框,除了提示信息之外,只有“确定”和“取消”两个按钮,往往用来征询用户是否同意
confirm
方法返回一个布尔值,如果用户点击“确定”,返回true
;如果用户点击“取消”,则返回false
var result = confirm('你最近好吗?');
var okay = confirm('Please confirm this message.');
if (okay) {
// 用户按下“确定”
} else {
// 用户按下“取消”
}
window.open()
window.open
方法用于新建另一个浏览器窗口,类似于浏览器菜单的新建窗口选项。它会返回新窗口的引用,如果无法新建窗口,则返回null
window.open("https://itbaizhan.com")
JavaScript_Navigator对象
window.navigator
属性指向一个包含浏览器和系统信息的 Navigator 对象。脚本通过这个属性了解用户的环境信息
Navigator.userAgent
navigator.userAgent
属性返回浏览器的 User Agent 字符串,表示用户设备信息,包含了浏览器的厂商、版本、操作系统等信息
userAgent
可以大致准确地识别手机浏览器,方法就是测试是否包含mobi
字符串
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf("mobi") > -1) {
// 手机浏览器
} else {
// 非手机浏览器
}
Navigator.plugins
Navigator.plugins
属性返回一个类似数组的对象,成员是 Plugin 实例对象,表示浏览器安装的插件,比如 Flash、ActiveX 等
var pluginsLength = navigator.plugins.length;
for (var i = 0; i < pluginsLength; i++) {
console.log(navigator.plugins[i].name);
console.log(navigator.plugins[i].filename);
console.log(navigator.plugins[i].description);
console.log(navigator.plugins[i].version);
}
Navigator.platform
Navigator.platform
属性返回用户的操作系统信息,比如MacIntel
、Win32
、Linux x86_64
等
navigator.platform
// 'Win32'
Navigator.language,Navigator.languages
Navigator.language
属性返回一个字符串,表示浏览器的首选语言。该属性只读
navigator.language
// 'zh-CN'
Navigator.languages
属性返回一个数组,表示用户可以接受的语言
navigator.languages
// ['zh-CN', 'zh']
JavaScript_Screen对象
Screen 对象表示当前窗口所在的屏幕,提供显示设备的信息。window.screen
属性指向这个对象
Screen.height
浏览器窗口所在的屏幕的高度(单位像素)。除非调整显示器的分辨率,否则这个值可以看作常量,不会发生变化。显示器的分辨率与浏览器设置无关,缩放网页并不会改变分辨率
Screen.width
浏览器窗口所在的屏幕的宽度(单位像素)
console.log(screen.width);
Screen.availHeight
浏览器窗口可用的屏幕高度(单位像素)。因为部分空间可能不可用,比如系统的任务栏或者 Mac 系统屏幕底部的 Dock 区,这个属性等于height
减去那些被系统组件的高度
screen.availHeight
Screen.availWidth
浏览器窗口可用的屏幕宽度(单位像素)
screen.availWidth
Screen.pixelDepth
整数,表示屏幕的色彩位数,比如24
表示屏幕提供24位色彩
screen.pixelDepth
// 24
Screen.orientation
返回一个对象,表示屏幕的方向。该对象的type
属性是一个字符串,表示屏幕的具体方向,landscape-primary
表示横放,landscape-secondary
表示颠倒的横放,portrait-primary
表示竖放,portrait-secondary
表示颠倒的竖放。
screen.orientation
// {angle: 270, type: 'portrait-primary', onchange: null}
JavaScript_History对象
window.history
属性指向 History 对象,它表示当前窗口的浏览历史
History 对象保存了当前窗口访问过的所有页面网址
window.history.length
History.back()
History.back()
:移动到上一个网址,等同于点击浏览器的后退键。对于第一个访问的网址,该方法无效果
history.back();
History.forward()
History.forward()
:移动到下一个网址,等同于点击浏览器的前进键。对于最后一个访问的网址,该方法无效果
history.forward();
History.go()
History.go()
:接受一个整数作为参数,以当前网址为基准,移动到参数指定的网址,比如go(1)
相当于forward()
,go(-1)
相当于back()
。如果参数超过实际存在的网址范围,该方法无效果;如果不指定参数,默认参数为0
,相当于刷新当前页面
history.go(-2);
温馨提示
移动到以前访问过的页面时,页面通常是从浏览器缓存之中加载,而不是重新要求服务器发送新的网页。
JavaScript_Cookie对象
Cookie 是服务器保存在浏览器的一小段文本信息,每个 Cookie 的大小一般不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息
Cookie 的目的就是区分用户,以及放置状态信息,它的使用场景主要如下
-
对话(session)管理:保存登录状态、购物车等需要记录的信息
-
个性化信息:保存用户的偏好,比如网页的字体大小、背景色等等
-
追踪用户:记录和分析用户行为
Cookie 不是一种理想的客户端存储机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端存储建议使用 Web storage API 。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面
每个 Cookie 都有以下几方面的元数据
- Cookie 的名字
- Cookie 的值(真正的数据写在这里面)
- 到期时间(超过这个时间会失效)
- 所属域名(默认为当前域名)
- 生效的路径(默认为当前网址)
不同浏览器对 Cookie 数量和大小的限制,是不一样的。一般来说,单个域名设置的 Cookie 不应超过30个,每个 Cookie 的大小不能超过 4KB。超过限制以后,Cookie 将被忽略,不会被设置
读取cookie
document.cookie
JavaScript_Cookie属性
Expires
Expires
属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式,可以使用Date.prototype.toUTCString()
进行格式转换
如果不设置该属性,或者设为null
,Cookie 只在当前会话(session)有效,浏览器窗口一旦关闭,当前 Session 结束,该 Cookie 就会被删除。另外,浏览器根据本地时间,决定 Cookie 是否过期,由于本地时间是不精确的,所以没有办法保证 Cookie 一定会在服务器指定的时间过期。
document.cookie = "name=iwen;Expires=Fri, 31 Dec 2021 16:00:00 GMT"
Max-Age
Max-Age
属性指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365
(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie
如果同时指定了Expires
和Max-Age
,那么Max-Age
的值将优先生效
document.cookie = "name=iwen;Max-Age=3600"
Domain
Domain
属性指定 Cookie 属于哪个域名,以后浏览器向服务器发送 HTTP 请求时,通过这个属性判断是否要附带某个 Cookie
Path
Path
属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path
属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,Path
属性是/
,那么请求/docs
路径也会包含该 Cookie。当然,前提是 Domain 属性必须符合条件
Secure
Secure
属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的Secure
属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开
HttpOnly
HttpOnly
属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是document.cookie
属性、XMLHttpRequest
对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie
JavaScript_封装Cookie(在服务器运行)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>学习Cookie</h3>
<script>
/*
1. 设置
2. 获取
3. 删除
*/
var cookie = {
// name value 过期时间
set:function(name,value,days){
var d = new Date();
d.setDate(d.getDate() + days); // 设置过期时间 name=itbaizhan;
document.cookie = name + "=" + value +";expires=" + d+";";
},
// 读取cookie,知道你要读取的是那个cookie
get:function(name){
var cookiesArr = document.cookie.split("; ")
for(var i = 0;i< cookiesArr.length;i++){
var newArr = cookiesArr[i].split("=");
if(name === newArr[0]){
return newArr[1]
}
}
},
// 干掉哪一个cookie
unset:function(name){
this.set(name,"",-1)
}
}
// cookie.set("sxt","iwen",30);
// console.log(cookie.get("name"));;
cookie.unset("name");
</script>
</body>
</html>
JavaScript_call_apply_bind函数
无论是apply、call还是bind其实都是改变this的指向,我们先来看一个例子
var obj = {
name:"小张",
getName:function(){
console.log(this.name)
}
}
obj.getName(); // 小张
下面我们来看一下通过他们来改变this指向
var obj = {
name:"小张",
getName:function(){
console.log(this.name)
}
}
var newObj = {
name:'小王'
}
obj.getName.call(newObj); // 小王
obj.getName.apply(newObj); // 小王
obj.getName.bind(newObj)(); // 小王
由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行
我们再来看一下apply和call的区别
var obj = {
name:"小张",
getName:function(city){
console.log(this.name,city)
}
}
var newObj = {
name:'小王'
}
obj.getName.call(newObj,'北京');
obj.getName.apply(newObj,['上海']);
obj.getName.bind(newObj,'深圳')();
第一个参数:this的指向,第二个参数为方法传递参数
常见应用场景
var arr = [10,20,30]
Math.max.apply(null,arr) // 30
var arr = [10,20,30]
Math.max.call(null,...arr)
null代表指向window对象,这里是因为Array本身是window对象的子元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
回顾this指向
1. 事件中的this,指向当前DOM元素
2. 对象中的this,指向当前调用者
3. 闭包中的this,指向window
4. 定时器中的this,指向window
5. call、apply和bind改变this指向
*/
// var user = {
// name:"itbaizhan",
// getName:function(){
// console.log(this.name);
// }
// }
// var username = "sxt"
// function getFn(){
// var username = "itbaizhan"
// return function(){
// console.log(this.username);
// }
// }
// var name = "sxt"
// var user = {
// name:"itbaizhan",
// getName:function(){
// setTimeout(function(){
// console.log(this.name);
// })
// }
// }
// user.getName()
</script>
<script>
var user = {
name: "itbaizhan",
getName: function (city) {
console.log(this.name,city);
}
}
var people = {
name: "sxt"
}
// user.getName(); // 指向user
// this指向,传递的额外参数
// user.getName.call(people); // sxt
// user.getName.apply(people); // sxt
// user.getName.bind(people)(); // sxt
// 由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行
// call和apply的区别
// call和apply接受的参数是不同的
// user.getName.call(people,"北京"); // sxt 北京
// user.getName.apply(people, ["北京"]); // sxt 北京
var arr = [10,20,30];
console.log(Math.max.apply(window,arr)); // null === window
console.log(Math.max.call(window,10,20,30));
console.log(Math.max(10,20,30));
</script>
</body>
</html>