http是超文本传输协议,是目前互联网上使用最多的协议。是在应用层进行传输的。
https是安全超文本传输协议,是基于http开发的,并使用了ssl(安全套接字层)进行加密的网络协议,可以说是更安全版的http。是在传输层进行传输。
区别:
建立安全通道,确保数据的安全与完整,以及验证对方的身份,缺点就是贵和慢
content-type决定如何展示返回的消息内容
text/html : HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif图片格式
200:请求成功,一般用于get/post请求
201:已创建,成功请求并创建了新的资源
202:已接受,但是未完成处理
304:如果客户端发送了一个带条件的GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个304状态码。(也就是执行了get请求 但是文件并没有改变)
400:客户端请求的语法错误,服务器无法理解
403:请求资源的访问被服务器拒绝,没有权限访问
404:找不到页面
500:服务器内部错误
get、post、head(前三个是http1.0)、options、put、delete、trace和connect(后面是http1.1新增的)
(为什么不是两次?无法确认客户端的接收能力)
(为什么不是四次?为了解决问题,三次就足够了,再多用处就不大了)
websocket只需要完成一次握手,就可以在客户端和服务器端之间建立持久的连接,并且服务器端和客户端可以相互发送多个数据
1996年,发布了HTTP的1.0版本,这一版才加入了大量的内容,使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件等,报文格式也规定下来,引入了post和head等命令,是第一个比较成熟的版本。然而,1.0版本的主要缺点是每个HTTP连接只能发送一个请求(短连接),发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。这就会带来很多不必要的开销。
1997年,发布了HTTP的1.1版本,这一版将HTTP更加完善,直到现在还是主要的使用版本。1.1 版的最大变化,就是引入了持久连接(长连接),在响应完第一个请求之后,TCP连接默认不关闭,可以被多个请求复用,这样就客服了1.0版本的缺陷。除此之外,还引入了管道机制:一次可以发送多个请求,增加了put、delete等命令。
2015年,HTTP/2 发布(不叫 HTTP/2.0,不再发布子版本,下一个是3),HTTP2的变化在于:(1)二进制协议,头信息和数据体都是二进制,统称为帧(头信息帧和数据帧)。(2)多工,双向实时通信,复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。(3)数据流。据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。(4)头信息压缩机制。**(5)允许服务器推送。**允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送。
保存用户的登录状态,用户在下次进入页面就不需要再登录了,可以提示用户多久不需要登录;跟踪用户行为,例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。
document.cookie创建一个cookie字符串,里面的设置属性就行,它是一个字符串,可以用字符串的一些方法获取属性值。
document.cookie="name=Nicholos;expries=time"
等等
相同点:都存储在客户端
不同点:
浏览器缓存分为强缓存和协商缓存
强缓存就是首先判断头部信息,没过期就直接从缓存中读取数据(cache-control)
强缓存是利用http头中的Expires 和 Cache-Control两个字段来控制,
Expires 它的值是一个绝对的时间,这个时间代表资源的失效时间,就是说在这个时间之前缓存始终有效,始终会读取缓存中的数据。但是,这里会有一个明显的缺点:因为它是一个绝对时间,当服务器时间与客户端时间有偏差时,就可能会导致缓存失效,比如用户随意修改了本地时间…
**Cache-Control:**表示资源的最大有效时间的“秒数”,是一个相对值,不会因为用户修改本地时间而导致失效。
协商缓存就是需要服务器去判断是否数据更新过,如果没有则继续采用缓存中的数据,返回304,如果有则采用更新的数据,返回200(etag:每个文件唯一,Last-Modified)
Last-Modified/If-Modified-Since 和 ETag/If-None-Match
Last-Modified/If-Modified-Since:
1) 浏览器第一次发送请求,让服务端在response header中返回请求的资源上次更新时间(Last-Modified的值),浏览器会存下这个时间;
2)当浏览器下次请求时,request header中带上If-Modified-Since(即保存的Last-Modified的值)。根据浏览器发送的修改时间和服务端的修改时间进行比对,一致的话代码资源没有改变,服务端返回正文为空的响应,让浏览器在缓存中读取资源,从而减少请求消耗。
ETag/If-None-Match:
1) 浏览器第一次发送一个请求得到ETag的值,然后在下一次请求request header中带上If-none-match(即保存的ETag的值);
2)通过发送的ETag的值和服务器重新生成的ETag的值进行比对,如果一致代表资源没有改变,服务器返回的正文为空的响应,让浏览器从缓存中读取资源,从而减少请求消耗。
合并资源,减少http请求(合并资源、合并css、js文件、合理使用缓存)
CDN简单来说,就是加速,当一个网站开启了CDN加速,其给用户的感觉是访问网站速度或者下载东西的速度会明显比没有开启加速更快,变快或者下载东西变快了。
当网站开启CDN,用户访问该网站,并非直接访问该网站的原服务器,而是一个服务器分发的离你最近的一个服务器节点,由于服务器离你近了,所以访问速度或者下载速度会更快。
html5:
<img draggable="true">
a.拖动什么 - ondragstart 和 setData()
b.放到何处 - ondragover
c.进行放置 - ondrop
meta标签用来提供页面的一些信息
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0, user-scalable=no"/>
<!--
viewport参数详解:
width:宽度(数值 / device-width)(默认为980 像素)
height:高度(数值 / device-height)
initial-scale:初始的缩放比例 (范围从>0 到10)
minimum-scale:允许用户缩放到的最小比例
maximum-scale:允许用户缩放到的最大比例
user-scalable:用户是否可以手动缩 (no,yes)
-->
SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
一句话总结:都是2D做图,svg是矢量图,canvas是位图。Canvas 是逐像素进行渲染的,适合游戏。
用来装页面上元素的矩形区域,盒子模型包括IE盒子模型和标准盒子模型
box-sizing设置盒子模型
box-sizing:content-box;//默认值,标准盒模型 内容真正宽度=设置的宽度
box-sizing:border-box;//内容真正宽度=设置的width-左右padding-左右border
box-sizing:padding-box;//把padding计算到content里面(有的地方说有,有的说没有)
box-sizing:inhert;//规定从父元素继承此值
标准盒子模型:width就是指的是content
IE盒子模型(怪异盒子模型):width包括content、padding和border
弹性布局,传统的布局时依赖display属性+position+float属性,非常不方便,比如实现垂直居中就很不方便
display:flex声明弹性布局,任何元素都可以声明为弹性布局,inline-flex行内元素弹性布局,声明flex之后子元素的float、clear和vertical-align属性就失效了
容器属性:flex-direction,flex-wrap、flex-flow、justify-content、align-item、align-content
内部元素属性:order、flex-grow、flex-shrink、flex-basis、flex、align-self
order属性:定义项目的排列顺序,顺序越小,排列越靠前,默认为0
flex-grow属性:定义项目的放大比例,即使存在空间,也不会放大
flex-shrink属性:定义了项目的缩小比例,当空间不足的情况下会等比例的缩小,如果定义个item的flow-shrink为0,则为不缩小
flex-basis属性:定义了在分配多余的空间,项目占据的空间。
flex:是flex-grow和flex-shrink、flex-basis的简写,默认值为0 1 auto。
align-self:允许单个项目与其他项目不一样的对齐方式,可以覆盖align-items,默认属性为auto,表示继承父元素的align-items
flex-direction:决定主轴的方向
.box {
display:flex;
//row水平,从左到右,row-reverse从右到左,column垂直从上到下,column-reverse垂直从下到上
flex-direction: row | row-reverse | column | column-reverse;
}
flex-wrap:决定换行方式
.box{
//nowrap不换行,wrap换行,直接在下面换行, wrap-reverse换行到原有到的上面
flex-wrap: nowrap | wrap | wrap-reverse;
}
flex-flow:flex-direction和flex-wrap属性的简写方式,默认为row|nowrap
.box {
flex-flow: <flex-direction> || <flex-wrap>;
}
justify-content:元素在主轴上的对齐方式
.box {
//flex-start左对齐(默认),flex-end右对齐,center居中,space-between两端对齐,元素之间的间隔相等,
//space-around每个元素两侧的间隔相等,元素之间的间隔是两边元素离边界距离的两倍
justify-content: flex-start | flex-end | center | space-between | space-around;
}
align-item:在交叉轴上的对齐方式
.box {
//flex-start交叉轴起点对齐,flex-end交叉轴终点对齐,center居中对齐,
//baseline元素第一行文字的基线对齐,stretch(默认)如果元素未设置高度,将占满整个容器
align-items: flex-start | flex-end | center | baseline | stretch;
}
align-content定义多根轴线的对齐方式
.box {
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
格式化上下文,即为一个独立的区域,内部有自己的一套渲染规则,规定了内部的元素的定位规则,以及与其他元素的关系和相互作用
BFC
格式化上下文,浮动元素和绝对定位元素,非块级盒子的块级容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不为"visiable"的块级盒子,都会为他们的内容创建新的BFC
BFC渲染规则
(1)BFC内部垂直方向边距重叠,两个bfc不会重叠
(2)BFC的区域不会与浮动元素的box重叠
(3)BFC是一个独立的容器,外面的元素不会影响里面的元素
(4)计算BFC高度的时候浮动元素也会参与计算
应用场景:
居中元素有宽高
//需要居中的元素
.box {
position: absolute;
width:100px;
height:100px;
top: 50%;//相对于父元素
left: 50%;
margin-left: -50px;//让元素向相反的方向偏移子元素的一半
margin-top: -50px;
}
.box {
position: absolute;;
width:100px;
height:100px;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
.box {
position: absolute;
width:100px;
height:100px;
top: calc(50% - 50px);//calc动态计算长度值。
left: calc(50% - 50px);
}
居中元素没有宽高:
.box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);//相对于自身的宽和高去偏移
}
.parent{
display: flex;
justify-content: center;
align-items: center;
}
opacity=0,设置透明度为100%,该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定一些事件,如click事件,那么点击该区域,也能触发点击事件。
visibility=hidden,该元素隐藏起来了,不会改变页面布局,但是不会触发该元素已经绑定的事件。
display=none,把元素隐藏起来,并且会改变页面布局,可以理解成在页面中把该元素删除掉一样。
,并在CSS中赋予.clear{clear:both;}
属性即可清理浮动。亦可使用
或
来进行清理。//在css中添加:after伪元素
.parent:after{
/* 设置添加子元素的内容是空 */
content: '';
/* 设置添加子元素为块级元素 */
display: block;
/* 设置添加的子元素的高度0 */
height: 0;
/* 设置添加子元素看不见 */
visibility: hidden;
/* 设置clear:both */
clear: both;
}
<div class="parent">
<div class="f"></div>
</div>
id选择器,类选择器、标签选择器、后代选择器(空格 只要是后代都可以)、子元素选择器(>只能是父子关系)、伪类选择器(a:hover)等(伪类和伪元素不一样,伪类的操作对象是文档树中已有的元素,而伪元素则创建了一个文档树外的元素。)
同一元素引用了多个样式时,排在后面的样式属性优先级高
样式选择器的类型不同时,id选择器>class选择器>标签选择器
标签之间存在层级包含关系时,后代元素会继承父级元素的样式,后代的样式会覆盖父级
带有!importan优先级最高
内联样式>内部样式>外部样式
合理的标签去做合理的事情,便于开发者阅读和写出更优雅的代码,同时让浏览器的爬虫和机器很好地解析。
html,body,.father {
display: flex;
width: 100%;
height: 100%;
}
.left,
.right {
width: 200px;
background-color: blue;
}
.center {
flex: 1;
background-color: red;
}
他们的大小都不是固定的,只是相对父级/html元素的
white-space: nowrap; //文本不换行。
overflow: hidden; //超过部分隐藏
text-overflow: ellipsis; //超过部分用…代替
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
width: 200px;
height: 200px;
background-color: green;
margin: 100px auto;
/* 过渡:过渡的属性 时间 方式 延迟时间 */
transition: all 2s linear 0s;
}
.box:hover{
width: 500px;
background-color: orange;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
width: 200px;
height: 200px;
background-color: green;
margin: 100px auto;
//加过渡让旋转慢一点
transition: all 5s;
}
.box:hover{
/* transform:scale:放大缩小; */
/* transform: scale(2,0.5); */
/* transform:rotate旋转,正值顺时针,负值逆时针 */
/* transform-origin设置以谁旋转 */
/* transform-origin: bottom center;
transform: rotate(360deg); */
/* 3d转换 */
/* rotateY绕Y轴旋转多少度 */
transform: rotateY(360deg);
background-color: orange;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
css动画详解
//一个时钟的例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
width: 3px;
height: 200px;
background-color:black;
margin: 100px auto;
transform-origin: center bottom;
/* animation: 定义的动画名称 持续时间 执行次数 是否反向 运动曲线 延迟执行。(infinite 表示无限次) */
animation: myClock 60s steps(60) infinite;
}
@keyframes myClock{
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
div{
width:0;
height:0;
border:50px solid;
border-color:red tranplate tranplate tranplate;
}
border本质不是长方形的边框,而是三角形
1.采用meta viewport方式(viewport只针对移动端)
<meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5"/>
2.采用transform:scale()方式
transform: scale(0.5,0.5);//transform表示2d/3d的旋转、缩放、移动、倾斜等,scale就是缩放
模板字面量,在es6之前,将字符串拼接在一起的方法是+或者concat方法,而模板字面量可以用``(反引号),再使用${}表示占位符
解构,在es6中可以使用解构从数组和对象中提取值并赋予给独特的变量
for…of(不需要计数器i,忽略索引)循环结合了for(需要计数器)和for…in(需要索引)的优势,仅仅只是循环访问对象中的值(注意forEach仅仅是数组的方法)
展开运算符:…连续三个点,将字面量(字符串字面量、数组字面量、对象字面量、函数字面量)展开为多个元素(展开数组)
箭头函数
闭包是指有权访问另一个函数作用域中的变量的函数,也就是能够读取另一个函数作用域中变量的函数。
闭包的作用: 正常函数执行完毕后,里面声明的变量被垃圾回收处理掉,但是闭包可以让作用域里的变量,在函数执行完之后依旧保持没有被垃圾回收处理掉
正常情况下函数在调用完就会被销毁,但是闭包就是去阻止这件事情发生的
闭包的缺点:
内存泄漏(在退出函数之前,将不使用的局部变量全部删除),this指向的问题
new操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象
new操作符都干了些什么?
apply和call改变函数的this指向,基本没啥区别,主要是参数不同,第一个参数都是要改变指向的对象,第二个是传入的参数,apply是数组,而call是arg1,arg2…
通过bind改变this作用域会返回一个新的函数,这个函数不会马上执行
bind不会立即执行,apply和call会立即执行
手写call
Function.prototype.myCall = function(context) {
if (typeof context === "undefined" || context === null) {
context = window
}
//context=context||window 和上面的代码一样
context.fn = this
const args = [...arguments].slice(1)
const result = context.fn(...args)
delete context.fn
return result
}
实现分析:
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
let result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
手写bind
Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
//返回一个绑定this的函数,这里我们需要保存this
const _this = this
const args = [...arguments].slice(1)
//返回一个函数
return function F() {
//因为返回一个函数,我们可以new F()需要判断能当做构造函数吗
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
var student = {
name: 'xiaoming'
}
function showAge(age) {
console.log(`My name is ${
this.name}, I am ${
age} years old.`)
}
showAge.myCall(student, 18) // My name is xiaoming, I am 18 years old.
基本的(es5):undefined、null、number、Boolean、string
引用数据类型:object、function、array等等
还有一个symbol(es6):用给定名称作为唯一标识,确保唯一,及时采用相同的名称,也会产生不同的值
堆:存储引用类型值的空间
栈:存储基本数据类型、指定代码的环境
上面的除了object,其余都是基本类型,object又包括数组、对象、函数等
区别如下:
转换规则如下:
垃圾回收机制就是为了防止内存泄漏(内存泄漏就是当已经不需要模块内存时,但是这块内存还存在着)垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存
(c#、java、JavaScript都有自动垃圾回收机制,c/c++没有垃圾回收机制)
js垃圾回收有两种方式:标记清除和引用计数
类的创建:new一个function,可以直接在function中定义属性和方法,也可以在这个function的prototype里面添加属性和方法
类的继承:
function Parent2() {
this.name = 'parent2';
this.play = [1, 2, 3]
}
function Child2() {
this.type = 'child2';
}
Child2.prototype = new Parent2();
console.log(new Child2());
容易实现,父类新增原型属性和方法,子类都能访问到,缺点就是原型属性在所有实例中都是共享的,无法实现多继承
function Parent1(){
this.name = 'parent1';
}
function Child1(){
Parent1.call(this);
this.type = 'child1'
}
console.log(new Child1);
可以实现多继承,只能继承实现父类实例的属性和方法,不能继承原型上的属性和方法
function Parent5 () {
this.name = 'parent5';
this.play = [1, 2, 3];
}
function Child5() {
Parent5.call(this);
this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;
es6中有class和extends,可以使用组合(compose来进行组合继承层,有选择的继承父类的方法)
onlicke=function(){}
函数回调函数延迟绑定(回调函数不是直接声明的,而是在通过后面的 then 方法传入的,即延迟传入)。
返回值穿透(会根据 then 中回调函数的传入值创建不同类型的Promise, 然后把返回的 Promise 穿透到外层, 以供后续的调用)。
错误冒泡(前面产生的错误会一直向后传递,被 catch 接收到,就不用频繁地检查错误了)。
实现promise的resolve、reject和fillaly、all、race,如下链接
promise对象代表了未来将要发生的事件,用来传递异步操作的信息
promise对象有两个特点:
1.对象的状态不受外界影响。promise对象代表一个异步操作,有三种状态:
2.一旦状态改变,就不会再变,任何时候都可以得到这个结果。promise对象状态改变,只有两种可能,pending变为fulfilled和pending变为rejected。只要这两种情况发生,状态就不会再改变了,会一直保持这个结果。与事件不同的是,事件错过了就监听不到结果了。
promise的优点:有了promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
缺点:首先无法取消promise,一旦新建就立即执行,中途无法取消。其次,如果不设置回调函数,promise内部抛出的错误,不会反应到外部,第三,当处于pending状态时,无法得知目前进展到哪一阶段(刚开始还是即将完成)
promise通过new来创建,构造函数中包含带有resolve和reject两个参数的回调函数,在回调中执行一些操作,如果一切正常,则调用resolve(resolve还可以将一个对象转化为promise对象),否则reject
var promise = new Promise(function(resolve, reject) {
// 异步处理
// 处理结束后、调用resolve() 或 reject()
});
之后promise对象可以调用.then方法
js是一门动态的、弱类型、基于原型的脚本语言
动态性:在JavaScript对象中,要为一个属性赋值,我们不必事先创建一个字段,只需要在使用的时候赋值即可
弱类型:与java、c/c++不同,js不需要特意指定具体的数据类型,能够很大程度的对一个变量进行复用
解释与编译:js是一门解释型的语言,边翻译边执行
基于原型
跨平台性好
**补充:**编译性语言和解释性语言
编译性语言,只需编译一次就可以把源代码编译成机器语言(c/c++),执行效率较高,比较依赖编译器,跨平台性差一些
解释性语言,源代码不能直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行,程序不需要编译,程序边运行边翻译,运行效率低一些,跨平台性好(JavaScript、python)
浅拷贝(赋值不是拷贝,只是指向了同一个地址空间,浅拷贝是指向的两个地址空间)的限制所在。它只能拷贝一层对象。如果有对象的嵌套,那么浅拷贝将无能为力。但幸运的是,深拷贝就是为了解决这个问题而生的,它能 解决无限极的对象嵌套问题,实现彻底的拷贝。
强制转换:Number()
parseInt()/parseFloat()
var arr = [3, 2, 1];
// for (const key in arr) {
// console.log(key) //0,1,2
// }
// for (const key of arr) {
// console.log(key)//3,2,1
// }
arr.forEach((item, index) =>
{
console.log(item);console.log(index)})
可以应用 for…of 的对象被称为 可迭代的。
for…in
Object.keys(对象)
Object.values(对象)
Object.getOwnPropertyNames(obj),返回一个数组,包含对象自身的所有属性
文档对象模型,定义了访问和操作HTML文档的标准方法,以树形的结构表达html文档
浏览器对象模型,提供了独立于内容而与浏览器窗口进行交互的对象。
所有的函数都有一个prototype属性,原型对象
所有的实例都有一个__proto__属性,执行构造函数的prototype
回流,也叫重排。当我们对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流的过程
具体一点,有以下的操作会触发回流:
防抖:
短时间内大量触发同一事件,只会执行一次函数,实现原理为设置一个定时器,约定在xx毫秒后再触发事件处理,每次触发事件都会重新设置计时器,直到xx毫秒内无第二次操作,防抖常用于搜索框/滚动条的监听事件处理,如果不做防抖,每输入一个字/滚动屏幕,都会触发事件处理,造成性能浪费。
function debounce(fn, delay) {
let timer = null;
return function (...args) {
let context = this;
if(timer) clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
}
}
节流:
防抖是延迟执行,而节流是间隔执行,函数节流即每隔一段时间就执行一次,实现原理为设置一个定时器,约定xx毫秒后执行事件,如果时间到了,那么执行函数并重置定时器,和防抖的区别在于,防抖每次触发事件都重置定时器,而节流在定时器到时间后再清空定时器
function throttle(fn, interval) {
let flag = true;
return funtion(...args) {
let context = this;
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(context, args);
flag = true;
}, interval);
};
};
function A() {
}
function B(a) {
this.a = a;
}
function C(a) {
if (a) {
this.a = a;
}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a); //1,在原型中找到1
console.log(new B().a); //自己有属性a,但是没有传值,所以是undefined
console.log(new C(2).a); //2,有属性a且传值了,所以是2
let a={
},
b='1',//'undefined'
c=1;//undefined
a[b]='123';
a[c]='456';
console.log(a[b]);
//结果为456,对象的属性名称唯一,且数字属性名和字符串属性名是一样的,
//对象的属性名称数据类型可以是布尔、null、undefined等等
//undefined结果也是456
let a={
},
b=Symbol('1'),
c=Symbol('1');
a[b]='123';
a[c]='456';
console.log(a[b]);//结果为123,因为symbol创建的是唯一值
let a={
},
b={
m:'1'},
c={
n:'2'};
a[b]='123';
a[c]='456';
console.log(a[b]);
//结果是456,对象的话都会由object.prototype.toString转换为字符串'object object',不管这个对象是啥
js循环机制:宏任务和微任务
async function async1(){
console.log("async1 start")
await async2()
console.log("async1 end")
}
async function async2(){
console.log("async2")
}
console.log("script start")
setTimeout(function(){
console.log("setTimeout")
},0)
async1();
new Promise(function(resolve){
console.log("promise1")
resolve()
}).then(function(){
console.log("promise2")
})
console.log("script end")
//最终结果
//script start
//async1 start
//async2
//promise1
//script end
//async1 end
//promise2
//setTimeout
//定义a,让下面成立,
//对象跟数字比首先会调用toString方法,这里相当于重写了toString方法,每次调用返回值都加1
var a={
i:0,
toString(){
return ++this.i
}
}
if(a==1 && a==2 && a==3){
console.log('条件成立')
}
function Foo() {
getName = function () {
document.write(1 + "第一个函数输出结果" + "
")
}
return this
}
Foo.getName = function () {
document.write(2 + "第二个函数输出结果" + "
")
}
Foo.prototype.getName = function () {
document.write(3 + "第三个函数输出结果" + "
")
}
var getName = function () {
document.write(4 + "第四个函数输出结果" + "
")
}
function getName() {
document.write(5 + "第五个函数输出结果" + "
")
}
Foo.getName() //2
getName() //4
Foo().getName() //1
getName() //1
new Foo.getName() //2
new Foo().getName() //3
new new Foo().getName() //3
var a=1;
function printA(){
console.log(this.a);
}
var obj={
a:2,
foo:printA,
bar:function(){
printA();
}
}
obj.foo(); //2
obj.bar(); //1
var foo=obj.foo;
foo(); //1
//因为setTimeout为宏任务,由于JS中单线程eventLoop机制,在主线程同步任务执行完后才去执行宏任务,
//因此循环结束后setTimeout中的回调才依次执行,但输出i的时候当前作用域没有,
//往上一级再找,发现了i,此时循环已经结束,i变成了6。因此会全部输出6
for(var i = 1; i <= 5; i ++){
setTimeout(function timer(){
console.log(i)
}, 0)
}//输出5个6
如何解决
以下均输出1 2 3 4 5
//1.利用IIFE(立即执行函数表达式)当每次for循环时,把此时的i变量传递到定时器中
for(var i = 1;i <= 5;i++){
(function(j){
setTimeout(function timer(){
console.log(j)
}, 0)
})(i)
}
//2.给定时器传入第三个参数, 作为timer函数的第一个函数参数
for(var i=1;i<=5;i++){
setTimeout(function timer(j){
console.log(j)
}, 0, i)
}
//3.使用ES6中的let
//let使JS发生革命性的变化,让JS有函数作用域变为了块级作用域,用let后作用域链不复存在
for(let i = 1; i <= 5; i++){
setTimeout(function timer(){
console.log(i)
},0)
}
var test=(function(i){
return function(){
console.log(i*=2)
}
})(2); //到这相当于变量test被赋值为一个函数的返回值
test(5)
//结果还是4,i的值回去寻找上一级的i,跟5没啥关系,所以i不会被销毁,这就是闭包
//下面一段代码相当于定义了一个函数,并马上被执行了,参数i=2
//(function(i){
// return function(){
// console.log(i*=2)
// }
//})(2);
var a=0;
var b=0;
function A(a){
A=function(b){
console.log(a+b++)
}
console.log(a++)
}
A(1) //1
A(2) //4
//首先定义了两个全局变量a和b,还有一个函数A
//A(1):首先形参a赋值为1,然后在A函数里面函数A被重新赋值了(但是这个重新被复制的代码并没被执行)
// 然后执行打印a++,打印结果为1,但是此时形参a=2
arr.push(a):将a添加到数组的最后一项
arr.pop():删除数组的最后一项
arr.shift():删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined 。
arr.unshift(“a”,“b”):将参数a和b添加到原数组开头,并返回数组的长度 。
arr.sort():从小到大排序
arr.reverse():反转数组的顺序
arr.concat():将参数添加到原数组中,原数组并没有被修改
arr.slice():返回从原数组指定开始下标到结束下标之间的项组成的新数组
arr.splice():删除、插入和替换(改变原有的数组)
删除:指定 2 个参数:要删除的第一项的位置和要删除的项数。
书写格式:arr.splice( 1 , 3 )
插入:可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、 0(要删除的项数)和要插入的项。
书写格式:arr.splice( 2,0,4,6 )
替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。
书写格式:arr.splice( 2,2,4,6 )
indexOf():接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的开头(位置 0)开始向后查找。
书写格式:arr.indexof( 5 )
lastIndexOf:接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的末尾开始向前查找。
书写格式:arr.lastIndexOf( 5,4 )
forEach():对数组进行遍历循环,对数组中的每一项运行给定函数。这个方法没有返回值。参数都是function类型,默认有传参,参数分别为:遍历的数组内容;第对应的数组索引,数组本身。
书写格式:arr.forEach(function(x当前元素,index当前元素的index,arr当前元素所属的数组))
filter():“过滤”功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组。
every():判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true。
some():判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true。
书写格式:arr.some()
slice(start,end)
substr(start,length)
substring(start,end)
charAt(pos)
concat(string…)
indexOf(searchString,position)/lastIndexOf(searchString,position)
localeCompare(that)
match(regexp)
replace(searchValue,replaceValue)
jsonp的原理:动态添加一个script标签,而script标签的src属性是没有跨域的限制的
jsonp的缺点
简单请求
它会自动在请求头当中,添加一个Origin字段,用来说明请求来自哪个源。服务器拿到请求之后,在回应时对应地添加Access-Control-Allow-Origin字段,如果Origin不在这个字段的范围中,那么浏览器就会将响应拦截。
因此,Access-Control-Allow-Origin字段是服务器用来决定浏览器是否拦截这个响应,这是必需的字段。与此同时,其它一些可选的功能性的字段,用来描述如果不会拦截,这些字段将会发挥各自的作用。
Access-Control-Allow-Credentials。这个字段是一个布尔值,表示是否允许发送 Cookie,对于跨域请求,浏览器对这个字段默认值设为 false,而如果需要拿到浏览器的 Cookie,需要添加这个响应头并设为true, 并且在前端也需要设置withCredentials属性。
Access-Control-Expose-Headers。这个字段是给 XMLHttpRequest(XMLHttpRequest.getResponseHeader拿到字段) 对象赋值,让它不仅可以拿到基本的 6 个响应头字段(包括Cache-Control、Content-Language、Content-Type、Expires、Last-Modified和Pragma), 还能拿到这个字段声明的响应头字段。
非简单请求:
非简单请求相对而言会有些不同,体现在两个方面: 预检请求和响应字段
预检请求的方法是OPTIONS,同时会加上Origin源地址和Host目标地址,同时也会加上两个关键的字段:
Access-Control-Request-Method, 列出 CORS 请求用到哪个HTTP方法
Access-Control-Request-Headers,指定 CORS 请求将要加上什么请求头
响应字段:
其中有这样几个关键的响应头字段:
Access-Control-Allow-Origin: 表示可以允许请求的源,可以填具体的源名,也可以填*表示允许任意源请求。
Access-Control-Allow-Methods: 表示允许的请求方法列表。
Access-Control-Allow-Credentials: 简单请求中已经介绍。
Access-Control-Allow-Headers: 表示允许发送的请求头字段
Access-Control-Max-Age: 预检请求的有效期,在此期间,不用发出另外一条预检请求。
在预检请求的响应返回后,如果请求不满足响应头的条件,则触发XMLHttpRequest的onerror方法,当然后面真正的CORS请求也不会发出去了。
CORS 请求的响应。绕了这么一大转,**到了真正的 CORS 请求就容易多了,现在它和简单请求的情况是一样的。浏览器自动加上Origin字段,服务端响应头返回Access-Control-Allow-Origin。**可以参考以上简单请求部分的内容。
ajax是异步的JavaScript和xml,在无需重新加载整个网页的情况下,能够与服务器交换数据并更新部分网页
XMLHttpRequest是ajax的基础,用于和服务器交换数据
//创建XMLHttpRequest对象
var xmlHttp=new XMLHttpRequest()
向服务器发送请求,使用open()和send()方法
//open的三个参数,第一个请求类型,第二个请求的url,第三个异步(true)还是同步
//send也有一个参数string,参数是发送的数据,但是只有在post请求才能用,post请求还需要添加请求头
xmlHttp.open("Get","url",true)
xmlHttp.send()
获取服务器响应,使用responseText或responseXML属性
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
XMLHttpRequest有三个重要的属性:
创建XMLHttpRequest对象、open与服务器创建连接,send发送数据、针对返回的不同的响应状态进行处理
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = callback
// get 方式
xhr.open("get", "test.php", true)
// post 方式发送数据 需要设置请求头
xhr.open("post", "test.php", true) //true表示异步,false表示同步
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
// get 不需要传递参数
xhr.send()
// post 需要传递参数
xhr.send("name=jay&age=18")
function callback() {
// 判断异步对象的状态
if(xhr.readyState == 4) {
// 判断交互是否成功
if(xhr.status == 200) {
// 获取服务器响应的数据
var res = xhr.responseText
// 解析数据
res = JSON.parse(res)
}
}
}
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 可选地,上面的请求可以这样做
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
虚拟dom中采用的算法,只更新我们修改的那一小块dom而不是更新整个dom(直接到真实的dom上去渲染会更新整个dom树的渲染)
首先根据真实的dom生成一颗虚拟dom,当虚拟dom某个节点的数据改变后会生成一个新的虚拟节点,然后虚拟节点和旧的节点比较,发现有不一样的地方就直接修改在真实的dom上,然后使旧的节点值为虚拟节点的值
diff的过程就是调用patch的函数,比较新旧节点,一边比较一边给真实的dom打补丁
diff比较新旧节点的时候,只会在同层级进行比较,不会跨层级比较
两棵树完全的diff算法时间复杂度是O(n3),vue转换成了O(n),只比较同级不考虑跨级
patch(oldVnode,vnode):首先判断是否值得比较(是不是同一个元素),值得比较就执行patchVnode,不值得比较就直接用Vnode替换oldVnode
如果两个节点是一样的,那么就深入的检查他们的子节点,根据不同的情况做出处理
加上key可以减少性能消耗,更高效的更新虚拟dom。vue是用的diff算法,diff源码中有一个判断是否是同一个元素的sameVnode函数,标准是key相同且tag相同,如果不加key,默认所有的节点的key都是相同的,就会按照就地复用来进行比较(当旧的节点有abcd,而新的节点只有bcd,那a就会复用b的结构)
表面上没有什么问题,都是有两点不足:
1.index作为key,其实就等于不加key
2.index作为key,只适用于不依赖子组件状态或临时dom状态(表单输入值)的列表渲染输出
因为不管你数组的顺序怎么颠倒,index 都是 0, 1, 2 这样排列,导致 Vue 会复用错误的旧子节点,做很多额外的工作。
**打包原理:**把所有依赖打包成一个bundle.js文件,通过代码分割成单元片段并按需加载。
bundle是由webpack打包出来的文件,chunk是指webpack在进行模块的依赖分析的时候,代码分割出来的代码块。module是开发中的单个模块。
webpack与grunt、gulp的不同?
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。
Task
,会找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。不同的作用
Loader直译为"加载器"。webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
不同的用法
Loader在module.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options)
Plugin在plugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
webpack的热更新又称热替换。这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可
多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。 多页应用中要注意的是:
每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表
随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
很多组件库已经提供了现成的解决方案,如Element出品的babel-plugin-component和AntDesign出品的babel-plugin-import 安装以上插件后,在.babelrc配置中或babel-loader的参数中进行设置,即可实现组件按需加载了。
CommonJS(node.js、webpack)是一种被广泛使用的js模块化规范,核心思想是通过require方法来同步加载依赖的其他模块,通过module.exports导出需要暴露的接口。
//导入
const moduleA = require ( ’. / moduleA’);
//导出
module .exports = moduleA.someFunc;
common.js用于服务器,AMD用于浏览器
AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"
AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:
require([module], callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。
两者的模块导入导出语法不同,commonjs是module.exports,exports导出,require导入;ES6则是export导出,import导入。
commonjs是运行时加载模块,ES6是在静态编译期间就确定模块的依赖。(之后就说只了解这么多)
与技术无关,代表的是一种软件架构风格
在http协议中,四个表示操作方式的动词:GET/Post/put/Delete,他们分别对应四种基本操作。
Get用来获取资源。post用来新建立资源,也可以更新资源。put用来更新资源。Delete用来删除资源。
还有一些状态码,url尽量使用一看就知道意思的取名
软件工程就是按照工程学的管理方式,有组织、有计划的,在一定的质量基础、时间限度和成本范围内,实现功能明确的软件系统。几个阶段
是model-view-viewmodel的缩写
model和view之间并没有直接的联系,而是通过viewmodel进行交互,model和viewmodel之间的交互是双向的,因此view数据的变化会同步到model中,而model中数据变化也会立即反应到view中
mvc:用户对View的操作交给了Controller处理,在Controller中响应View的事件调用Model的接口对数据进行操作,一旦Model发生变化便通知相关视图进行更新。
mvvm:它实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变。
MVC有两个很明显的问题:
1.m层和v层直接打交道,导致这两层耦合度高
2.因为所有逻辑都写在c层,导致c层特别臃肿
为了解决这两个问题,MVC的变种模式出现了MVVM.
vue实例都有一个完整的生命周期,初始化、运行到结束。
创建期间的生命周期函数:
1.把模板编译为render函数
2.根据根节点render函数的调用,递归的生成虚拟dom
3.通过diff算法对比虚拟dom,渲染到真实dom(类同react的虚拟DOM渲染过程)
指令有v-bind,v-model(一些表单控件input数据的双向绑定),v-if,v-show,v-for等等
通过vue.directive来自定义
代码详情
vue.directive(‘参数名称’,{这里面可以写钩子函数}) 定义指令,它有两个参数
-参数1:指令名称,注意在定义的时候,指令名称前不需要加 v- 前缀,但是在调用的时候,必须加v- 前缀。
参数2:参数2是一个对象,这个对象身上有一些指令相关的函数(也就是下边介绍的钩子函数),这些函数可以在特定的阶段,执行相关操作。
操纵dom的一个属性,在标签中定义ref=“xxx”,用this.$refs.xxx来调用
子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码
用法:(具名插槽)可以在slot中写上name=“xxx”,然后父组件中插入的内容中用v-slot绑定对应的name值,还有默认插槽,就是没有名字的插槽
作用域插槽就是一般需要传递参数数据,子组件slot中通过:data="data"来绑定数据,然后在父组件中用slot-scope来获取子组件中的数据
双向数据绑定就是让数据层和视图层中的数据同步,在写入数据时视图层实时的跟着更新
params和query
区别:query要用path来引入,params要用name来引入,接收参数都是类似的;query更加类似于get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
XSS 攻击是指浏览器中执行恶意脚本(无论是跨域还是同域),从而拿到用户的信息并进行操作。
这些操作一般可以完成下面这些事情:
CSRF(Cross-site request forgery), 即跨站请求伪造,指的是黑客诱导用户点击链接,打开黑客的网站,然后黑客利用用户目前的登录状态发起跨站请求。
CSRF攻击一般会有三种方式: