前端工程师实习面试总结

前端面试题整理

Vue相关

一、.Vue生命周期

从无到有,从有到挂载到界面,再到界面数据更新,再到最后死亡的一个过程。详细来说也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程。下面找了两个比较有代表性的图来抽象理解这一概念。
在这里插入图片描述
在这里插入图片描述

二、.Vuex

vue状态管理工具,提供这样一个在多个组件间共享状态的插件。
比如用户的登录状态、用户名称、头像、地理位置信息等等,需要在多个界面进行状态共享。
Vuex的使用:
1.先提取出一个公共的store对象,用于保存在多个组件中共享的状态。

import Vue from 'vue'
import Vuex from 'vuex'

import mutations from "./mutations.js"
import actions from "./actions.js"
import getters from "./getters.js"

//1.安装插件
Vue.use(Vuex)

//2.创建store对象
const store = new Vuex.Store({
    
    
	state:{
    
    
		cartList:[]
	},
	mutations,
	actions,
	getters
})

//3.挂载到vue实例上
export default store

2.将store对象放置在new Vue对象中,这样可以保证在所有的组件中都可以使用到

3.在main.js中import入store,在其他组件中使用store对象中保存的状态即可

通过this.$store.state.属性的方式来访问状态
通过this.$store.commit('mutation中方法')来修改状态

Vuex的核心概念:
State单一状态树
Getters
Mutation:Vuex的store状态的更新唯一方式:提交Mutation
传参可传递对象
在这里插入图片描述

Action
Module

三.vue组件间通信

1.父传子:props
2.子传父:this.$emit(事件,参数),发布订阅的方式
3.事件总线

$bus,vue.prototype.$bus = new Vue()

this.bus.$emit(事件,参数)//发出事件
this.bus.$on(事件,回调函数)

在这里插入图片描述

4.vueX

5.插槽slot

6. p a r e n t / parent/ parent/children,获取到父组件或子组件实例

7.ref

四.webpack

前端模块化打包工具,根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源,可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求.
包含打包文件和目标文件,在webpack的配置文件webpack.config.js中包含了入口entry路径和出口output路径。
要实现不同种类文件的打包需要引入不同的loader,然后在配置文件下进行配置,如css需要css-loader和style-loader
比较gulp
gulp的核心是Task,定义任务流,称为前端自动化任务管理工具,适用于工程模块简单的项目,实现简单的合并压缩。

五.watch和computer区别

相同之处
watch和computed都是以Vue的依赖追踪机制为基础的,它们都试图处理这样一件事情:当某一个数据(称它为依赖数据)发生变化的时候,所有依赖这个数据的“相关”数据“自动”发生变化,也就是自动调用相关的函数去实现数据的变动。

不同之处:
computed是计算属性,事实上和和data对象里的数据属性是同一类的(使用上)
computed擅长处理的场景:一个数据受多个数据影响

 data: {
    
    
    // 路飞的全名:蒙奇·D·路飞
    firstName: '蒙奇',
    secName: 'D',
    thirdName: '路飞'
  },
  computed: {
    
    
    luFei_Name: function () {
    
    
      return this.firstName + this.secName + this.thirdName
    }
  }
// 将“路飞”改为“海军王”
vm.thirdName = '海军王'  // 蒙奇·D·海军王

watch:类似于监听机制+事件机制
watch擅长处理的场景:一个数据影响多个数据

// 在watch中,一旦haiZeiTuan_Name(海贼团名称)发生改变
   data选项中的船员名称全部会自动改变 (suoLong,naMei,xiangJiShi),并把它们打印出来
 
// 多个)船员名称数据 --> 依赖于 --> (1个)海贼团名称数据一个数据变化 --->  多个数据全部变化
data: {
    
    
    haiZeiTuan_Name: '草帽海贼团',
    suoLong: '草帽海贼团索隆', (haiZeiTuan_Name + 海贼名称)
    naMei: '草帽海贼团娜美',
    xiangJiShi: '草帽海贼团香吉士'
},
watch: {
    
    
    haiZeiTuan_Name: function (newName) {
    
    
      this.suoLong = newName + '索隆'
      this.naMei = newName + '娜美'
      this.xiangJiShi = newName + '香吉士'
      console.log(this.suoLong)
      console.log(this.naMei)
      console.log(this.xiangJiShi)
    }
 }
// 更改watch选项中监控的主数据
vm.haiZeiTuan_Name = '橡胶海贼团'
结果:this.suoLong会变为 '橡胶海贼团索隆',以此类推

