闭包
闭包是函数嵌套函数形成的作用域链。
定义:函数A内部有一个函数B,函数B可以访问函数A中的变量,那么函数B 就是闭包。
自由变量的规则是在函数定义的时候去查找,而不是在执行时候去查找。
作用:
1.实现js模块化,防止全局污染
2.封装私有变量
3.一个变量长期驻扎在内存中
实例:
for(var i=0;i<3;i++){
(function(i){
setTimeout(function(){
console.log(i)
},1000)
})(i)
}
深拷贝和浅拷贝
浅拷贝: 只拷贝对象的第一层数据。基本类型拷贝的是基本类型的值;引用类型拷贝的是内存地址,当引用值改变时,另一个对象也会受影响。
深拷贝: 将一个对象从内存中完整的拷贝一份,从堆内存中开辟一个新的区域。修改对象不会改变原始对象。
- 递归
- JSON.parse(JSON.stringify())
- lodash工具库
- jQuery中extend( )
var array = [1,2,3,4]; var newArray = $.extend(true,[],array);
function deepClone(obj){
// 对特殊情况的处理
if(obj == null) return null;
if(obj instanceof Date) return new Date(obj);
if(obj instanceof RegExp) return new RegExp(obj);
var target = {};
for(var key in obj){
if(Object.prototype.hasOwnProperty.call(obj,key)){
if(typeof obj[key] === 'object'){
target[key] = deepClone(obj[key])
}else{
target[key] = obj[key];
}
}
}
return target;
}
for...in 和for...of有什么区别?
- for..in 遍历的是对象的key ,for..of 遍历的是对象的value值
- for..in 不仅遍历键名,还会遍历手动添加的键,甚至原型链上的键; for..of 不可以!!
- for..in 适合遍历对象; for..of 适合遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象
节流和防抖
防抖:任务频繁触发的情况下,事件响应函数在一段时间后才执行。如果在这段时间内再次调用,则重新计算执行时间。
应用场景:
resize/scroll 触发统计事件;
文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)
<button id="debounce">点我防抖!</button>
节流:n秒内只会执行一次任务。
应用场景:
DOM 元素的拖拽功能实现(mousemove)
射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
计算鼠标移动的距离(mousemove)
Canvas 模拟画板功能(mousemove)
搜索联想(keyup)
监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
<button id="throttle">点我节流!</button>
普通函数和箭头函数的区别
-
箭头函数会捕获其所在上下文的this,作为自己的this值。
-
箭头函数不绑定arguments,取而代之的是res参数。
-
箭头函数不能作为构造函数,不能使用new
-
箭头函数没有原型属性
-
箭头函数不能当做Generator函数,不能使用yield关键字
new的执行过程
1.创建一个空对象
2.这个新对象被执行原型连接
3.绑定this,并执行函数中的方法
4.返回这个对象(如果函数没有返回其他对象,那么this指向这个新对象,否则this指向构造函数中返回的对象)
function new(func){
let target={};
target.__proto__ = func.prototype;
let res = func.call(target);
if(res && target(res) =="object" || typeof(res) == "function"){
return res;
}
retrun target;
}
对原型的理解
原型:在Js中,每当定义一个函数时有一个prototype属性,这个属性指向函数的原型对象。使用原型对象的好处是所有对象的实例共享了包含的属性和方法。
什么是原型链,原型链解决了什么问题
原型链主要解决了继承的问题。 每个对象都有一个__proto__属性,这个属性指向了原型对象(prototype),并继承原型中的属性和方法。原型对象也可能拥有原型,一层一层向上最终指向null(Object.prototype.proto 指向的是null),这种关系被称为原型链。
原型,构造函数,实例的关系
构造函数.prototype === 原型;
原型.constructor === 构造函数;
实例对象.proto === 原型;
实例对象.constructor === 构造函数;
prototype 和 proto 的区别
prototype是构造函数的属性。 proto 是每个实例对象的属性。 实例的__proto__ 和构造函数的prototype 都指向原型。
call、apply、bind的区别
call 和 apply 都是改变this的指向,传参方式不同。如果不传入参数或者参数为null,默认指向为 window / global。都会直接被调用。
fn.call(obj, 1, 2, 3);
fn.apply(obj, [1, 2, 3]);
bind会创建一个新函数,这个新函数被调用时第一个参数作为它运行时候的this。
fn.bind(obj, 1, 2, 3);
模拟Object.create
Object.create()方法创建了一个新对象,使用现有的对象来提供新创建的对象的__proto__
function create(proto){
function F(){}
F.prototype = proto;
return new F()
}
对This的理解
this: 函数的上下文,可以通过call,apply,bind改变this的指向。
-
匿名函数或者直接调用时,this指向window,nodejs指向global。
-
谁调用了它,this就指向谁。
-
通过new关键字创建的函数对象,this指向被new出来的对象。
-
被当做事件处理函数,this是触发的DOM元素。
-
定时器调用函数时,this是window。
JS的继承
- 原型链继承:
核心:新实例的原型等于父类的实例
缺点:原型对象的属性和方法都会是共享的,无法实现多个继承
- 构造函数式继承
核心:用.call()和.apply()将父类构造函数引入子类函数
缺点:方法都在构造函数中定义,只能继承父类的实例属性和方法,不能继承原型的属性和方法,无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
- 组合式继承(原型链 + 构造函数式继承)
核心:即通过在原型上定义方法来实现函数复用,又保证每个实例有自己的属性。
缺点:调用了两次父类构造函数,一次是在创建子类原型时候,另一次在子类构造函数内部。 优点:可以向父类传参;每个实例都有自己的属性;实现了函数复用。
*原型式继承 核心:通过Object.create() 实现原型继承 缺点:原型对象的属性和方法都会是共享的,无法实现多个继承
*寄生式继承
核心:创建一个仅用于封装继承过程的函数,该函数在内部已某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。
缺点:原型对象的属性和方法都会是共享的
*寄生式组合式继承
核心:创建父类型原型的一个副本 -> 为创建的副本添加constructor 属性 -> 将新建的对象赋值给子类型的原型 优点:只调用一次父类构造函数,效率高,避免在SuberType.prototype上面创建不必要的、多余的属性,与其同时,原型链还能保持不变。
cookie、sessionStorage、localStorage的区别
相同点:客户端的存储
不同点:
1.存储大小
cookie数据大小不能超过4k
sessionStroge 和localStorage可以达到5M
2.有效时间
localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除。
sessionStorage数据在当前浏览器窗口关闭后自动删除。
cookie设置的cookie过期时间之前一直有效,即使浏览器窗口关闭。
3.数据与服务器之间的交互方式
cookie的数据会自动的传递到服务器,服务器端也可以写写cookie到客户端。
sessionStorage和loacalStroage不会自动把数据发给服务器,仅保存在本地。
export和export default的区别?
- export 可以导出多个方法和变量
export xxx
import {xxx} from './'
- export default 只导出当前模块
export default xxx
import xxx from './'
介绍promise
promise是异步编程的一种解决方案,有效解决了地狱回调的问题。 promise对象代表一个异步操作,有三种状态:pending(进行中) , resolve(已成功), rejected(已失败)。 状态只有两种可能:pending-->resolve 和 pending-->rejected。
promise构造函数是同步执行的,then是异步执行的。
优点:
1.可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
2.一旦状态改变,就不会再变,任何时候都可以得到这个结果。
缺点:
1.无法取消promise
2.当处于pending状态时候,无法得知目前进展到了哪一阶段
堆和栈的区别
基本类型:string,number,boolean,undefined,null,Symbol (保存在栈内存,通过按值来访问)
引用类型:object,Function,Array,Date,RegExp (指针存放在栈中,指针指向了堆中实体)
栈:
在它的当前执行环境结束后就会被销毁被垃圾回收机制收回。
堆:
由于不确定其他的地方是不是还在引用,所以堆内存中的变量只有在所有对他的引用都结束后才会被收回。
闭包是保存在堆内存中的。
宏任务和微任务
宏任务:
JS 内部(任务队列里)的任务,setTimeout,setInterval,script,requestAnimationFrame,setImmediate, I/O, UI rendering
微任务:
promise process.nextTick
当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件, 然后再去宏任务队列中取出一个事件。同一次事件循环中, 微任务永远在宏任务之前执行。
Eventloop
主线程从任务对列中读取执行事件,这个过程是循环不断地,此机制成为EventLoop。
机制:
-
首先执行同步代码
-
执行完同步代码后,执行栈为空,查询是否有异步代码需要执行
-
执行所有的微任务
-
执行完微任务后,如有 必要会渲染页面
-
然后开启下一轮eventloop,执行宏任务的异步代码
同步和异步
同步:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
异步:不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程。
跨域
跨域是指浏览器不能执行其他网站的脚本,它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。 所谓同源策略,是指地址的协议、域名、端口号完全一样。
-
jsonp跨域,通过script标签引入一个js的文件,此文件返回一个js的函数调用
只能实现get请求 -
document.domain + iframe 跨域,要求主域相同
修改document.domain的方法只适用于不同子域的框架间的交互 -
window.name + iframe 跨域
-
location.hash + iframe 跨域
-
通过HTML5的postMessage方法跨域
-
跨域资源共享 CORS
服务器实现了CORS接口,就可以跨源通信,CORS支持所有类型的HTTP请求
浏览器解析 URL
- 输入url地址
- 对URL地址进行DNS解析
- 建立TCP连接(三次握手)
- 浏览器发起HTTP请求报文
- 服务器返回HTTP响应报文
- 关闭TCP连接 (四次挥手)
- 浏览器解析文档资源并渲染在页面
浏览器解析文档资源并渲染页面的流程
- HTML解析出DOM Tree
- CSS 解析出CSSDOM Tree
- JavaScript由 JS引擎处理
- DOM Tree 和 CSSDOM Tree结合生成了rending tree
- 通过rending tree得到节点的样式,然后计算他们页面的大小和位置,最后把节点绘制到页面
重绘与回流
重绘:
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
回流:
当页面中元素样式的尺寸,结构或者某些属性发生改变时,浏览器需要重新渲染时叫回流。
页面的首次渲染
浏览器窗口大小发生改变
元素尺寸或位置发生改变
添加或删除可见的DOM元素
激活CSS伪类(例如::hover)
查询某些属性或调用某些方法:client,offset,scroll
事件流
事件流分为三个阶段:捕获阶段、目标阶段、冒泡阶段
事件委托
事件委托是指将事件绑定目标元素的父元素上,利用冒泡机制触发该事件。
优点:
-
可以减少事件注册,节省大量内存占用
-
可以将事件应用于动态添加的子元素上
Vue生命周期
beforeCreat实例创建之前:data,$el,methods中方法没有初始化都是undefined。场景:添加loading
created实例创建完成:data,methods中方法初始化完成,$el没有初始化。场景:移除loading效果,进行ajax请求
beforeMounted挂载之前:data已存在。虚拟dom创建完成,即将开始渲染。
mounted挂载完毕:真实dom挂载完毕,完成双向数据绑定,可以访问dom,使用$ref属性对dom进行操作。
beforeUpdate更新之前:界面没有更新,数据发生更新。在当前阶段进行更改数据,不会造成重渲染。
updated更新完成之后:组件DOM已完成更新,避免在此阶段更改数据,可能会导致无限循环更新。
beforeDestroy销毁之前:清除定时器之类工作。
destroyed销毁之后:只剩下dom空壳,组件已被拆解,数据绑定被卸除,监听被移出,子实例也被销毁。
v-show 与 v-if 有什么区别?
v-if 会确保在切换过程中条件块内的事件监听器和子组件适当的被销毁和重建。动态的向DOM树内添加或者删除DOM元素 v-show 无论初始条件怎么,元素都会被渲染,只是简单的基于css的display属性进行切换。
Vue的通信
父-->子: 父组件可以通过Prop传递数据到子组件中
父组件:
<cart :title="titleVar"></cart>
data(){
titleVar:'aaa'
}
子组件:
<h2>{{title}}</h2>
props:['title']
子-->父:子组件中$emit数据,在父组件中通过事件@事件名接收值
父组件:
引入子组件,调用子组件。
<k-button @childEv="fclick"></k-button>
methods:{
fclick(obj){
console.log(obj); //{msg:'hellow'}
}
}
子组件:
<button @click="handleClick">button</button>
methods:{
handleClick(){
this.$emit('childEv',{ msg:'hellow' })
}
}
EventBus:适用于 父子、隔代、兄弟组件通信,通过一个空的vue实例作为中央事件总线,用它来触发事件和监听事件。
为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
组件是用来复用的,每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响。
// data
data() {
return {
message: "子组件",
childName:this.name
}
}
如果是对象,实例会共享同样的data对象,这样没办法做到组件的复用。
// new Vue
new Vue({
el: '#app',
data:{
message:"子组件"
},
template: '<App/>',
components: {App}
})
v-model 的原理?
1.v-bind绑定value的值 2.v-on绑定input事件监听到函数中,函数会获取最新的值 赋值到 绑定的属性中。
v-modle指令在表单 input、textarea、select 等元素上创建双向数据绑定,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
-
text 和 textarea 元素使用 value 属性和 input 事件;
-
checkbox 和 radio 使用 checked 属性和 change 事件;
-
select 字段将 value 作为 prop 并将 change 作为事件。
<input v-model='something'>
相当于
<input v-bind:value="something" v-on:input="something = $event.target.value">
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件:
<ModelChild v-model="message"></ModelChild>
子组件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小红')
},
},
Vuex 的理解
vuex是Vue的状态的管理模式,它采用集中式存储 管理应用的所有组件的状态,更改状态的唯一方法是提交mutation。
他主要解决:
1.多个组件依赖同一个状态时
2.来自不同组件的行为需要变更同一状态。
核心属性:
state: 定义应用状态的数据结构,可以在这里设置默认的初始状态。
getter: 实现state数据的映射,变化时自动计算出一个新的结果。 放在computed中;
mutation: 修改数据,可以直接改变状态;要同步操作。提交方式:this.$stroe.commit('SET_NUMBER',data)。第一个参数是state; 放在methods中
action: 提交的是mutation,而不直接改变状态。可以包含异步操作。提交方式:this.$stroe.dispath('ACTION_NAME',data)。第一个参数是context; 在一个动作中多次改变mutation时可以封装到action; 放在methods中
module: 允许将单一的store拆分多个store且同时保存在单一的状态树
vue-router 路由模式有几种?
Vue路由钩子函数
Vue 是如何实现数据双向绑定的?
Vue数据双向绑定指的是数据变化更新视图,视图变化更新数据。
利用Object.defineProperty来实现数据劫持,重写data的set和get函数。由set的通知机制,通知watcher, watcher 会告知虚拟DOM树,通过diff算法比较,把最新的DOM 更新到页面。
key值的作用
key的作用主要是为了高效的更新虚拟DOM。在Vue的虚拟DOM算法,对比新旧nodes来辨识VNodes。
如果不适用key,Vue会尽可能的尝试复用相同类型的元素;
如果使用key,会基于key的变化重拍元素,并且会销毁不存在的元素。
react 和 vue 的 diff 过程有什么区别
实现双向绑定 Proxy 与 Object.defineProperty 相比优劣如何?
computed 与watch的区别
computed 需要额外加工data 或者props中的数据时使用,可以产生一些新的属性,具有缓存性
watch 主要用作监听,使用场景:监听props,$emit或本组件的值执行异步操作,无缓存
BFC
BFC格式化上下文,它是一个独立渲染区域,让处于BFC内部的元素和外部的元素相互隔离,使内外元素的定位不会相互影响。
flex布局
盒模型理解
盒模型分为标准模型和IE模型。
标准模型:
盒模型的宽高只是内容(content)的宽高
box-sizing:content-box;
IE模型:
盒模型的宽高包括内容(content)+填充(padding)+边框(border)的总宽高
box-sizing:border-box;
前端优化
1.减少http请求,合并css,js,image
2.大量数据或图片时使用懒加载或者预加载优化
3.按需加载,加快首屏渲染速度
4.进行缓存机制
5.频繁执行的函数使用节流或者防抖优化
6.js放到body尾部
7.及时清理无用的定时器,避免内存泄漏
get和post区别
-
GET在浏览器回退时是无害的,而POST会再次提交请求。
-
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
-
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
-
GET请求在URL中传送的参数是有长度限制的,而POST没有。
-
GET参数通过URL传递,POST放在Request body中。
-
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
-
http协议并未规定get和post的长度限制,get的最大长度限制是因为浏览器和web服务器限制了URL的长度。
-
GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200。而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok
css优先级
常见状态码
jQuery事件委托方法bind,on,live,delegate的区别
bind: 直接绑定到目标元素上,不适用与动态添加元素时候使用
$( “#members li a” ).bind( “click”, function( e ) {} );
live: 没有把监听绑定到自己的身上,而是绑定到了this.context上,利用事件委托机制完成事件监听,把节点的处理委托给document。新添加的元素不用在绑定一次监听器。
$( document ).live( “click”, “#members li a”, function( e ) {} );
delegate: 事件监听绑定到了就近的父元素上。可以用在动态添加的元素上
$(“#info_table”).delegate(“td”,”click”,function(){});
on: 将监听事件绑定到指定元素上。可以用在动态添加的元素上
$(“#info_table”).on(“click”,”td”,function(){});