闭包以及应用场景
- 作用域
- 作用域决定了代码区块中变量、函数、对象和其他资源的可见性
- 全局作用域、函数作用域和块级作用域
-
let和const声明的变量不会提升到代码块顶部。 - 在同一作用域内,禁止重复声明
-
for循环中,设置循环变量那部分是一个父作用域,而循环体内部是一个单独的子作用域。由于var不能定义块级作用域,在循环体或者循环内部,使用var定义变量,在循环外部可以访问到变量
-
- 作用域链
- 父级作用域是在定义的时候就确定的,不是执行时确定
- 当前作用域不存在的变量称为自由变量,会沿着作用域链寻找
- 闭包概念
-
JavaScript语言特有的 "链式作用域" 结构,子对象会一级一级地向上寻找所有父对象的变量。所以父对象的所有变量,对子对象都是可见的,反之则不成立。 - 闭包就是能够读取其他函数内部变量的函数,就是将函数内部和函数外部连接起来的一座桥梁
-
- 用途:
- 外部函数读取函数内部的变量
- 让变量的值始终保存在内存中
- 注意点:
- 由于使用闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题。解决方法是在退出函数之前,将不使用的局部变量全部删除
- 闭包会在父函数外部,改变父函数内部变量的值。不要随便使用父函数对象调用属性的方式改变内部变量的值。
面试题目:
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:<flex-grow><flex-shrink><flex-basis>缩写
-
单页面应用和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命令用于输入其他模块提供的功能
-
-
CommonJS和ES6模块循环加载处理的区别
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组件
31、Vue源码,讲解其中熟悉的一块
32、JsBrage使用及原理
33、学习渠道,及博客等平台关注的人学到了什么