六.vue如何监听数据改变并通知相应组件刷新界面

总的来说:vue对象的响应式数据原理分为监听和发布订阅两个过程,对于每个属性通过数据劫持来它的变化,同时给每个属性创建一个Dep发布者对象,Dep对象会添加所有用到这个属性的地方到Subs里,当监听到该数据发生变化后,该Dep对象会循环遍历所有用到他的组件,调用每个组件的update方法进行视图层面的数据更新。
1.Object.defineProperty->监听对象属性的改变

Object.defineProperty(obj,key,{
    
    
get(){
    
    
console.log('获取' + key + '对于值');
return value
},
set(newvalue){
    
    
console.log('监听' + key + '改变');
//告诉谁了?谁告诉谁?谁在用了?
//根据解析html代码,获取到哪些人有用这些属性
value = newValue
//dep是紧接着下面的发布订阅对象
dep.notify()
}
})

2.发布订阅模式

class Dep{
    
    
constructor(){
    
    
this.subs = []
}
addSub(Watcher){
    
    
this.subs.push(watcher)
}
notifu(){
    
    
this.subs.forEach(item => {
    
    
item.update()
})
}
}

class Watcher{
    
    
constructor(name){
    
    
this.name = name;
}
updata(){
    
    
console.log(this.name + '发生update');
}
}

const dep = new Dep()
const w1 = new Watcher('张三')
dep.addSub(w1)

在这里插入图片描述

7.nextTick

根据官方文档的解释,它可以在DOM更新完毕之后执行一个回调

在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中
nextTick是全局vue的一个函数,在vue系统中,用于处理dom更新的操作。vue里面有一个watcher,用于观察数据的变化,然后更新dom,vue里面并不是每次数据改变都会触发更新dom,而是将这些操作都缓存在一个队列,在一个事件循环结束之后,刷新队列,统一执行dom更新操作。

8.虚拟DOM

虚拟 DOM 到底是什么,说简单点,就是一个普通的 JavaScript 对象
而原生 DOM 因为浏览器厂商需要实现众多的规范(各种 HTML5 属性、DOM事件),即使创建一个空的 div 也要付出昂贵的代价。虚拟 DOM 提升性能的点在于 DOM 发生变化的时候,通过 diff 算法比对 JavaScript 原生对象,计算出需要变更的 DOM,然后只对变化的 DOM 进行操作,而不是更新整个视图。
由于JavaScript需要借助浏览器提供的DOM接口才能操作真实DOM,所以操作真实DOM的代价往往是比较大的(这其中还涉及C++与JavaScript数据结构的转换问题)。再加上修改DOM经常导致页面重绘,所以一般来说,DOM操作越多,网页的性能就越差。我们以一个简单的图例来理解这个过程:
在这里插入图片描述
,虚拟DOM并不能消除原生的DOM操作,但是虚拟DOM带来了一个重要的优势,那就是我们可以在完全不访问真实DOM的情况下,掌握DOM的结构,这为框架自动优化DOM操作提供了可能。举例来说,如果我们本打算手动进行三次真实DOM操作,而框架在分析了虚拟DOM的结构后,把这三次DOM操作简化成了一次,这不就带来了性能上的提升吗?
优点:
1.虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种GUI。
2. diff 算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗
缺点:
无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入

9.Diff算法:

css相关

0.CSS盒子模型

标准盒子模型:宽度=内容的宽度(content)+ border + padding
低版本IE盒子模型:宽度=内容宽度(content+border+padding)

1.css三角形及梯形:

通过盒子模型的border构建

		*{
    
    
			margin: 0;
			padding: 0;
		}
		div{
    
    
			margin: 20px auto;
			width: 0px;
			height: 0px;
			border: 10px;
			border-style: solid;
			border-color: red  transparent  blue  transparent ;
		}

2.垂直居中布局

1.三种定位

		不需要考虑宽高,但必须要有宽高
.box{
    
    
				position: absolute;
				left: 0;
				top: 0;
				right: 0;
				bottom: 0;
				margin: auto;
			} -->
			
			必须知道盒子的宽和高
<!-- 			.box{
    
    
				position: absolute;
				top: 50%;
				left: 50%;
				margin-top: -50px;
				margin-left: -25px;
			} -->

<!-- 			
css3新属性transform,不需要知道宽高,也不需要有宽高,确定是不兼容

.box{
    
    
				position: absolute;
				top: 50%;
				left: 50%;
				transform: translate(-50%, -50%);
			} -->

