1 对闭包的理解?闭包使⽤场景?
闭包说的通俗⼀点就是打通了⼀条在函数外部访问函数内部作⽤域的通道。正常情况下函数外部是 访问不到函数内部作⽤域变量的, 表象判断是不是闭包:函数嵌套函数,内部函数被return 内部函数调⽤外层函数的局部变量 优点:可以隔离作⽤域,不造成全局污染 缺点:由于闭包⻓期驻留内存,则⻓期这样会导致内存泄露 如何解决内存泄露:将暴露全外部的闭包变量置为null
适⽤场景:封装组件,for循环和定时器结合使⽤,for循环和dom事件结合.可以在性能优化的过程中,节流 防抖函数的使⽤,导航栏获取下标的使⽤
2 深拷⻉浅拷⻉的区别
JavaScript 中存在两⼤数据类型: 基本类型 引⽤类型 基本类型数据保存在在栈内存中 引⽤类型数据保存在堆内存中,引⽤数据类型的变量是⼀个指向堆内存中实际对象的引⽤,存在栈中 浅拷⻉ 浅拷⻉,指的是创建新的数据,这个数据有着原始数据属性值的⼀份精确拷⻉ 如果属性是基本类型,拷⻉的就是基本类型的值。如果属性是引⽤类型,拷⻉的就是内存地址 即浅拷⻉是拷⻉⼀层,深层次的引⽤类型则共享内存地
深拷⻉开辟⼀个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改⼀个对象的属性,不会 改变另⼀个对象的属性
.什么是递归?
递归,就是在运行的过程中不断地调用自己。递归有两个过程,简单地说一个是递的过程,一个是归的过程。简单用代码来理解:
public void fun(参数) {
if (终止条件) {
return;
}
fun(参数);
(其他判断条件或语句);
}
判断递归使用的场景
1.大问题可以拆分为多个子问题
2.原问题和拆分后的子问题除了数据规模不同,解决思路完全相同。
递归在线性数据结构中使用不太明显,迭代基本可以很容易地解决问题。递归在非线性结构中非常重要,比如二叉树,回溯,典型的树形问题-九宫格字母组合
JavaScript的数据类型都有什么?
基本数据类型:Number、String、Boolean、Null、Undefined
复杂数据类型:Object(Function、Array、Date、RegExp)
什么是变量提升?
变量提升是当栈内存作用域形成时,JS代码执行前,浏览器会将带有var, function关键字的变量提前进行声明(值默认就是 undefined),定义 (就是赋值操作),这种预先处理的机制就叫做变量提升机制也叫预定义。
带 var 和不带 var 的区别?
全局作用域中不带var声明变量虽然也可以但是建议带上 var声明变量,不带 var 的相当于给window对象设置一个属性罢了。
私有作用域(函数作用域),带 var 的是私有变量。不带 var 的是会向上级作用域查找,如果上级作用域也没有那么就一直找到 window 为止,这个查找过程叫作用域链。
全局作用域中使用 var 申明的变量会映射到 window 下成为属性。
堆和栈都是运行时内存中分配的一个数据区,因此也被称为堆区和栈区;
堆(heap)用于复杂数据类型(引用类型)分配空间,例如数组对象、object对象;它是运行时动态分配内存的,因此存取速度较慢。
栈(stack)中主要存放一些基本类型的变量和对象的引用,(包含池,池存放常量),其优势是存取速度比堆要快,并且栈内的数据可以共享,但缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性
作用域
作用域是指程序源代码中定义变量的区域。也就是程序可以生效并运行的空间。
全局作用域是指变量可以在当前脚本的任意位置访问,拥有全局作用域的变量也被称为“全局变量”
在函数内部声明的变量具有局部作用域,拥有局部作用域的变量也被称为“局部变量”,局部变量只能在其作用域中(函数内部)使用
-
typeof对于基本数据类型判断是没有问题的,但是遇到引用数据类型(如:Array)是不起作用的
instanceof可以用来判断数组和对象,但不能用于基础数据类型。
constructor来判断数据的类型,但是除了null、undefined,因为他们不是由对象构建。
Object.prototype.toString.call(); 任何类型都可以精准检测出来
闭包
内部函数可以访问外部函数的值,该值会长期存储在内存中,因此产生了闭包
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。 直观的说就是形成一个不销毁的栈环境, 闭包实现了传递值和功能的调用。
优点:避免变量污染全局,变量的叠加使用
缺点:因为常驻内存,如果有大量闭包不被释放,容易造成内存溢出
递归
程序自我调用,简单理解就是函数自己调用自己。目的是为了处理不确定层级的
JavaScript的数据类型都有什么?
基本数据类型:Number、String、Boolean、Null、Undefined
复杂数据类型:Object(Function、Array、Date、RegExp)
什么是变量提升?
变量提升是当栈内存作用域形成时,JS代码执行前,浏览器会将带有var, function关键字的变量提前进行声明(值默认就是 undefined),定义 (就是赋值操作),这种预先处理的机制就叫做变量提升机制也叫预定义。
带 var 和不带 var 的区别?
全局作用域中不带var声明变量虽然也可以但是建议带上 var声明变量,不带 var 的相当于给window对象设置一个属性罢了。
私有作用域(函数作用域),带 var 的是私有变量。不带 var 的是会向上级作用域查找,如果上级作用域也没有那么就一直找到 window 为止,这个查找过程叫作用域链。
全局作用域中使用 var 申明的变量会映射到 window 下成为属性。
堆(heap)用于复杂数据类型(引用类型)分配空间,例如数组对象、object对象;它是运行时动态分配内存的,因此存取速度较慢。
栈(stack)中主要存放一些基本类型的变量和对象的引用,(包含池,池存放常量),其优势是存取速度比堆要快,并且栈内的数据可以共享,但缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性
作用域
作用域是指程序源代码中定义变量的区域。也就是程序可以生效并运行的空间。
全局作用域是指变量可以在当前脚本的任意位置访问,拥有全局作用域的变量也被称为“全局变量”
在函数内部声明的变量具有局部作用域,拥有局部作用域的变量也被称为“局部变量”,局部变量只能在其作用域中(函数内部)使用
-
typeof对于基本数据类型判断是没有问题的,但是遇到引用数据类型(如:Array)是不起作用的
instanceof可以用来判断数组和对象,但不能用于基础数据类型。
constructor来判断数据的类型,但是除了null、undefined,因为他们不是由对象构建。
Object.prototype.toString.call(); 任何类型都可以精准检测出来
闭包
内部函数可以访问外部函数的值,该值会长期存储在内存中,因此产生了闭包
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。 直观的说就是形成一个不销毁的栈环境, 闭包实现了传递值和功能的调用。
优点:避免变量污染全局,变量的叠加使用
缺点:因为常驻内存,如果有大量闭包不被释放,容易造成内存溢出
递归
程序自我调用,简单理解就是函数自己调用自己。目的是为了处理不确定层级的
堆和栈都是运行时内存中分配的一个数据区,因此也被称为堆区和栈区;
相同数据结构的数据处理。
优点:代码更简洁清晰,可读性更好;
数组方法
合并数组:concat
指定字符数组转字符串:join
添加元素操作:push()尾部 和unshift()头部
shift() & pop() 删除元素操作
sort() 数组排序,改变原数组
reverse() 反转数组,改变原数组
slice() 截取数组,不改变原数组
splice() 更新数组,改变原数组
indexOf() & lastIndexOf() 索引方法,不改变原数组
find() & findIndex() 根据函数内的判断返回找到的数组内的第一个元素。不改变原数组。 (es6新增方法)
forEach()、map()、filter()、some()、every() 迭代方法,不改变原数组。
reduce()、reduceRight() 归并方法,不改变原数组
keys()、values()、entries() 遍历数组方法,不改变原数组。 (es6新增方法)
includes() 不改变原数组。 (es6新增方法)
对象
创建对象的三种方式:
var obj = {} 字面量
New Object() 构造函数方式
Object.create() 对象方法创建
Object.entries(obj): 把对象转成键值对的数组
Object.definedProperty() 监听对象属性的变化,vue2的数据响应式原理。
Object.assign() 合并对个对象为一个对象
Object.values() 把对象的值序列化为数组
Object.keys() 把对象的属性名序列化为数组
JavaScript的基本规范?
不要在同一行声明多个变量
使用 ===或!==来比较true/false或者数值
switch必须带有default分支
函数应该有返回值
for if else 必须使用大括号
语句结束加分号
命名要有意义,使用驼峰命名法
栈和堆的区别?
栈(stack):由编译器自动分配释放,存放函数的参数值,局部变量等;
堆(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统释放。
谈谈this的理解
this总是指向函数的直接调用者(而非间接调用者)
如果有new关键字,this指向new出来的那个对象
在事件中,this指向目标元素,特殊的是IE的attachEvent中的this总是指向全局对象window。
什么是window对象? 什么是document对象?
window对象代表浏览器中打开的一个窗口。
document对象代表整个html文档。实际上,document对象是window对象的一个属性。
null,undefined的区别?
null表示一个对象被定义了,但存放了空指针,转换为数值时为0。
undefined表示声明的变量未初始化,转换为数值时为NAN。
typeof(null) -- object;
typeof(undefined) -- undefined
同步和异步的区别?
同步:每次程序只能执行一件事情,所有的执行程序需要排队。
异步:同时可以做多件事情
数组对象有哪些原生方法pop、push、shift、unshift、splice、reverse、sort、concat、join、slice、toString、indexOf、lastIndexOf、reduce、forEach、map、filter、every、some
JS垃圾回收机制?
标记清除:这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象。从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。
引用计数:这是最简单的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。该算法有个限制:无法处理循环引用。两个对象被创建,并互相引用,形成了一个循环。它们被调用之后不会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。
缺点:时间和空间消耗比较大、很多计算都是重复的、调用栈可能会溢出。
浅拷贝和深拷贝
浅拷贝只是拷贝一层,对更深层次对象级别的只拷贝引用(地址)
深拷贝拷贝多层,每一级别的数据都会拷贝
Object.assign(target,...sources) es6新增方法可以浅拷贝
JSON.parse(JSON.stringify(obj)) 可以实现暴力深拷贝
一般深拷贝使用递归去实现
字符串常用方法
获取字符串长度:length
获取字符串指定位置的值:charAt() 方法获取到的是指定位置的字符
查询是否包含某字符:indexOf、lastIndexOf、includes、search。
字符串拼接:出了使用+号,应该使用concat
字符串分割成数组:split
截取字符串:substr()、substring()和 slice()
字符串大小写转换:toLowerCase转小写、toUpperCase转大写
字符串转数字:parseInt转整数、parseFloat转小数
## 生命周期
created:初始化了$el(dom节点),data和methods(请求放在哪?因为这个时候初始化data和methods,所以在这近早的发起请求)
beforeCreate 创建前、 created创建后、beforeMount 挂载前、mounted挂载后、beforeUpdate 更新前、updated更新后、beforeDestory 销毁前、 destoryed销毁后## 组件通信
父子:父组件绑定一个属性,通过子组件的props接收
子父:在父组件的子组件上定义一个事件,通过this.$emit来派发
兄弟:建一个空的envbus(事件总线),导出vue实例,在组件内引入用$emit派发,用$on来接收,vuex
多级:通过provide传递,通过inject接收
普否应的 应接个他
## 通过下标更改数组使用方法如splice,pop,push,unshift
原理:vue内部重写了这些数组的操作方法,所以被调用时会感知到
## 有时候设置某些属性没有变化
使用$set动态添加
原理:用$set动态添加的属性会使用object.difineproperty()去劫持对象
## vue的双向绑定原理
通过object.difineproperty()劫持对象,在get函数中收集依赖,在set函数中通知更新。
## key的作用
标识vnode(虚拟节点)的唯一性可以在diff算法中进行新旧节点的虚拟dom树对比,没有变化的不替换,
## index作为key的不好的地方
index是有变化的,不具有唯一性,所以在diff算法中进行新旧节点的虚拟dom树对比时,虽然dom节点没变化,但是key值不一样,所以会导致重新渲染
## 常用的指令
v-html:用innerHTML
v-text:用innerTEXT
v-if:通过添加删除节点进行显示隐藏
v-show:通过display进行显示隐藏,频繁显示隐藏时用,
v-for:优先大于v-if,不建议和v-if一起使用,与key搭配使用
v-bind:绑定属性
v-on:绑定事件
v-once:只渲染一次
v-model:
## 常用的修饰符
.stop:阻止事件冒泡
.prevent:阻止默认事件
.trim:去除前后空格
.once:只触发一次
.keydown:建盘摁下
.number:将数据转换为数字
.sync:可以让子组件双向绑定
.native:绑定dom的原生事件
## 绑定事件不生效
使用.native
原理:绑定dom的原生事件
## keep-alive
名称:缓存组件
用法:
生命周期:activated激活时 deactivated未激活时
属性:
max:缓存的最大数量
include:可以缓存的组件名称
exclude:不可以缓存的组件名称
## data为什么是个函数
防止组件在重复使用时,数据互相干扰,使用函数将产生新作用域,所以同一个组件在不同位置被使用时,不适用同一份数据
## computed,watch,methods的区别
抗不秋他是 买塞吃
computed 计算结果会缓存,如果依赖值发生改变,就会重新调整计算结果,有get和set函数。watch: 监听数据变化,可以接收新值和旧值,
属性
deep:开启深度监听
immediate:立即执行没有旧值
handler:执行函数
``` js
watch: {
name(new,old) {
},
name: {
deep:true,
immediate:true,
hanlder: function(new,old) {
}
}
}
```methods 事件方法 执行一次,调用一次,不会缓存
## 怎么操作dom元素
ref
document.querySelector
倒块们他 块瑞 死来们他
document.getElementById
哎了们他
document.getElementsByName/ByClassName/ByTagName# 路由
## 传值方式
query:通过url传参,刷新值不会变化,不能传输对象。获取方式: this.$route.query.id
params:可以传递对象,但是刷新会消失。获取方式:this.$route.params.id
## $route和$router的区别
$route:路由对象,包含当前的路由信息。
$router:包含路由的操作方法。