常见问题

es6

  1. let 和var 的区别:var作用域为所在函数内,let作用域为语句所在代码块,不存在变量提升且不允许重复声明

  2. 为什么var可以重复声明:

    当我们执行代码时,我们可以简单的理解为新变量分配一块儿内存,命名为 a,并赋值为 2,但在运行的时候编译器与引擎还会进行两项额外的操作:判断变量是否已经声明:首先编译器对代码进行分析拆解,从左至右遇见 var a,则编译器会询问作用域是否已经存在叫 a 的变量了,如果不存在,则招呼作用域声明一个新的变量 a,若已经存在,则忽略 var 继续向下编译,这时 a = 2被编译成可执行的代码供引擎使用。引擎遇见 a=2时同样会询问在当前的作用域下是否有变量 a,若存在,则将 a赋值为 2(由于第一步编译器忽略了重复声明的var,且作用域中已经有 a,所以重复声明会发生值得覆盖而并不会报错)。若不存在,则顺着作用域链向上查找,若最终找到了变量 a则将其赋值 2,若没有找到,则招呼作用域声明一个变量 a并赋值为 2

  3. 箭头函数:

    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(转载参考)

这里写图片描述

这里写图片描述

闭包:
  1. 闭包的定义很简单:函数 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事件被触发了。

  1. 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)实现方法的重载:

  1. 实现参数相加的函数
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这几个值
缓存机制
  1. 强缓存(返回200)和协商缓存(命中缓存304)
  2. 强缓存:expires和cache-control:后者的优先级高于前者。
  3. 协商缓存,1,last-modified(文件的最后修改时间),if-last-modified发送last-modified到服务端,
  4. 协商缓存,2, etag(文件指纹),if-no-match会把etag发送给服务端,如果有跟新则返回更新资源
cookie,localstorage,sessionStorage
  • 问题点:页面刷新后保留vuex数据:可以将vuex数据放到localstorage里(vuex-persistedstate)这里写图片描述
跨域
  1. jsonp,利用script标签引用的文件没有同源限制,src访问服务端地址,服务端返回预先定义好的函数的调用。以及可以传递参数之类的。
  2. CORS,服务端给响应头部添加Aceess-Control-Allow-Origin的字段,可以指定具体的域名
  3. websocket,他是一种通信协议,只要服务端支持,就能进行跨域通信
  4. 设置相同的document.domain,两个网页就可以共享cookie(比如主域名相同的情况)
  5. 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
  1. 使用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如何监听数组的变动
  1. 重新赋值
  2. splice等方法
  3. this.$set(arr, index, newVal);
vue监听对象的变动
  1. 使用 this.$set(object, key, value)
  2. 使用深度监听 deep: true,只能监听原有属性的变化,不能监听增加的属性
mounted () {
  // 这里使用深度监听 blog 对象的属性变化,会触发 getCatalog 方法
  this.$watch('blog', this.getCatalog, {
    deep: true,
  });
},
  1. 使用 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. 使用方法函数让每个组件都有自己的作用域,组件相互独立不受影响。
平时代码里都用过哪些优化的写法

1.for循环一个变量的属性的时候,应该使用一个临时变量,比循环一个data.data会好点–这个应该算吧?
2.

项目里遇到过什么问题
  1. 小程序项目:
  • 当时生成的图片比较模糊:把导出图片的大小设置为Canvas大小的2到4倍即可
  • input输入框在ios下闪烁,不用双向绑定,改用@input监听
  • 无法使用网络图片。要先将图片用downLoadFile下载下来才可以绘制(做二维码的时候)
  • 不能使用字体图标
  1. 轻应用项目(首屏优化):
  • 使用CDN加载第三方资源库,并在webpack中配置externals不将其打包进去
  • 使用CommonChunkPlugin提取公共代码。
  • 不使用vue-resource或者axios自己实现一个ajax库: new XMLHttpRequest()
  • 代码分块/Code Splitting
  • 路由组件懒加载
有哪些模块化规范CMD,AMD
require和import的区别?
DOM事件
  1. 事件级别:
    dom0:element.function(){}
    dom2:element.addEventListener(‘click’,function() {})
    dom3:增加了更多事件类型而已
  2. 事件模型:冒泡,捕获
  3. 事件流:捕获(window-document-html-body)–>目标阶段–>冒泡
  4. 事件捕获的具体流程: 事件发生时先到达window-document-html
  5. event对象的常见应用: preventDefault(),stopPropagation(),stopImmediatePropagation()事件优先级,event.currentTarget:(当前绑定的事件),event.target:(当前元素)
  6. 自定义事件(模拟事件):
    可以指定事件名称,可以设置参数之类的
    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();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值