2.flex布局

			<!-- 2.flex布局,问题是不兼容,移动端用的比较多 -->
<!-- 		body{
    
    
				display: flex;
				align-items: center;
				justify-content: center;

			} --> */

3.display: table-cell;

			/* <!-- 3.display: table-cell; 控制文本要求父级固定宽高 --> */
					
			body{
    
    
				display: table-cell;
				vertical-align: middle;
				text-align: center;
				width: 500px;
				height: 500px;
				background-color: #006705;
			}

3.清除浮动

1.父元素添加:overflow:hidden
2.父元素同时浮动
3.最后添加空元素,clear:both
4.伪类

float_div::after{
    
    
content:".";
clear:both;
display:block;
height:0;
overflow:hidden;
visibility:hidden;
}

4.左右固定,中间自适应三栏布局

1.圣杯布局,为了中间div内容不被遮挡,将最外容器设置了左右padding-left和padding-right后,将左右两个div用相对布局position: relative并分别配合right和left属性,以便左右两栏div移动后不遮挡中间div。

.container{
    
    
				height: 100%;
				padding: 0 200px;
			}
			
			.left,
			.right{
    
    
				width: 200px;
				min-height: 200px;
				background-color: lightblue;
			}
			
			.center{
    
    
				width: 100%;
				min-height: 400px;
				background-color: lightsalmon;
			}
			
			.left,
			.center,
			.right{
    
    
				float: left;
			}
			
			.left{
    
    
				margin-left: -100%;
				position: relative; 
				left: -200px;
			}
			.right{
    
    
				margin-right: -200px;

			}

2.双飞翼布局,为了中间div内容不被遮挡,直接在中间div内部创建子div用于放置内容,在该子div里用margin-left和margin-right为左右两栏div留出位置。

.container,
			.left,
			.right{
    
    
				float: left;
			}
			
			.container{
    
    
				width: 100%;

			}
			.container .center{
    
    
				margin: 0 200px;
				min-height: 400px;
				background: lightsalmon;
			}
			
			.left,
			.right{
    
    
				width: 200px;
				min-height: 200px;
				background: lightblue;
			}

			
			.left{
    
    
				margin-left: -100%;	
			}
			.right{
    
    
				margin-left: -200px;
			}

.center{
width: calc(100% - 400px)
}
4.flex

.container{
    
    
				display: flex;
				justify-content: space-between;
				height: 100%;
			}
			.left,
			.right {
    
    
				flex: 0 0 200px;  //放大比例/缩小比例/占比
				height: 200px;
				background-color: lightblue;
			}
			.center {
    
    
				flex: 1; //自动分配剩余空间
				min-height: 400px;
				background-color: lightsalmon;
			} 

5.css优先级

不同级别:
总结排序:!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性
1.属性后面加!import 会覆盖页面内任何位置定义的元素样式
2.作为style属性写在元素内的样式
3.id选择器
4.类选择器
5.标签选择器
6.通配符选择器(*)
7.浏览器自定义或继承
同一级别:
eg:

	<style type="text/css">
			.a{
    
    
				background-color: red;
			}
			.b{
    
    
				background-color: blue;
			}
			.c{
    
    
				background-color: yellow;
			}
		</style>
			</head>
	<body>
	//这么写与a,b的定义顺序有关
		<button type="button"  class="a b "  ></button>
		//这么写是最先的a
		<button type="button"  class="a "  class=" c" class=" b"></button>
	</body>
</html>

6.回流和重绘

回流:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree。
重绘:当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

js相关

0.创建对象的方式:

1.工厂模式:
无法识别对象类型

function creatPerson(name,job,age){
    
    
var o = new Object();
o.name = name;
o.job = job;
o.age = age;
o.sayName = function(){
    
    
alert(this.name);
};
return o;
}

var person1 = creatPerson('jack','Doctor',27)

2.构造函数模式:
确定:每个方法都要在每个实例上重新创建一遍

function person(name,job,age){
    
    
this.name = name;
this.job = job;
thisage = age;
this.sayName = function(){
    
    
alert(this.name);
};
}

var person1 = new person('jack','Doctor',27)

3.原型模式:
引用类型的浅复制导致无法复用
在这里插入图片描述

function Person(){
    
    }
Person.prototype.name = 'Nicholas';
Person.prototype.age= 29;
Person.prototype.job= 'Softwars Engineer';
Person.prototype.sayName= function(){
    
    
alert(this.name);
};
var person1 = new Person();

