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 就被称为闭包。
js的数据类型
基本数据类型:Undefined、Null、Boolean、Number和String,
复杂的数据类型:object、Array、Function...引用类型,
typeof(null) // 返回Object(表示空指针)
深拷贝和浅拷贝
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
包含额外的属性合并到第一个参数
如何区别node环境和浏览器环境
1)node中this指向global,浏览器中this指向window
2)在浏览器中的window下不少的API 比如 alert 、document、location、history 等等还有很多。我门就不能在node环境中xxx();或window.xxx();了。因为这些API是浏览器级别的封装,纯javascript中是没有的。
prototypehe _proto_的区别
1):对象有属性——_proto_指向对象的构造函数的原型对象
2)方法除了有属性_proto_,还有属性prototype,prototype指向该方法的原型对象:
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);
移动端点击穿透,原理及解决方法
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事件被触发了。
- window.onload和domcontentload
onload是在文件都加载完后执行,domcontentload是在DOM加载完后执行,不用等待图片js之类的加载
jq的ready()方法,是页面已经呈现(图片之类的也渲染了)之后执行
自定义事件
// 注册customer事件名,ev事件对象
// 注册customer事件名,ev事件对象
var btnDom = document.getElementById("#btn")
var eve = new Event('cutomer',{})
// 给dom节点绑定自定义事件
btnDom.addEventListener('customer', function(){
console.log('自定义事件触发成功')
})
// 直接触发
btnDom.dispatchEvent(eve) //触发自定义事件eve-事件对象
//或者点击document触发
document.addEventListener('click',function(){
btnDom.dispatchEvent(eve) //触发自定义事件eve-事件对象
})
事件委托
1)事件委托是指利用“事件冒泡”,只通过指定一个事件处理程序,来管理某一类型的所有事件。也就是说,当此事件处理程序被触发时,通过当前事件对象中的target来确认究竟是在哪个元素触发的事件,从而达到一次注册 处理多个元素触发事件的目的。https://www.cnblogs.com/liugang-vip/p/5616484.html
什么是事件循环
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,name)
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
}
}
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)
argument如何成为一个数组
function a () {
// 方法1:
var r = [].slice.apply(arguments) // 用call也可以
// 方法2:
var r = Array.prototype.slice.call(arguments) // apply
// 方法3:
var r = []
for(i = 0; i< arguments.length; i++) {
r.push(arguments[i])
}
// 方法4:
var r = Array.from(arguments)
// 方法5
var r = [...arguments]
console.log(r)
}
实现一个instanceof
function ins(left, right) {
// 获取要判断的类型原型
let prototype = right.prototype
// 获取对象的原型
left = left.__proto__
while (true) {
if (left === null)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
instanceof 和 typeof 和 Object.prototype.toString.call()
1. instanceof用来判断A是否为B的实例,不能判断null和undefined,用字面量生成的也无法判断
```
1 instanceof Number // false,字面量形式的判断不了
new Number(1) instanceof Number // true
[] instanceof Array // true
{} insranceof Object // true
new Date() instanceof Date // true
new RegExp instanceof RegExp // true
```
2. typeof判断返回包括number,string,Boolean,symbol,object,undefined,function,不能判断null和array
```
typeof Symbol(); // symbol 有效
typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
```
3. Object.prototype.toString.call(),最准确的方式
```
// object上的toString的作用是返回当前方法的
// 执行主体(方法中的this)所属类的详细信息[object Object]
Object.prototype.toString.call('') // [object string]
Object.prototype.toString.call(1) // [object Number]
Object.prototype.toString.call(null) // [object Null]
...
```
for in 和 for of,forEach
- for in,in遍历的是索引,of遍历的是数组的值,for of无法遍历对象
- forEach无法使用break, continue 和 return
- for in 的毛病:index索引为字符串型数字,不能计算,遍历顺序不是真正的顺序,会遍历数组所有可枚举属性包括原型上的方法和name属性(可以用hasOwnProperty()方法判断),比较适合遍历对象
- 可以通过Object.keys(myObject)获取对象的实例属性组成的数组,不包括原型方法和属性
- for of可以正确的响应break,continue和return
function fors() {
// for of-- 1 2 4 5 6 7
Array.prototype.method=function(){
console.log(this.length);
}
var myArray=[1,2,4,5,6,7]
myArray.name="数组";
for (var value of myArray) {
console.log(value);
}
// for in-- 1 2 4 5 6 7 数组
// ƒ (){
// console.log(this.length);
// }
// 会遍历原型上的方法和属性所以会输出: '数组' 和f(){}
Array.prototype.method=function(){
console.log(this.length);
}
var myArray=[1,2,4,5,6,7]
myArray.name="数组"
for (var index in myArray) {
console.log(myArray[index]);
}
}
computed和watch,methods的区别
- computed是获取一个值或者结果,而这个值受其他的依赖的影响,如果他所依赖的这些属性值没有发生改变,那么计算属性的值是从缓存中来的,而不是重新编译,那么性能要高一些
- watch是监听一个数据,当这个数据变动时,去处理其他相关的变化,(比较适合做一些异步操作的事情)。
- methods: 一般相当于手动触发
ios,1px问题
- 在retina屏上devicePixelRatio(物理像素和css像素比)的值为2或者3,所以1px映射上去就是2px或者3px
- 使用小数来处理px,安卓,低版本ios不支持
@media screen and (-webkit-min-device-pixel-ratio: 2) {
.border { border: 0.5px solid #999 }
}
- :before,:after与transform:构建1个伪元素, 将它的长宽放大到2倍, 边框宽度设置为1px, 再以transform缩放到50%.
- js动态获取retina屏的devicePixelRatio值设置scale=1/devicePixelRatio,然后去设置meta标签的initial-scale,maximum-scal,minimum-scale这几个值
缓存机制
- 强缓存(返回200)和协商缓存(命中缓存304)
- 强缓存:expires和cache-control:后者的优先级高于前者。
- 协商缓存,1,last-modified(文件的最后修改时间),if-last-modified发送last-modified到服务端,
- 协商缓存,2, etag(文件指纹),if-no-match会把etag发送给服务端,如果有跟新则返回更新资源
cookie,localstorage,sessionStorage
- 问题点:页面刷新后保留vuex数据:可以将vuex数据放到localstorage里(vuex-persistedstate)
跨域
- jsonp,利用script标签引用的文件没有同源限制,src访问服务端地址,服务端返回预先定义好的函数的调用。以及可以传递参数之类的。
- CORS,服务端给响应头部添加Aceess-Control-Allow-Origin的字段,可以指定具体的域名
- websocket,他是一种通信协议,只要服务端支持,就能进行跨域通信
- 设置相同的document.domain,两个网页就可以共享cookie(比如主域名相同的情况)
- h5新增方法:window.postMessage,父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了
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']
})
externals
- 使用cdn引入一个库,将不用打包的放在这个配置里,但是依然可以使用import或requier引入使用
call,apply,bind
- 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()才能返回最大值
// 再来个例子
var a ={}
// 构造一个方法
function sum(a,b,c) {
console.log(a+b+c)
}
// 让a对象继承sum方法,并将[1,2,4],作为参数传入
sum.apply(a, [1,2,4])
vue如何监听数组的变动
- 重新赋值
- splice等方法
- this.$set(arr, index, newVal);
vue监听对象的变动
- 使用 this.$set(object, key, value)
- 使用深度监听 deep: true,只能监听原有属性的变化,不能监听增加的属性
mounted () {
// 这里使用深度监听 blog 对象的属性变化,会触发 getCatalog 方法
this.$watch('blog', this.getCatalog, {
deep: true,
});
},
- 使用 Object.assign 方法
this.watchObj = Object.assign({}, this.watchObj, {
name: 'xiaoyue',
age: 15,
});
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
vue的data为什么是个函数
- 使用方法函数让每个组件都有自己的作用域,组件相互独立不受影响。
平时代码里都用过哪些优化的写法
1.for循环一个变量的属性的时候,应该使用一个临时变量,比循环一个data.data会好点–这个应该算吧?
2.
项目里遇到过什么问题
- 小程序项目:
- 当时生成的图片比较模糊:把导出图片的大小设置为Canvas大小的2到4倍即可
- input输入框在ios下闪烁,不用双向绑定,改用@input监听
- 无法使用网络图片。要先将图片用downLoadFile下载下来才可以绘制(做二维码的时候)
- 不能使用字体图标
- 轻应用项目(首屏优化):
- 使用CDN加载第三方资源库,并在webpack中配置externals不将其打包进去
- 使用CommonChunkPlugin提取公共代码。
- 不使用vue-resource或者axios自己实现一个ajax库: new XMLHttpRequest()
- 代码分块/Code Splitting
- 路由组件懒加载
有哪些模块化规范CMD,AMD
require和import的区别?
DOM事件
- 事件级别:
dom0:element.function(){}
dom2:element.addEventListener(‘click’,function() {})
dom3:增加了更多事件类型而已 - 事件模型:冒泡,捕获
- 事件流:捕获(window-document-html-body)–>目标阶段–>冒泡
- 事件捕获的具体流程: 事件发生时先到达window-document-html
- event对象的常见应用: preventDefault(),stopPropagation(),stopImmediatePropagation()事件优先级,event.currentTarget:(当前绑定的事件),event.target:(当前元素)
- 自定义事件(模拟事件):
可以指定事件名称,可以设置参数之类的var evens = new Event('custome') ev.addEventListener('custome', function(){}) ev.dispatchEvent(eve) //触发
列举IE与其他浏览器不一样的特性
触发事件的元素被认为是目标(target)。而在IE中,目标包含在event对象的srcElement属性;
获取字符代码、如果按键代表一个字符(shift、ctrl、alt除外),IE的keyCode会返回字符代码(Unicode),DOM中按键的代码和字符是分离的,要获取字符代码,需要使用charCode属性;
阻止某个事件的默认行为,IE 中阻止某个事件的默认行为,必须将 returnValue 属性设置为 false,Mozilla 中,需要调用 preventDefault() 方法;
停止事件冒泡,IE 中阻止事件进一步冒泡,需要设置 cancelBubble 为 true,Mozzilla 中,需要调用 stopPropagation();