JSV5
1、vue 双向绑定的原理
通过object.defineProperty()方法来劫持属性的getter和setter,当Observer监听到属性变化时,就会发布消息给订阅者Watcher,指令解析器Compile会对每个节点元素进行扫描和解析,将相关指令对应初始化成订阅者watcher,替换模板的数据或者绑定相应的函数,当订阅者watcher接收到属性变化时,就会执行对应的更新函数,从而更新视图,实现双向绑定。
2、需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。
作用主要是为了高效的更新虚拟DOM
3、使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取
4、vue-loader是基于webpack的一个的loader,解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理,核心的作用,就是提取,划重点。
5、keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
6、对vue生命周期的理解?
Vue 实例在被创建时经过一系列的初始化过程。 总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和**数据对象**data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
7、什么是vuex?以及相关属性
Vuex是通过全局注入store对象,来实现组件间的状态共享。
vuex五大核心属性:state,getters,mutations,actions,module
state:存储数据,存储状态;在根实例中注册了store 后,用 this.$store.state 来访问;对应vue里面的data;存放数据方式为响应式,vue组件从store中读取数据,如数据发生变化,组件也会对应的更新。
getters:可以认为是 store 的计算属性,它的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
mutations:包含了很多方法 用于更改 Vuex 的 store 中的状态。
actions:包含任意异步操作,通过提交 mutation 间接更变状态。
module:将 store 分割成模块,每个模块都具有state、mutation、action、getter、甚至是嵌套子模块。
视图通过点击事件,触发mutations中方法,可以更改state中的数据,一旦state数据发生更改,getters把数据反映到视图。
那么actions,可以理解处理异步,而单纯多加的一层。
既然提到了mutions actions这时候 就不得不提commit,dispatch
在vue例子中,通过click事件,触发methods中的方法。当存在异步时,而在vuex中需要dispatch来触发actions中的方法,actions中的commit可以触发mutations中的方法。同步,则直接在组件中commit触发vuex中mutations中的方法。
8、vue2.0和vue3.0区别
8.1.项目目录结构变化
移除了配置文件目录 例如config和build文件夹 配置文件从config/index.js 挪到了vue.config.js
移除了static静态文件夹,新增public文件夹,并把index.html移动到public中
8.2.数据监听
2.0基于object.defineProperty()来挟持属性的getter和setter 当给一个对象新增属性时 这个对象的所有订阅者watcher都会重新运行,而3.0是基于Proxy来按需监听 减少内存占用
9、vue组件中的data为何必须是一个function
首先需要创建一个组件构造器,然后注册组件。注册组件的本质其实就是建立一个组件构造器的引用。使用组件才是真正创建一个组件实例。所以,注册组件其实并不产生新的组件类,但会产生一个可以用来实例化的新方式。
vue组件中data值不能为对象,因为对象是引用类型,组件可能会被多个实例同时引用。如果data值为对象,将导致多个实例共享一个对象,其中一个组件改变data属性值,其它实例也会受到影响。
data为函数,通过return 返回对象的拷贝,致使每个实例都有自己独立的对象,实例之间可以互不影响的改变data属性值。
10、computed和watch的区别
如果没有computed对属性进行计算的话 属性计算可能需要用表达式的方式放在模板上执行 这样模板就显得过重 而且在页面中大量使用表达式去处理复杂的逻辑数据时 维护性不友好
10.1 computed是计算一个新的属性 并将其挂载在vue实例上 而watch是监听已经存在且已挂载在实例上的数据
10.2 computed具有缓存性,只有依赖发生变化后,第一次访问computed属性才会计算新值 而watch则是当数据发生变化便会调用执行函数
10.3 computed适用一个数据被多个数据影响,而watch适用一个数据影响多个数据;
11、为何vue数组通过下标方式无法改变视图更新
vue对数组只是observe了每个元素的值 数组的下标并没有被监听 所以通过下标是不能更新视图 而数组方法能够响应式 是因为对数组的方法进行了def操作。可能仅仅只需要改变数组的一个值而已 如果使用下标方式 会对数组的每一项进行监听 当遇到一个数组较大时 对性能是一个比较大的损耗
![](https://img.haomeiwen.com/i6112298/416b7a8c9a9166fc.png)
12、从URL输入到页面展现的过程
1.在浏览器输入url
2.浏览器获取了这个url,当然就去解析了,它先去缓存当中看看有没有,从 浏览器缓存-系统缓存-路由器缓存 当中查看,如果有从缓存当中显示页面,然后没有那就进行步骤三域名解析;
3.域名解析
由DNS服务器来完成将域名解析成对应的服务器IP地址这项工作
4.服务器处理请求 TCP三次握手连接
TCP是互联网中的传输层协议,提供可靠的链接服务,采用三次握手确认一个连接:
浏览器向服务器发送建立连接的请求。
服务器接收到浏览器发送的请求后,想浏览器发送统一连接的信号。
浏览器接受到服务器发出的同意连接的信号后,再次向服务器发出确认连接的信号。
当三次握手返程,TCP客户端和服务端成功的建立连接,就可以开始传输数据了。
5.服务器收到请求
服务器收到浏览器发送的请求信息,返回一个响应头和一个响应体。
6.页面渲染
浏览器收到服务器发送的响应头和响应体,进行客户端渲染,生成Dom树、解析css样式、js交互
13、前端性能优化
1.使用字体图标代替图片
2.html css js 图片等文件的压缩 图片懒加载
3.使用CDN
4.模块按需加载 import require
5.减少重绘 避免使用层级较深的选择器 提升渲染效率
6.减少dom操作 由于DOM操作比较耗时,且可能会造成回流,因此要避免频繁操作DOM,可以批量操作DOM,先用字符串拼接完毕,再用innerHTML更新DOM
14、webpack 构建流程
1.从配置文件或者脚本语句中得出初始化参数
2.通过参数初始化compiler对象,加载配置的插件,开始编译
3.根据配置中的 entry 找出所有的入口文件
4.从入口文件 通过递归处理 对模块进行编译 找到依赖的模块
5.模块编译完成后,得到编译后的内容和彼此的依赖关系
6.根据依赖关系,对模块进行分片组装,转换成单独文件加入到输出列表中
7.确定输出内容的路径和文件名,进行输出
webpack里的loader和plugin
loader是使webpack拥有加载和解析非js文件的能力 对模块的源代码进行转换 可以在import 或"加载"模块时预处理文件 常见的 style-loader css-loader file-loader(把url里面内容转换成我们最终需要使用的那个路径。把图片转移到我们输出的目录,并且把图片更改为另外一种名字或者做一些其他的处理
) url-loader(引入图片资源base64处理 用limit约束 小于某个大小时才base64处理)
plugin可以拓展webpack的功能 比如通过api改变输出结果 使webpack更加灵活
常见的
1. clean-webpack-plugin 配合 webpack -p命令 编译文件时清楚build或dist文件 再生成新的
2.html-webpack-plugin 简单创建html文件 用于服务器访问 同时为输出文件添加哈希值标记,避免老的不变的文件重新加载,避免新修改的文件受缓存影响
3.extract-text-webpack-plugin 主要是为了抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象。
15、内存泄漏
已经不再使用的内存未能被程序释放,叫内存泄露(memory leak)
原因
1.全局变量
全局对象就是window对象。变量在窗口关闭或重新刷新页面之前都不会被释放
2.闭包
闭包可以读取函数内部的变量,然后让这些变量始终保存在内存中。如果在使用结束后没有将局部变量清除,就可能导致内存泄露。
3.事件监听
对同一个事件重复监听,但是忘记移除,会导致内存泄露。
4.其他原因
console.log打印的对象不能被垃圾回收,可能会导致内存泄露。
setInterval也可能会导致内存泄露。
内存泄漏的识别方法
开发者工具Performance勾选 Memory 点击左侧record 访问网页 stop 查看Event Log分析结果
16、new和声明的不同
1.声明的作用域限制在定义类对象的方法中,当方法结束时,类对象也被系统释放了,(安全不会造成内存系统泄漏)。
2.new 创建的是指向类对象的指针,作用域变成了全局,当程序结束时,必须用delete删除,系统不会自动释放,(不注意可能造成内存泄漏)。
3.new的存储空间在堆上 声明的存储空间在栈上
17、vue2.0和vue3.0区别
1.项目目录结构变化
移除了配置文件目录 例如config和build文件夹 配置文件从config/index.js 挪到了vue.config.js
移除了static静态文件夹,新增public文件夹,并把index.html移动到public中
2.数据监听
2.0基于object.defineProperty()来挟持属性的getter和setter 当给一个对象新增属性时 这个对象的所有订阅者watcher都会重新运行,而3.0是基于Proxy来按需监听 减少内存占用
18、vue和react的区别
1.模板渲染方式
Vue是在模板中,通过指令来实现渲染 模板的数据都必须挂在this上进行一次中转 而React是在组件JS代码中,通过原生JS实现模板中的常见语法,比如插值,条件,循环等,都是通过JSX渲染模板
2.渲染过程不同
vue在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树 而react在状态被改变时,全部子组件都会重新渲染。不过可以通过shouldComponentUpdate这个生命周期方法进行控制
3.数据
vue是数据可变的,双向绑定,声明式的写法 而react是整体的思路是函数式的,推崇纯组件的方式,数据不可变,单向数据流
19、状态码:
400 Bad Request 语义有误,当前请求无法被服务器理解 或者 请求参数有误
403 Forbidden 服务器已经理解请求,但是拒绝执行它
404 Not Found
502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
504 Gateway Timeout
20、防抖与节流
防抖
等待足够的空闲时间后,才执行代码一次 利用定时器实现防抖
![](https://img.haomeiwen.com/i6112298/ecf4e4b0feebbbeb.png)
节流
一定时间内只执行代码一次 有时间戳和定时器两种简单的实现方式
![](https://img.haomeiwen.com/i6112298/0c7af2c25b20f4b3.png)
21、es6新特性
1.let const块级作用域 不会变量提升 var会变量提升
2.for(let item of arr){} 循环数组 可以用break来终止整个循环,或者continute来跳出当前循环,继续后面的循环;
3.反引号创建字符串模板 `your num is ${num}`
4.函数的默认参数 function sayHello2(name='dude'){
console.log(`Hello ${name}`);
}
5.拓展参数
它允许传递数组或者类数组直接做为函数的参数而不用通过apply。
var people=['Wayou','John','Sherlock'];
//sayHello函数本来接收三个单独的参数人妖,人二和人三
function sayHello(people1,people2,people3){
console.log(`Hello ${people1},${people2},${people3}`);
}
//但是我们将一个数组以拓展参数的形式传递,它能很好地映射到每个单独的参数
sayHello(...people);//输出:Hello Wayou,John,Sherlock
//而在以前,如果需要传递数组当参数,我们需要使用函数的apply方法
sayHello.apply(null,people);//输出:Hello Wayou,John,Sherlock
6.新API
"abcde".contains("cd") // true
"abc".repeat(3) // "abcabcabc"
Object.assign
22、filter() let arr2 = arr1.filter(item => item.age > 20);
filter()方法:主要用于过滤筛选数组,数组filter后,返回的结果为新的数组,不会改变原数组的值。
find() 主要用于查找数组的数据,只要查找到一条符合条件的数据,直接返回,不会再继续查找下去。
23、Array.prototype.filter的实现
Array.prototype.filter = function(cb, context){
context = context || this; //确定上下文,默认为this
var len = this.length; //数组的长度
var r = []; //最终将返回的结果数组
for(var i = 0; i < len; i++){
if(cb.call(context, this[i], i, this)){ //filter回调函数的三个参数:元素值,元素索引,原数组
r.push(this[i]);
}
}
return r;
};
24、存储
数据上的生命周期的不同
Cookie 一般由服务器生成,可设置失效时间,如果在浏览器端生成cookie,默认是关闭后失效。
localStorage 除非被永久清除,否则永久保存。
sessionStorage 仅在当前会话会有效,关闭页面或浏览器后被清除
存放数据的大小不同
Cookie 一般为4kb
localStorage 和 sessionStorage 一般为5mb
与服务器端通信不同
Cookie 每次都会携带HTTP头中,如果使用cookie保存过多数据会带来性能问题
localStorage 和 sessionStorage 仅在客户端(即浏览器)中保存,不参与和服务器的通信。
易用性
Cookie 需要程序员自己来封装,原生的cookie接口不够友好
localStorage 和 sessionStorage 原生接口可以接受,可以封装来对Object和Array有更好的支持。
25、缓存
缓存分为
1. 服务端侧如CDN缓存
其目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络"边缘",使用户可 以就近取得所需的内容,解决Internet网络拥塞状况,提高用户访问网站的响应速度。从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均等 原因,解决用户访问网站的响应速度慢的根本原因。
2. 客户端缓存就是指浏览器缓存
浏览器缓存分为强缓存和协商缓存
强缓存:浏览器在加载资源时,先根据这个资源的一些http header判断它是否命中强缓存,强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。比如某个css文件,如果浏览器在加载它所在的网页时,这个css文件的缓存配置命中了强缓存,浏览器就直接从缓存中加载这个css,连请求都不会发送到网页所在服务器;
协商缓存:当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一些http header验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回(304),但是不会返回这个资源的数据,而是告诉客户端可以直接从缓存中加载这个资源,于是浏览器就又会从自己的缓存中去加载这个资源;若未命中请求,则将资源返回客户端,并更新本地缓存数据(200)。
强缓存与协商缓存区别:强缓存不发请求到服务器,协商缓存会发请求到服务器。
设置缓存
1. html Meta标签控制缓存 CONTENT="no-cache"
2.HTTP头信息控制缓存是通过Expires(强缓存)、Cache-control(强缓存)、Last-Modified/If-Modified-Since(协商缓存)、Etag/If-None-Match(协商缓存)实现
![](https://img.haomeiwen.com/i6112298/2a46a088e049a52b.png)
![](https://img.haomeiwen.com/i6112298/4a16660468828ed6.png)
26、promise弊端
![](https://img.haomeiwen.com/i6112298/1c157f8dc12426e6.png)
27、eventloop机制
主线程从“任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为event loop(事件循环)
![](https://img.haomeiwen.com/i6112298/5a24f0053135dfd0.png)
28、$attrs概念: 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。
$listeners概念:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听。
29、Promise.then()定义之后便会立即执行 会在当前事件循环末尾中执行,而settimeout中的任务是在下一次事件循环中执行
30、https和http
http请求头有哪些字段
![](https://img.haomeiwen.com/i6112298/da56508f3fbb5b74.png)
31、简单实现promise.all
Promise.prototype.all = function (promises) {
return new Promise((resolve, reject) => {
let count = 0;
let result = new Array(promises.length);
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then((res) => {
count++;
result[i] = res;
if (count === promises.length) {
return resolve(result);
}
}).catch((err) => {
reject('err');
});
}
})
}
32、浅拷贝和深拷贝
浅拷贝如Object.assign()的时候如果数据是基本数据类型,那么就如同直接赋值那种,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么对于浅拷贝而言就只能拷贝其引用,对象的改变会反应到拷贝对象上;但是深拷贝如JSON.parse(JSON.stringify())就会拷贝多层,即使是嵌套了对象,也会都拷贝出来。
实现深拷贝
function extendDeep(parent, child) {
let i,
proxy;
proxy = JSON.stringify(parent);
proxy = JSON.parse(proxy);
child = child || {};
for(i in proxy) {
if(proxy.hasOwnProperty(i)) {
child[i] = proxy[i];
}
}
proxy = null;
return child;
}
33、链接取值
function GetQueryString(name)
{
let reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
let r = window.location.search.substr(1).match(reg);
if(r!=null)return unescape(r[2]); return null;
}
GetQueryString('open_id')
34、bind函数并手写
Function.prototype.bind
bind() 函数会创建一个新绑定函数,绑定函数与被调函数具有相同的函数体(在 ECMAScript 5 中)。调用绑定函数通常会导致执行包装函数 绑定函数也可以使用new运算符构造:这样做就好像已经构造了目标函数一样。提供的this值将被忽略,而前置参数将提供给模拟函数
bind有如下三个功能点:
改变原函数的 this 指向,即绑定上下文,返回原函数的拷贝
当绑定函数被调用时,bind的额外参数将置于实参之前传递给被绑定的方法。
注意,一个绑定函数也能使用new操作符创建对象,这种行为就像把原函数当成构造器,thisArg 参数无效。也就是 new 操作符修改 this 指向的优先级更高。
Function.prototype.bindFn = function bind(thisArg) {
if (typeof this !== 'function') {
throw new TypeError(this + 'must be a function');
}
// 存储函数本身
var self = this;
// 去除thisArg的其他参数 转成数组
var args = [].slice.call(arguments, 1);
return function () {
// bind返回的函数 的参数转成数组
var boundArgs = [].slice.call(arguments);
// apply修改this指向,把两个函数的参数合并传给self函数,并执行self函数,返回执行结果
return self.apply(thisArg, args.concat(boundArgs));
}
}
35、获取数组最大值的下标
Math.max(...arr)
function getMaxIndex(arr) {
let max = arr[0];
let index = 0;
for (let i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
index = i;
}
}
return index;
}
36、数组去重
1.ES6的set函数 return [...new Set(arr)]
2.把数组的值转化成对象的属性 function unique(arr) {
let json = {};
let newArr = [];
for (let i = 0; i < arr.length; i++) {
if (!json[arr[i]]) {
newArr.push(arr[i]);
json[arr[i]] = 1;
}
else {
json[arr[i]]++;
}
}
return newArr;
}
37、冒泡排序
function bubbleSort(arr){
for(var i = 0;i<arr.length;i++){
for(var j=arr.length-1;j>i;j--){
if(arr[j]<arr[j-1]){
var temp = arr[j]
arr[j] = arr[j-1]
arr[j-1] = temp
}
}
}
return arr
}
38、快排
var quickSort = function(arr) {
if (arr.length <= 1) { return arr; }
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++){
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat([pivot], quickSort(right));
};
39、求dom树的最大深度
const getDepth = node => {
if (!node.children || node.children.length === 0) {
return 1
}
const maxChildrenDepth = [...node.children].map(v => getDepth(v))
return 1 + Math.max(...maxChildrenDepth)
}
console.log(getDepth(document.documentElement));
怎么去设计一个组件封装?
1、组件封装的目的是为了重用,提高开发效率和代码质量
2、低耦合,单一职责,可复用性,可维护性
3、前端组件化设计思路
常见web安全及防护原理
sql注入原理
就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
总的来说有以下几点:
1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。
XSS原理及防范
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者javascript代码。比如:攻击者在论坛中放一个看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,
当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
XSS防范方法
首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。
其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。
尽量采用POST 而非GET 提交表单
Javascript垃圾回收方法
标记清除(mark and sweep)
这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。
垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了
引用计数(reference counting)
在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的,
也就是说只要涉及BOM及DOM就会出现循环引用问题。
js原型链以及特点
原型链继承和类继承。然后类继承只继承了实例属性,没有原型属性。原型链继承可以继承所有。然后用apply和call怎么继承原型链上的共享属性?通过空函数传值。新建一个空函数C。C实例化后C的实例属性就是空,然后用B的apply/call去继承C,相当于继承了C的实例属性。
304与200读取缓存的区别
网友评论