4.组合构造函数和原型:
就是引用类型放构造函数里
方法放原型里

function person(name,job,age){
    
    
this.name = name;
this.job = job;
this.age = age;
this.friends = ['Shelby','Court']
};
}
Person.prototype.sayName= function(){
    
    
alert(this.name);
};

1.继承

1.原型链继承,过多继承没用属性且会共享引用数据类型
2.构造函数继承,简单理解为借用,方法和函数每次都会重新定义
3.组合继承:方法用原型链,其他用构造函数。但会调用两次超类型的构造函数
4.组合寄生继承:
首先基于已有对象创建新对象
在这里插入图片描述

在这里插入图片描述

2.原型和原型链

每个函数都有一个prototype属性(只有函数才有prototype属性),这是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。实例通过__proto__指向这个原型对象。而原型链就是让原型对象等于另一个类型的实例,如此形成一条链,通过__proto__向当前实例所属类的原型上查找属性或方法的机制,如果找到Object的原型上还是没有找到想要的属性或者是方法则查找结束,最终会返回undefined
在这里插入图片描述

3.跨域相关

浏览器同源策略(协议+端口号+域名要相同),否则算是跨域
解决方案:
1、jsonp跨域(只能解决get) 原理:动态创建一个script标签。利用script标签的src属性不受同源策略限制,因为所有的src属性和href属性都不受同源策略的限制,可以请求第三方服务器资源内容
步骤: 1).去创建一个script标签 2).script的src属性设置接口地址 3).接口参数,必须要带一个自定义函数名,要不然后台无法返回数据 4).通过定义函数名去接受返回的数据。
2.服务器设置对CORS的支持 原理:服务器设置Access-Control-Allow-Origin HTTP响应头之后,浏览器将会允许跨域请求。
简单请求:get\head\post:
浏览器会直接发送CORS请求,具体说来就是在header中加入origin请求头字段。同样,在响应头中,返回服务器设置的相关CORS头部字段,Access-Control-Allow-Origin字段为允许跨域请求的源。请求时浏览器在请求头的Origin中说明请求的源,服务器收到后发现允许该源跨域请求,则会成功返回。
非简单请求:
put\delete\connect\options浏览器会自动先发送一个options请求,如果发现服务器支持该请求,则会将真正的请求发送到后端,反之,如果浏览器发现服务端并不支持该请求,则会在控制台抛出错误。
3.Comet长轮询(单向)
4.Web Sockets(双向)

4.get和post的区别

在这里插入图片描述

5.事件流

事件流描述的是从页面中接受事件的顺序,
事件:捕获阶段 处于目标阶段 事件冒泡阶段
自定义事件 :addEventListener(要处理的事件名称,处理函数,true/false)最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
IE的自定义事件:div.attchEvent(‘on’+事件类型,处理函数)只支持事件冒泡
删除自定义事件:removeEventListener(,传入函数必须相同,)
detachEvent()
1、事件捕获阶段:实际目标div在捕获阶段不会接受事件,也就是在捕获阶段,事件从document到再到就停止了。
2、处于目标阶段:事件在div发生并处理,但是事件处理会被看成是冒泡阶段的一部分。
3、冒泡阶段:事件又传播回文档
取消冒泡:event.stopPropagation()
IE:cancelBubble()
阻止默认事件:preventDefault(),只有cancelable属性为true的事件才能使用

  阻止冒泡事件event.stopPropagation()
	  function stopBubble(e) {
    
    
    		if (e && e.stopPropagation) {
    
     // 如果提供了事件对象event 这说明不是IE浏览器
      		e.stopPropagation()
    		} else {
    
    
      		window.event.cancelBubble = true //IE方式阻止冒泡
    	      }
  		   }
   阻止默认行为event.preventDefault()
 function stopDefault(e) {
    
    
    if (e && e.preventDefault) {
    
    
      e.preventDefault()
    } else {
    
    
      // IE浏览器阻止函数器默认动作的行为
      window.event.returnValue = false
    }
  }

不支持冒泡的事件:鼠标事件:mouserleave mouseenter 焦点事件:blur focus UI事件:scroll resize
事件委托
对“事件处理程序过多”问题的解决方案,利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。比如:
在这里插入图片描述
其中包含3个被单击后会执行操作的列表项。按照传统的做法,需要为它们添加3个事件处理程序。
在这里插入图片描述
使用事件委托,只需在DOM数中尽量最高的层次上添加一个事件处理程序。占用内存少,提升整体性能。

