前端总结
闭包以及应用场景
作用域
作用域决定了代码区块中变量、函数、对象和其他资源的可见性
全局作用域、函数作用域和块级作用域
let 和 const 声明的变量不会提升到代码块顶部。
在同一作用域内,禁止重复声明
for 循环中,设置循环变量那部分是一个父作用域,而循环体内部是一个单独的子作用域。由于var不能定义块级作用域,在循环体或者循环内部,使用 var 定义变量,在循环外部可以访问到变量
作用域链
父级作用域是在定义的时候就确定的,不是执行时确定
当前作用域不存在的变量称为自由变量,会沿着作用域链寻找
闭包概念
JavaScript 语言特有的 "链式作用域" 结构,子对象会一级一级地向上寻找所有父对象的变量。所以父对象的所有变量,对子对象都是可见的,反之则不成立。
闭包就是能够读取其他函数内部变量的函数,就是将函数内部和函数外部连接起来的一座桥梁
用途:
外部函数读取函数内部的变量
让变量的值始终保存在内存中
注意点:
由于使用闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题。解决方法是在退出函数之前,将不使用的局部变量全部删除
闭包会在父函数外部,改变父函数内部变量的值。不要随便使用父函数对象调用属性的方式改变内部变量的值。
面试题目:
Excuse me?这个前端面试在搞事!
function a() {
for (var i = 0; i < 8; i++) {
setTimeout(function() {
console.log(i, 1000 * i)
},
1000 * i)
}
}
// 结果:每隔一秒打印 8,8000,共打印8次
function b() {
for (var i = 0; i < 8; i++) { (function() {
setTimeout(function() {
console.log(i, 1000 * i)
},
1000 * i)
})();
}
}
// 结果:每隔一秒打印 8,8000,共打印8次
function c() {
for (var i = 0; i < 8; i++) {
setTimeout((function(i) {
console.log(i, 1000 * i)
})(i), 1000 * i)
}
}
// 结果:没有延迟,一次打印全部。自执行函数,传入变量参数。闭包。
> 0, 0
> 1, 1000
> 2, 2000
> 3, 3000
> 4, 4000
> 5, 5000
> 6, 6000
> 7, 7000
function d(){
for (let i = 0; i < 8; i++) {
setTimeout(function() {
console.log(i, 1000 * i)
},
1000 * i)
}
}
// 结果:每隔一秒,打印一次结果。使用块级作用域。闭包。
> 0, 0
> 1, 1000
> 2, 2000
> 3, 3000
> 4, 4000
> 5, 5000
> 6, 6000
> 7, 7000
function f() {
for (var i = 0; i < 8; i++) {
(function(i) {
setTimeout(function() {
console.log(i, 1000 * i);
},
i * 1000);
})(i);
}
}
// 结果:每隔一秒,打印一次结果。闭包。
> 0, 0
> 1, 1000
> 2, 2000
> 3, 3000
> 4, 4000
> 5, 5000
> 6, 6000
> 7, 7000
function test() {
for (var i = 0; i < 8; i++) {
(function() {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
}
// 结果:每个一秒,打印一次结果。结果都是8,8000
this的指向
this 要在执行时才能确认值,定义时无法确认
箭头函数this指向: 箭头函数没有自己的this,看其外层是否有函数
如果有,外层函数的this就是内部箭头函数的this
如果没有,则this就是window
this指向
面试题目
this指向和闭包结合的问题
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
console.log(object.getNameFunc()());
// 结果: The Window
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
console.log(object.getNameFunc()());
// 结果:My Object
Vue中的什么情况下this指向Vue实例
所有的生命周期钩子自动绑定 this 上下文到实例中,因此你可以访问数据,对属性和方法进行运算。
判断浏览器和系统的信息
使用 navigator 对象
输入网址到加载完页面的过程
加载一个资源的过程
浏览器根据 DNS 服务器取得域名的 IP 地址
向这个 IP 的机器发送http 请求
服务器收到、处理并返回 http 请求
浏览器得到返回内容
浏览器渲染页面的过程
根据 HTML 机构生成 DOM Tree;根据 CSS 生成 CSSOM
将 DOM 和 CSSOM 整合形成 RenderTree,根据 RenderTree 开始渲染和展示
遇到 script 时,会执行并阻塞渲染
Vue的渲染过程
new Vue,执行初始化
挂载 $mount 方法,通过自定义 Render 方法、template模板、el 等生成 Render 函数
Vue会遍历传入实例的data选项,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把 "接触" 过的数据属性记录为依赖。通过 Watcher 监听数据变化。响应式开始监听。
当依赖项的 setter 触发时,会通知watcher,Render 函数执行,生成 VNode 对象
通过 patch 方法,对比新旧 VNode 对象,通过 DOM Diff 算法,添加、删除、修改真正的 DOM 元素
Cookie、Session、LocalStorage、SessionStorage的区别
cookie 和 session 都是用来跟踪浏览器用户身份的会话方式。cookie存储在浏览器端,session 存储在服务器端
功能:cookie 本身用于客户端和服务器端通信,它有本地存储的功能,于是也被当做存储使用。LocalStorage 是 HTML5 中专门为存储设计
容量:cookie 存储量太小,最大容量 4kB; LocalStorage 最大容量为 5M
HTTP请求:所有的http请求,都会携带cookie ,会影响获取资源的效率
同源策略和跨域解决方案
同源策略:
目的是为了保证用户信息的安全,防止恶意的网站窃取数据。
"同源" 指的是"三个相同":协议相同、域名相同、端口相同。
非同源,浏览器端会限制三种行为:
Cookie、LocalStorage、IndexDB 无法读取
DOM 无法获得
Ajax 请求不能发送
跨域:协议、域名、端口,有一个不同就算跨域
解决方案:
JSONP
利用 script 标签没有跨域限制的漏洞,动态插入 script 标签,实现跨域获取数据,JSONP 请求一定需要对方的服务器做支持才可以
JSONP 仅支持 get 方法,具有局限性。不安全,可能会遭受 XSS 攻击
CORS 跨域方式,服务器端设置HTTP消息头。需要浏览器和服务器同时支持, IE 浏览器不能低于 IE10。整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。浏览器一旦发现 Ajax 请求跨源,就会自动添加一下附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
nginx 反向代理实现跨域;node 请求转发实现跨域
正则表达式
普通文本字符
元字符
匹配一个字符:匹配行的起始 ^;匹配行的结束 $;匹配任意一个字符 .;匹配若干字符之一的字符组 [...];匹配一个未列出的字符的排除型字符组 [^...]
多选结构:每个多选结构自身都可以是完整的正则表达式,都可以匹配任意长度的文本;匹配任意子表达式 |;| 配合 ()可以限制子表达式的界限
量词:作用与之前紧邻的元素,包括单个元素或使用 () 限制起来 的元素
? :0次或者1次
+:1次或者多次
*:任意多次或者0次
{a, b}:[a, b] 次
转义: \,匹配的某个字符就是元字符,则需要进行转义
px、em、rem
px 像素是相对长度单位,相对于显示器屏幕分辨率而言
em 是相对长度单位,相对于当前对象内文本的字体尺寸。如果当前未对文本的字体尺寸进行设置,则相对于浏览器的默认字体尺寸
rem :root em 是 CSS3 中新增的一个相对单位。相对的是HTML根元素
CSS3新增的特性
选择器:伪类选择器 :first-child、:last-child、:nth-child(n)等
动画效果:Transitions、Transforms、Animation
边框:圆角边框、边框阴影、边框图片
颜色:RGBA、渐变颜色
文本:文本溢出、文本阴影
Vue项目部署
使用webpack构建工具,npm script脚本将项目打包成资源文件,部署到 nginx 服务器上
MVVM
Vue 是 MVVM 架构,ViewModel 是 Model 和 View 之间的一个桥,相当于一个连接器,内部实现事件监听和双向数据绑定
Vue的生命周期
所有的声明周期钩子自动绑定 this 上下文到实例中,因此你可以访问数据,对属性和方法进行运算
beforeCreate:在实例初始化之后,数据观测(data observer)和event/watcher事件配置之前被调用
created:在实例创建完成后被立即调用。已完成:数据观测、属性和方法的运算、watch/event事件回调。挂载阶段还没开始,$el属性目前尚不可用
beforeMount:在挂载开始之前被调用:相关的reader函数首次被调用
mounted:实例被挂载后调用,这时的el 被新创建的 vm.$el替换了。mounted不会保证所有的子组件也都一起被挂载。确保整个视图都渲染完毕,可以在mounted内部使用vm.$nextTick
beforeUpdate:数据更新时调用,发生在虚拟DOM打补丁之前。
updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。updated 不会保证所有的子组件也都一起被重绘。相关操作放在updated钩子中调用vm.$nextTick的回调函数中。也可以用计算属性和watcher代替
beforeDestory:实例销毁之前调用。在这一步,实例仍然完全可用
destoryed:实例销毁后调用。该钩子被调用后,对应Vue实例的所有指令都被解绑,所有的事件监听被移除,所有的子实例也都被销毁
activated:被 keep-alive 缓存的组件激活时调用
deactivated:被 keep-alive 缓存的组件停用时调用
Vue中组件通信:父组件 -> 子组件、子组件 -> 父组件、兄弟组件
父组件通过Prop形式传递值给子组件。单向传递
子组件通过emit事件传递数据给父组件
兄弟组件通信使用vuex
介绍Vuex、使用场景、弊端和如何解决
Vuex 是状态管理模式,采用集中式存储管理应用的所有组件状态,每个应用仅仅包含一个 store 实例
state:驱动应用的数据源
Getter :看做是 store 的计算属性,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖发生了改变才会被重新计算。 Getter 接受 state 作为其第一个参数。接受getters作为第二个参数。可以让getter返回一个函数,来实现给getter传参,此种形式,不会缓存结果
Mutation:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,Mutation必须是同步函数 。每个mutation都有一个字符串的事件类型type和一个回调函数handler。handler就是我们实际进行状态更改的地方,第一个参数是 state,第二个参数是 payload 载荷,载荷应该是一个对象,不能直接调用handler,提交突变commit,实现更改数据源。
Mutation 需遵守 Vue 的响应规则
最好提前在你的store中初始化好所有的所需属性
需要在对象上添加新属性时:
使用 Vue.set(obj, 'newProp', 123)
以新对象替换老对象
对象扩展运算符:state.obj = {...state.obj, newProp:123}
使用Object.assign()方法
Action:Action提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步状态。Action 函数第一个与 store 实例具有相同方法和属性的context对象,可以使用参数结构来简化代码;可以附加第二个参数payload。
可以在组件中使用 this.$store.dispatch('xxx')分发action
使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用,需要现在根节点注入 store
封装一个组件的思路
组件要实现什么功能
组件需要由外部提供哪些数据
组件需要向外部反馈什么信息
盒模型
margin外边距、padding内边距、border边框、content内容
标准盒模型:width = content
IE8及以下 非标准盒模型 :windth = content + padding + border
box-sizing:border-box;
CSS定位
static 文档常规流
relative 相对文档流原位置进行定位,原位置留下空白
absolute 移出正常文档流,不为元素预留空间,相对于最近的一个非 static 定位祖先元素定位
fixed 移出正常文档流,不为元素预留空间,相对于屏幕视口 viewport 定位
浮动布局、清除浮动
浮动会脱离正常的文档布局流,形成环绕的效果
清除浮动:
clear: both
浮动的父元素添加:overflow:hidden; zoom:1;
使用 :after 伪类,浮动的父元素添加:
.content:after{
content:".";
display:block;
height:0;
visibility:hidden;
clear:both;
}
.content{zoom:1;}
for...in、for...of、foreach、map有哪些区别
for...in:遍历所有可枚举属性,包括原型上的属性和方法,遍历拿到索引或者对象属性,return false 可以结束遍历
for...of:遍历,但不包含原型链上的,遍历拿到值,return false 可以结束遍历
forEach:接受一个回调函数,进行遍历,不能主动结束遍历
map:遍历,返回一个新数组
Vue和JQuery有哪些区别
Vue是MVVM架构,数据 和 视图的分离,解耦
以 数据 驱动视图,只关心数据变化, DOM 操作被封装
ES6
块级作用域 let const
模板字符串
函数参数的默认值、rest语法
箭头函数
对象中,键值重名,可简写
解构赋值
扩展运算符
模块化:import 导入模块;export导出模块
异步解决方案:Promise、Async/await
class、extends、super
class语法糖
class Demo{};
var demo = new Demo()
// 结果
typeof Demo // "function"
Demo === Demo.prototype.constructor // true
demo.__proto__ === Demo.prototype // true
class 是普通构造函数的语法糖,符合JS原型和原型链的规则
extends 实现原型链更方便
深拷贝、浅拷贝以及算法,什么时候用
浅拷贝与深拷贝
深拷贝和浅拷贝是针对Object和Array这样的引用数据类型
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存
深拷贝会另外创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改到原对象
浅拷贝的实现方式
Object.assign():可以把任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象,拷贝的是对象的属性的引用,而不是对象本身。当object是一层的时候,是深拷贝
Array.prototype.concat() 函数 和 Array.prototype.slice() 函数,两个函数都不会修改原数组,只会返回一个浅复制来了原数组中的元素的一个新数组
深拷贝的实现方式
JSON.parse(JSON.stringify()):用 JSON.stringify 将对象转成 JSON 字符串,再用 JSON.parse() 把字符串解析成对象,对象会开辟新的栈,实现深拷贝。不能处理函数。
手写递归遍历实现:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝
函数库:lodash中个的_.cloneDeep()
引用数据类型和基本类型数据
基本数据类型:Undefined、Null、Boolean、Number、String、Symbol表示独一无二的值
引用类型,统称为Object对象:对象、数组、函数
区别:
存储位置:基本数据类型值存储在栈中;引用类型指针存储在栈中,对象内容存储在堆中
参数传递:基本数据类型传递值的副本,值的改变,互不影响;引用数据类型传递指针,修改对象,相互影响
判断js类型,优缺点
typeof:null、数组和对象的typeof均是object
instanceof:null instanceof Null // 报错; undefined instanceof undefined // 报错
Object.prototype.toString.call():最准确的判断方式,Object.prototype.toString.call('') // [object String]
Promise对象
基本含义
Promise 就是一个容器,里面保存着某个未来才会结束的事件的结果
对象的状态不受外界影响。pending进行中、fulfillled已成功、rejected已失败,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。一旦状态改变,就不会再变,任何时候都可以得到这个结果,pending --> fulfilled、pending --> rejected
基本用法
promise对象是一个构造函数,用来生成 promise 实例。接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。这两个函数有 JavaScript 引擎提供,不用自己部署。
resolve 函数的作用是将pending状态变为resolved状态,并将异步操作的结果,作为参数传递出去。如果返回的是另一个Promise,则当前Promise状态被返回的Promise托管
reject 函数的作用是将pending状态变为rejected状态,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去,通常是一个 Error 实例。Promise对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止;如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码
finally方法:不管Promise最后状态如何,都会执行的操作
async/await
async 函数返回一个Promise对象
当异步函数执行的时候,一旦遇到await命令,就会等待返回结果,得到await后面的异步操作完成,再接着执行函数体后面的语句
async 函数内部抛出错误,会导致返回的Promise对象变为reject状态,抛出的错误对象会被catch回调函数接收到
await 命令后面是一个Promise对象,返回该对象的结果
任何一个await语句后面的Promise对象变为reject状态,那么整个async函数都会中断执行。如果希望前一个异步的状态不会影响后面的异步操作,可以使用try...catch块或者catch方法
多个await命令后面的异步操作,如果不存在继发关系,最好让他们同时触发
await Promise.all([...])
const ap = p1(); const bp = p2(); const a = await ap; const b = await bp;
扩展运算符,mapActions使用扩展运算符
扩展运算符是三个点(...),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列
Vuex中的mapActions是一个函数,返回的是一个可遍历的Mapper对象,混入到局部的methods中
Vue Router 传递参数
在根实例中注入路由,在任何组件内可通过this.$router访问路由器,通过 this.$route 访问当前路由,this.$route.params路径参数对象,this.$route.queryURl的查询对象
导航
声明式导航:router-link组件,传入 to 属性指定链接
编程式导航:this.$router,push 向 history 栈添加一个新纪录;replace 替换当前的 history 记录;go 前进或后退几步
路由出口:router-view 组件
导航守卫
全局守卫:
全局前置守卫:router.beforeEach((to, from, next) => {...}),当一个导航触发时调用
全局解析守卫 :router.beforeResolve,在导航被确认之前,在所有组件内守卫和异步路由组件被解析之后,被调用
全局后置钩子:router.afterEach,路由被确认,没有next回调
路由独享的守卫:可以在路由配置上直接定义 beforeEnter 守卫
组件内的守卫 :
beforeRouteEnter:不能获取实例的this。但是可以通过传一个回调给 next 来访问组件实例
beforeRouteUpdate:可以访问组件实例this,路由改变但组件被复用是调用
beforeRouteLeave:可以访问组件实例this,导航离开该组件的对应路由时调用。
导航解析流程
1、导航被触发
2、在失活的组件里调用离开守卫beforeRouteLeave
3、调用全局的beforeEach守卫
4、在重用的组件里调用 beforeRouteUpdate守卫
5、在路由配置里调用 beforeEnter 守卫
6、解析异步路由
7、在被激活的组件里调用 beforeRouteEnter 守卫
8、调用全局的 beforeResolve 守卫
9、导航被确认
10、调用全局的 afterEach 守卫
11、触发DOM更新
12、用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数
路由组件传参
导航可以添加params和 query 参数
使用 props 方式解耦,对于包含命名视图的路由,需要为每个路由添加 props 选项
路由元信息:meta对象,一个路由匹配到的所有路由记录会暴露为$route对象的 $route.matched数组,遍历数组来实现检查路由定义的 meta 字段
flex布局
采用Flex布局的元素,成为 Flex 容器;容器默认存在两根轴,水平的主轴和垂直的交叉轴,项目默认沿主轴排列
容器的属性:
flex-direction:主轴的方向即项目的排列方向
flex-wrap:项目如何换行
justify-content:项目在主轴上的对齐方式
align-items:项目在交叉轴上如何对齐
项目的属性:
order:定义项目的排列顺序。数值越小,排列越靠前,默认为0
flex-grow:定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大;
flex-shrink:定义项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
flex:缩写
单页面应用和SEO
SSR
Nuxt.js
静态页面等方式
原型规则
所有引用类型(数组、对象、函数)都具有对象特性,即可以自由扩展属性
所有引用类型都有一个隐式原型(__proto__)属性,属性值是一个普通的对象
所有的函数都有一个显式原型(prototype)属性,属性值也是一个普通的对象
所有的引用类型__proto__属性值是指向它的构造函数的prototype属性值。构造函数的prototype都有一个construtor属性,属性值等于构造函数本身
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么就会去它的__proto__(构造函数的prototype)中寻找
JS模块化
AMD规范:流行的require.js库
自动引入全局 define定义 函数
自动引入全局require引入函数,require只能引入define定义的函数
依赖JS会异步加载
CommonJS规范:NodeJS模块化规范
module.exports 输出模块
require 加载模块
同步加载
ES6模块化
export 命令用于规定模块对的对外结构
export default 命令为模块指定默认输出
import 命令用于输入其他模块提供的功能
36、项目性能优化
37、Android和IOS端前端兼容性问题
38、项目难点
39、Vue适合做pc端网站么?优缺点
41、前后端分离如何渲染页面及数据
7、XSS攻击
9、WebGL
14、排序算法
43、有没有关注最新的es特性
44、最近读的一本书,有什么感想
45、webpack的深入配置
46、Vue的双向数据绑定和微信小程序的双向数据绑定有什么异同
24、原生JS
28、高阶函数
29、map函数
30、封装一个V-model组件
发表评论 (审核通过后显示评论):