闭包以及应用场景
- 作用域
- 作用域决定了代码区块中变量、函数、对象和其他资源的可见性
- 全局作用域、函数作用域和块级作用域
-
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指向和闭包结合的问题
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.query
URl的查询对象 - 导航
- 声明式导航:
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
命令用于输入其他模块提供的功能
-
-
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、学习渠道,及博客等平台关注的人学到了什么