6.防抖、节流

防抖:

debounce(func, delay){
    
    
	//设置定时器开关
	let timer = null

	//返回防抖函数
	return function(...args){
    
    
	//在防抖期内再次触发,重新计时
		if(timer) clearTimeout(timer)

		timer = setTimeout(() => {
    
    
			func.apply(this,...args)

		},delay)

	}

}

节流

throttling(func, delay){
    
    
	//设置定时器开关
	let timer = null

	//返回节流函数
	return function(...args){
    
    
	//在节流期内再次触发,不会执行函数
		if(timer) return
		timer = setTimeout(() => {
    
    
			func.apply(this,...args)

		},delay)

	}

}

7.call、apply、bind

call() 和apply()的第一个参数相同,就是指定的对象。这个对象就是该函数的执行上下文。

call()和apply()的区别就在于,两者之间的参数。

call()在第一个参数之后的 后续所有参数就是传入该函数的值。

apply() 只有两个参数,第一个是对象,第二个是数组,这个数组就是该函数的参数。
bind() 方法和前两者不同在于: bind() 方法会返回执行上下文被改变的函数而不会立即执行,而前两者是 直接执行该函数。他的参数和call()相同。
bind的实现

Function.prototype.myBind = function(objThis,...params){
    
    
	const thisFn = this; //保存当前调用的函数this
	let funcForBind = function(...secondParams){
    
    
		//判断是不是new的bind
		const isNew = this instanceof funcForBind();
		const thisArg = isNew ? this : objThis,
		//就是把call进行了封装
		return thisFn.call(thisArg,...params,...secondParams)
	}
	funcForBind.prototype = Object.create(thisFn.prototype)
	return funcForBind()
}

call的实现

// call的实现:原理就是把要调用的函数拷贝一份给新的作用域
Function.prototype.myCall = function(thisArg,...arr){
    
    
	//判断是否传入的作用域
	if(thisArg === null || thisArg === undefined){
    
    
		thisArg = window;
	}
	//定义不重复的属性
	const specialMethod = Symbol('anything');
	//将这个不重复的属性给到thisArg
	thisArg[specialMethod] = this;
	//调用函数
	let result = thisArg[specialMethod](...arr);
	delete thisArg[specialMethod];
	
	return result;
	
}

8.数组常见方法

1.数组检测:isArray、.constructor()、[] instanceof Array、Array.prototype.isPrototypeOf(arr)、Object.prototype.toString.call()、Object.getPrototypeOf(arr)
2.栈方法:push()栈尾插入任意,pop()末尾移除一项
3.队列方法:shift()移除数组第一项,unshift()数组头部加一项
4.重排序:reverse()反转,sort(可传入函数)
5.操作方法:concat()合并数组,slice()接收两个参数,返回项的起始和结束
splice()删除(删除的第一项,删除的项数)、插入(起始位置,删除的项0、插入的项)、替换(起始位置,删除的项1、插入的项)
6.位置方法:indexOf()和lastIndexOf(),接收两个参数(要查找的项,查找起点的位置索引(可选))
7.迭代方法:
every()
filter()
forEach()
map()
some()
8.归并方法:
reduce(function(pre,cur,index,array){})

9.闭包

简单来说闭包就是在函数里面声明函数,本质上说就是在函数内部和函数外部搭建起一座桥梁,使得子函数可以访问父函数中所有的局部变量,只能取得函数中任何一个变量的最后一个值。会造成this的指向问题和内存泄漏问题(及闭包作用域指向的某个HTML元素无法销毁)
解决方案:
1.立即执行函数
2.用let
3.解决内存泄漏:将引用变量复制一份,然后让其=null.

10.ajax

ajax的原理:相当于在用户和服务器之间加一个中间层(ajax引擎),使用户操作与服务器响应异步化。
优点:在不刷新整个页面的前提下与服务器通信维护数据。不会导致页面的重载
可以把前端服务器的任务转嫁到客服端来处理,减轻服务器负担,节省宽带.
Ajax的四个步骤

1.创建ajax实例xml

2.执行open 确定要访问的链接 以及同步异步

3.监听请求状态( xhr.readyState==4表示接收数据完成,可以在客户端使用,xhr.status=200&&300表示请求成功到内容,可以使用responseText或responseXML.)

4.获取DOM进行修改

11.从输入url到页面展现的过程

