常见问题
es6
let 和var 的区别:var作用域为所在函数内,let作用域为语句所在代码块,不存在变量提升且不允许重复声明
-
为什么var可以重复声明:
当我们执行代码时,我们可以简单的理解为新变量分配一块儿内存,命名为 a,并赋值为 2,但在运行的时候编译器与引擎还会进行两项额外的操作:判断变量是否已经声明:首先编译器对代码进行分析拆解,从左至右遇见 var a,则编译器会询问作用域是否已经存在叫 a 的变量了,如果不存在,则招呼作用域声明一个新的变量 a,若已经存在,则忽略 var 继续向下编译,这时 a = 2被编译成可执行的代码供引擎使用。引擎遇见 a=2时同样会询问在当前的作用域下是否有变量 a,若存在,则将 a赋值为 2(由于第一步编译器忽略了重复声明的var,且作用域中已经有 a,所以重复声明会发生值得覆盖而并不会报错)。若不存在,则顺着作用域链向上查找,若最终找到了变量 a则将其赋值 2,若没有找到,则招呼作用域声明一个变量 a并赋值为 2
-
箭头函数:
1)跟普通函数的区别:箭头函数内的this继承自外围作用域
2)普通函数的this指向调用它的那个对象
3)箭头函数是匿名函数,不能作为构造函数,不能使用new
4)箭头函数在定义之后this就不会改变,无论用super(),arguments都不会改变
5)箭头函数不绑定arguments
6)普通函数达到箭头函数的效果的方法:
定义一个局部变量:_self = this
var obj = {
name:"hha",
fn:() => {
console.log(this) // window
},
fnn(){
console.log(this) // obj
},
fn2:{
fn3:() => {
console.log(this) // window
}
},
c: function() {
return ()=>{
console.log(this) // obj
console.log(this.name); //hha
}
}
}
obj.fn() // Window
obj.fn1() // obj
obj.fn2.fn3() // Window
obj.c()() // obj,hha
var obj = {
name: 'hah',
fn: function () {
console.log(this) // obj
},
fn1() {
console.log(this) // obj
},
fn2: {
fn3: function () {
console.log(this) // fn2
}
}
}
obj.fn() // obj
obj.fn1() // obj
obj.fn2.fn3() // fn2
3.promise
promise接收一个函数作为参数,这个函数有两个方法作为参数,resolve和reject,当异步操作成功后我们调用resolve函数,将异步任务的结果作为参数传递出去,失败的时候调用reject去处理,他的then方法可以接受两个回调作为参数,一个是resolve,一个就是reject
有3个状态pending,resolve,reject
js
原型链:
每个对象都有一个proto属性指向创建该对象的构造函数的原型,
每个函数都有一个prototype属性指向原型
new的时候发生了什么:
1.新生成了一个对象
2.链接到原型
3.绑定 this
4.返回新对象
https://juejin.im/entry/5883672c570c350062be16e5(转载参考)
闭包:
- 闭包的定义很简单:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
2.js的数据类型
基本数据类型:Undefined、Null、Boolean、Number和String,
复杂的数据类型:object、Array、Function...引用类型,
typeof(null) // 返回Object(表示空指针)
3.深拷贝和浅拷贝
1)浅拷贝只将对象的各个属性依次复制,深拷贝则递归复制了所有层级
2)浅拷贝区别:因为js存储对象都是存地址,所以浅拷贝会造成:shallowObj.arr[1] = 5;obj.arr[1] // = 5
3)深拷贝会生成一个新对象,深拷贝会影响性能吧,如果层级很多的话
4)方法:浅拷贝:jquery: extend(deep,target,object1,objectN):
let b = Object.assign({},a)
or
var a = {age:88}
let b = {...a}
deep类型: Boolean
如果是 true,合并成为递归(又叫做深拷贝)。不支持给这个参数传递 false
target类型: Object
对象扩展。这将接收新的属性。
object1类型: Object
一个对象,它包含额外的属性合并到第一个参数.
objectN类型: Object
包含额外的属性合并到第一个参数
4.如何区别node环境和浏览器环境
1)node中this指向global,浏览器中this指向window
2)在浏览器中的window下不少的API 比如 alert 、document、location、history 等等还有很多。我门就不能在node环境中xxx();或window.xxx();了。因为这些API是浏览器级别的封装,纯javascript中是没有的。
5.prototypehe proto的区别
1):对象有属性——proto指向对象的构造函数的原型对象
2)方法除了有属性proto,还有属性prototype,prototype指向该方法的原型对象:
6.mouseover和 mouseenter有什么区别:
1)mouseenter不会冒泡
2)当两者绑定的元素均没有子元素的时候两者无差
3)有子元素时mouseover经过绑定元素和资源苏都会触发
7.移动端的click事件和PC的不同
1)移动端的click会延迟300ms触发事件回调,
部分厂商为了识别翻页或者双击放大等复杂手势所以加了300ms延迟处理
2)PC不需要,设置了禁止缩放的不需要user-scalable=no
3)解决方法:引入fastclick.js解决,
他的原理是fastclick在检测到touchend事件的时候,
会通过DOM自定义事件立即触发一个模拟click事件,
并把浏览器在300ms之后真正触发的click事件阻止掉
在main.js中引入,并绑定到document.body上
import FastClick from 'fastclick'
FastClick.attach(document.body);
8.移动端点击穿透,原理及解决方法
1)点击穿透是指在移动端click事件延迟300ms触发,那么如果300ms内,页面显示变化(主要指DOM的隐藏和显示)的话,会实际点击元素触发touch事件,而300ms后该位置的实际元素又被再次click:
2)几种现象:
点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件。蒙层的关闭按钮绑定的是touch事件,而按钮下面元素绑定的是click事件,touch事件触发之后,蒙层消失了,300ms后这个点的click事件fire,event的target自然就是按钮下面的元素,因为按钮跟蒙层一起消失了。
跨页面点击穿透问题:如果按钮下面恰好是一个有href属性的a标签,那么页面就会发生跳转。因为 a标签跳转默认是click事件触发 ,所以原理和上面的完全相同。
另一种跨页面点击穿透问题:这次没有mask了,直接点击页内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了。
-
事件委托
1)事件委托是指利用“事件冒泡”,只通过指定一个事件处理程序,来管理某一类型的所有事件。也就是说,当此事件处理程序被触发时,通过当前事件对象中的target来确认究竟是在哪个元素触发的事件,从而达到一次注册 处理多个元素触发事件的目的。https://www.cnblogs.com/liugang-vip/p/5616484.html
10.什么是事件循环
1)JavaScript是单线程的,“主线程”负责执行所有的同步任务,一旦所有同步任务执行完成,则立即从“任务队列”中读取最优先的任务放到“主线程”中执行,如此循环往复。向“任务队列”插入的是一个个事件处理函数(确切的说是函数地址)或定时任务(setTimeout的回调)。
11.css3中有哪些属性可以直接影响JS中的事件?(可以讲一下pointer-events和touch-action属性吗)–过吧,浪费时间
-
js的继承:
1)继承就是通过将父类的实例作为子类的原型,让子类拥有父类的属性和方法,
可以通过寄生组合方式:es5:
function parent(name){
this.name = name
}
parent.prototype.getNama = function () {
// do..
}
function child (age) {
parent.call(this,namae)
this.age = age
}
child.prototype = new parent()
child.prototype.constructor = child
child.prototype.getAge = function () {
// do
}
var a = new child("name","age")
a.getName()
a.getAge()
es6
class parent {
constructor(name) {
this.name = name
}
getName() {
// do
}
}
class child extends parent {
constructor (name,age) {
super(name)
this.age = age
}
getAge() {
// do
}
}
13.
1)arguments: 由此,我们可以使用arguments对象来获取实参,或者判断参数个数等
2)实现方法的重载:
- 实现参数相加的函数
function add() {
var len = arguments.length;
var result = 0;
for (var i = 0; i < len; i++) {
result += arguments[i]
}
return result
}
2.利用arguments.callee实现递归:
// 方法一
// 实现阶乘:5*4*3*2*1
function a (num) {
if (num <= 1) {
return 1
} else {
return num*a(num-1)
}
}
//当这个函数变成了一个匿名函数时,我们就可以利用callee来递归这个函数。es4禁用了这个属性
// 方法二
function a (num) {
if (num <= 1) {return 1}
else {
num*arguments.callee(num)
}
}
数组去重:
// 方法一:
//indexof,判断当前循环的数组下标不是当前的i,则表示该数据重复
var arr = [1,2,2,3,'yy', 'lu','lu',1,5,6,5]
function doit (arr) {
var temp = []
for (let i = 0; i < arr.length; i++) {
if (arr.indexOf(arr[i]) === i) {
temp.push(arr[i])
}
}
return temp
}
doit(arr)
// 方法2:indexOf() === -1 ,不存在
//建立新数组,判断当前循环的值在新数组中是否存在,不存在就push
var arr = [1,2,2,3,'yy', 'lu','lu',1,5,6,5]
function doit (arr) {
var temp = []
for (let i = 0; i < arr.length; i++) {
if (temp.indexOf(arr[i]) === -1) {
temp.push(arr[i])
}
}
return temp
}
doit(arr)
方法3: es6的set()方法
var arr = [1,2,2,3,'yy', 'lu','lu',1,5,6,5]
function doit(arr) {
var x = new Set(arr)
return [...x]
}
var newArr = doit(arr)
缓存机制
- 强缓存(返回200)和协商缓存(命中缓存304)
- 强缓存:expires和cache-control:后者的优先级高于前者。
- 协商缓存,1,last-modified(文件的最后修改时间),if-last-modified发送last-modified到服务端,
- 协商缓存,2, etag(文件指纹),if-no-match会把etag发送给服务端,如果有跟新则返回更新资源
cookie,localstorage,sessionStorage
jsonp的原理
利用script标签引用的文件没有同源限制,src访问服务端地址,服务端返回预先定义好的函数的调用。以及可以传递参数之类的。
网络安全
1、XSS: 通过执行js代码来攻击
防御:1、最普遍的做法是转义输入输出的内容,对于引号,尖括号,斜杠进行转义
2、建立白名单,规定了浏览器只能够执行特定来源的代码。HTTP header 中 Content-Security-Policy首部设置
2、CSRF: 跨站请求伪造
防范 CSRF 可以遵循以下几种规则:
Get 请求不对数据进行修改
不让第三方网站访问到用户 Cookie—cookie设置SameSite
阻止第三方网站请求接口–refer
请求时附带验证信息,比如验证码或者 token
webpack如何配置多页面
1.创建多个HtmlWebpackPlugin:
new HtmlWebpackPlugin({
filename: 'a.html',
template: './a.html',
chunks: ['common']
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template:'./index.html',
chunks: ['app','common']
})
call,apply,bind
// 输出最大值458,a本身没有max方法,通过call和apply方法实现继承Math的max方法
var numbers = [5, 458 , 120 , -215 ];
var a = {}
var maxInNumbers = Math.max.apply(a, numbers)
//或者
var maxInNumbers = Math.max.call(a,5,458,120,-215)
//或者
var maxInNumbers = Math.max.call(a,5,458,120,-215)
maxInNumbers() // 使用bind的话不会立即执行,所以要执行一次maxInNumbers()才能返回最大值
vue的双向绑定原理
1、有一个observer的数据监听器,可以对对象上的属性进行监听,如果有变动就可拿到最新值并通知订阅者,
observer的实现:通过Object.definproperty()来截止各个属性的get和set,监听属性的变动,然后通知订阅者
2、有一个指令解析器compile。对每个元素节点进行指令扫描解析,根据指令模板替换数据,以及绑定相应的更新函数,
3、有一个watcher,连接observer和compile,能够订阅并收到每个属性的变动通知,执行指令绑定的的相应的回调函数,从而更新视图
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
如果有点乱,可以回顾下前面的思路整理
vue 的生命周期
beforeCreate: vue实例的挂载元素$el和数据对象data都为undefined
created: 该阶段vue实例的数据对象data有了,$el还没有
beforeMount: $el和data开始初始化,但还是挂载前的的虚拟DOM,
mounted:$el挂载完成,data渲染成功
beforeUpdate:data变化会触发beforeUpdate和updated
updated:beforeDestroy:执行destroyed后data的改变不会触发数据变动监听,说明已经解除了事件监听和Dom的绑定,但dom结构还存在
destroyed
平时代码里都用过哪些优化的写法
1.for循环一个变量的属性的时候,应该使用一个临时变量,比循环一个data.data会好点–这个应该算吧?
2.