1、浏览器的地址栏输入URL并按下回车。
2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
3、DNS解析URL对应的IP。Internet上的每个设备都会获得一个IP地址,该地址是查找相应Internet设备所必需的,DNS把域名装换成IP地址的步骤:
1.域名解析器接收输入的url---->将url传入根服务器进行查询---->根服务器使用顶级服务器(TLD)的地址相应解析器---->解析器向后缀如.com TLD进行请求----> TLD使用域名服务器url的IP地址进行响应----解析器向域名服务器进行查询----->域名服务器把该url的IP发给解析器
4、根据IP建立TCP连接(三次握手)。
5、HTTP发起请求。
6、服务器处理请求,浏览器接收HTTP响应。
7、渲染页面,构建DOM树。
8、关闭TCP连接(四次挥手)

12.浏览器渲染的主要流程

将html代码按照深度优先遍历来生成DOM树。 css文件下载完后也会进行渲染,生成相应的CSSOM。 当所有的css文件下载完且所有的CSSOM构建结束后,就会和DOM一起生成Render Tree。 接下来,浏览器就会进入Layout环节,将所有的节点位置计算出来。 最后,通过Painting环节将所有的节点内容呈现到屏幕上。

13.session、cookie、localStorage、sessionStorage

cookie:存放在客户端一个txt文件,在设置的cookie过期时间之前一直有效,数据不能超过4k,在所有同源窗口中共享;不是很安全
localStorage:存放在客户端,始终有效,5M,在所有同源窗口中共享
sessionStorage:存放在客户端,当前浏览器窗口关闭前有效,5M,不在不同的浏览器窗口中共享
session:存放在服务器,比较安全

14.xss跨站脚本攻击

指的是恶意攻击者往Web页面里插入恶意JS代码,当用户浏览该页之时,嵌入其中Web里面的JS代码会被执行,从而
达到恶意的特殊目的。
举例子:在论坛放置一个看是安全的链接,窃取cookie中的用户信息
防范:1.尽量采用post而不使用get提交表单
2.避免cookie中泄漏用户的隐式
3.使用白名单过滤掉用户输入的恶意字符

15.CSRF

CSRF(Cross-Site Request Forgery,跨站点伪造请求)是一种网络攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的情况下执行在权限保护之下的操作,具有很大的危害性。具体来讲,可以这样理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。

  1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

  1. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

  2. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;

  3. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
    CSRF漏洞防御主要可以从三个层面进行,
    服务端的防御:验证HTTP Referer字段,在请求地址中添加token并验证,在HTTP头中自定义属性
    用户端的防御:要轻易点击网络论坛、聊天室、即时通讯工具或电子邮件中出现的链接或者图片
    安全设备的防:。

16.事件循环

JavaScript是单线程的,但浏览器是多线程的,JS就是通过事件循环来实现异步操作的,JavaScript的核心就是事件循环机制,理解了事件循环机制,就理解了JS的执行机制。
js任务同步任务在一个JS主栈中,遇到异步处理任务时放到Event Quque里,这个事件队列分为微任务和宏任务,先执行微任务,再执行宏任务。
宏任务一般是:包括整体代码script,setTimeout,setInterval。

微任务:Promise,process.nextTick,async、await

计算机网络

一、.计算机五层协议
1 物理层:物理设施,可以传递0 1的信号

2 数据链路层:
(1)把0、1的信号组成数据包(帧),这是数据链路层的数据包(之后还有IP层 传输层 应用层的数据包)。
(2)数据包的结构都是head+data。
(3)这里的head是数据包的说明项,比如发送者 接受者 数据格式等等,数据链路层的发送者和接受者的数据是MAC地址(网卡的MAC地址,全球唯一)

3 IP层:因为数据包的发送本质上是把数据包发送给局网中所有的计算机,计算机判断数据包的接受者的MAC地址,如果一致就接收,如果不一致就丢包。 但是计算机网络是在是庞大,给几十亿台计算机每一台发送数据包是不可能的。所以需要新的一组地址来表示计算机的位置。这就是IP层的作用。 判断一个计算机的位置需要IP地址+MAC地址。首先通过IP地址(路由)找到计算机所在的局网,然后广播(发送给每一台计算机)。

4 传输层:确定计算机上的端口(一个程序占用一个端口)。

5 应用层:规定应用程序的数据格式,例如Eamil FTP WWW等等。

数据结构

1.两个栈实现一个队列

class Solution:
    def __init__(self):
        self.acceptStack = []
        self.outputStak = []
    def push(self, node):
        # write code here
        self.acceptStack.append(node)
    def pop(self):
        # return xx
        if self.outputStak == []:
            while self.acceptStack:
                self.outputStak.append(self.acceptStack.pop())
        if self.outputStak:
            return self.outputStak.pop()
        else:
            return None

2.判断一个链表是否有环

1.快慢指针
2.判断极端条件,如果链表为空,或者链表只有一个结点,一定不会带环

3.排序

冒泡排序

			// 前后两个数两两进行比较,符合则交换.
			
			var arr =  [9, 8, 7, 6, 5, 4]
			// 比较的轮次
			function maoPaoPaiXu(arr){
    
    
			for(var i = 0; i < arr.length; i++){
    
    
				// 每次比较的次数
				for(var j = 0; j < arr.length - i - 1; j++){
    
    
					// 判断前后两个数符合交换条件吗
					if (arr[j] > arr[j + 1]){
    
    
						var tmp = arr[j]
						arr[j] = arr [j + 1]
						arr[j + 1] = tmp
					}
				}
			}

快排序

	var arr=[1,23,11,2,1,14,5,11,9,8];
		 function quickSort(arr){
    
    
			if(arr.length<=1){
    
    
				return arr;
			}
			var left=[],right=[];
			//取中间的数作为比较的基数,也可以index=0
			var index=Math.ceil(arr.length/2);
			var privot=arr.splice(index,1)[0];
			for(var i=0;i<arr.length;i++){
    
    
				if(arr[i]<privot){
    
    
					left.push(arr[i]);
				}else{
    
    
					right.push(arr[i]);
				}
			}
			//递归
			return quickSort(left).concat([privot],quickSort(right));
		}

4.数组去重

// 1.Set
// let arr = [...new Set(array)];
// let arr = Array.from(new Set(array));
// console.log(arr)
// 3.创建新数组,不重复的push进去
function unique3(arr)
{
    
    
    var result = []; //结果数组
    for(var i = 0; i < arr.length; i++) 
    {
    
    
        //如果在结果数组result中没有找到arr[i],则把arr[i]压入结果数组result中
        if (result.indexOf(arr[i]) == -1) result.push(arr[i]);
    }
    return result;
}

5.判断字符串是否回文

//先把单词转换成数组,再通过reverse函数进行反转,进行比较
var str = 'abababa'
function checkPalindrom(str){
    
    
	return str = str.split('').reverse().join('');
}
console.log(checkPalindrom(str))

6.数组中两数之和等于目标值

// :在前一个算法的基础上降低时间复杂度。我们可以将数组排序,然后定义两个指针,一个指针i从左向右,另一个从j右向左。 
// ①如果data[i]+data[j] < tager ,则++i 
// ②如果data[i]+data[j] > tager ,则–j 
// ③如果data[i]+data[j] > tager ,则就找到 
// 由于排序的最佳时间复杂度为O(nlogn),两个指针的遍历时间复杂度为O(n)。所以总的时间复杂度为O(nlogn)
function getSetNum(arr, target){
    
    
	arr = arr.sort()
	let i = 0
	let j = arr.length - 1
	while(i < j)
	{
    
    
		if(arr[i] + arr[j] == target){
    
    
			return[arr[i], arr[j]]
		}else if(arr[i] + arr[j] < target){
    
    
			i += 1
		}else{
    
    
			j -= 1
		}
	}
	return []
}

//哈希表
function getSetNum(arr, target){
    
    
	let hashMap = new Map();
	for(let i = 0; i < arr.length; i++){
    
    
		if(hashMap.has(target - arr[i])){
    
    
			return [hashMap.get(target - arr[i]), i];
		}else{
    
    
			hashMap.set(arr[i], i)
		}
	}
	return [];
		
	}

7.深浅拷贝

	// 浅拷贝
	function clone(origin, target){
    
    
		var target = target || {
    
    };
		for(var prop in origin){
    
    
			//遍历私有属性
			if(!origin.hasOwnProperty(prop)) break;
			target[prop] = origin[prop]
		}
	}
	// ES6新浅克隆方式
	let obj2 = {
    
    ...obj}
// ES6新深克隆方式
let obj3 = JSON.parse(JSON.stringify(obj))

// 1.判断是否原始值
// 2.否则判断是数组还是对象
// 3.建立相应数据或对象
function deepClone(origin){
    
    
	if (!isObject(origin)) return origin; // 非对象返回自身
	
	//不直接创建空对象,目的是保持克隆结果和之前保持相同的所属类
	// let newObj = new obj.constructor;
	
	var target = Array.isArray(origin) ? [] : {
    
    };
	for (var prop in origin){
    
    
		if(origin.hasOwnProperty(prop)){
    
    
			if (isObject(origin[prop])) {
    
    
			            target[prop] = deepClone(origin[prop]); // 注意这里
			          } else {
    
    
			            target[prop] = origin[prop];
			          }
			
			
		}
	}
	return target;
	
}
function isObject(obj) {
    
    
return typeof obj === 'object' && obj != null;
}

ES6

1.变量声明

1.let:块级作用域,不允许重复定义,无变量提升
2.const:块级,无提升,只读

2.函数用法

1.箭头函数:

/只有一个变量可省去()。只有一个return可省区{
    
    }
()=>{
    
    }

参数扩展:…args,收集剩余参数,必须是最后一个

3.数组操作

  1. …arr展开数组
  2. 解构赋值:等号左右两边结构必须一样,右边必须是个东西(定义好的数组或者对象),声明和赋值不能分开,eg:
let {
    
    a:aa,b,c} = {
    
    a:12,b:13,d:15}
//aa = 12
//b = 13
//a,c,d未定义

4.字符串

  1. 两种新方法:startWith()和endsWith()
  2. 字符串模板:
let first = '第一个';
console.log(`
<ul>
  <li>${
    
    first}</li>
  <li>second</li>
</ul>
`);

5.面向对象

  1. 类的定义
class user{
    
    
constructor(name,pass){
    
    
this.name = name;
this.pass = pass;
}
showName(){
    
    
alert(this.name);
}
}
  1. 继承
class vip extends user{
    
    
    constructor(name,pass,level){
    
    
super(name,pass);
this.level = level
}
}

6.promise

他是ES6中新增加的一个类(new Promise),目的是为了管理JS中的异步编程的,所以把他称为“Promise设计模式” new Promise 经历三个状态:padding(准备状态:初始化成功、开始执行异步的任务)、fullfilled(成功状态)、rejected(失败状态)== Promise本身是同步编程的,他可以管理异步操作的(重点),new Promise的时候,会把传递的EC函数立即执行 Promise函数天生有两个参数,resolve(当异步操作执行成功,执行resolve方法),rejected(当异步操作失败,执行reject方法) then()方法中有两个函数,第一个传递的函数是resolve,第二个传递的函数是reject ajax中false代表同步,true代表异步,如果使用异步,不等ajax彻底完成
promise: 1.是一个对象,用来传递异步操作的信息。代表着某个未来才会知道结果的时间,并未这个事件提供统一的api,供进异步处理
2.有了这个对象,就可以让异步操作以同步的操作的流程来表达出来,避免层层嵌套的回调地狱
3.promise代表一个异步状态,有三个状态pending(进行中),Resolve(以完成),Reject(失败)
4.一旦状态改变,就不会在变。任何时候都可以得到结果。从进行中变为以完成或者失败
promise.all() 里面状态都改变,那就会输出,得到一个数组
promise.race() 里面只有一个状态变为rejected或者fulfilled即输出
promise.finally()不管指定不管Promise对象最后状态如何,都会执行的操作(本质上还是then方法的特例)

7.模块化开发

模块化侧重的功能的封装,主要是针对Javascript代码,隔离、组织复制的javascript代码,将它封装成一个个具有特定功能的的模块。

模块可以通过传递参数的不同修改这个功能的的相关配置,每个模块都是一个单独的作用域,根据需要调用。

一个模块的实现可以依赖其它模块。
ES6和commonJs的异同
1.CommonJS 模块是静态引用,输出的是一个值的拷贝,ES6 模块的动态引用,输出的是值的引用,只存只读,不改变该值。
2.CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。

8.proxy代理模式

代理Proxy是一个构造函数, 它可以接受两个参数:目标对象( target ) 与句柄对象( handler ) ,返回一个代理对象Proxy,主要用于从外部控制对对象内部的访问。
Proxy的行为很简单:
将Proxy的所有内部方法转发至target 。即调用Proxy的方法就会调用target上对应的方法。那么handler又是用来干嘛的? handler的方法可以覆写任意代理的内部方法。 外界每次通过Proxy访问 target 对象的属性时,就会经过 handler 对象,因此,我们可以通过重写handler对象中的一些方法来做一些拦截的操作,包括get()、set()

猜你喜欢

转载自blog.csdn.net/qq_39748940/article/details/108255742