JS
Q:常用es6语法
A:let、const、promise、模板字符串、解构赋值、拓展运算符、箭头函数
Q:有哪些遍历方法?
A:for,foreach,for in,for of,fillter,sort,reduce,map,every,some,Object.keys,Object.values
Q:get、post的区别
A:关键词:传参,大小,缓存,作用,安全性,编码格式,
1.get传参方式是通过地址栏URL传递,是可以直接看到get传递的参数,post传参方式参数URL不可见,get把请求的数据在URL后通过?连接,通过&进行参数分割。psot将参数存放在HTTP的包体内
2.get传递数据是通过URL进行传递,对传递的数据长度是受到URL大小的限制,URL最大长度是2048个字符。post没有长度限制
3.get后退不会有影响,post后退会重新进行提交
4.get请求可以被浏览器自动缓存,post不可以被自动缓存,只能通过手动设置缓存
5.get请求只支持URL编码,
post支持多种编码方式
如:application/x-www-form-urlencoded
multipart/form-data
application/json
text/xml
6.get请求的记录会留在历史记录中,post请求不会留在历史记录
7.get只支持ASCII字符,post没有字符类型限制
8.get多用于请求数据,post多用了增删改查
Q:简单说一下事件委托和事件冒泡
A:事件冒泡是指当点击子元素的时,会触发父元素的方法,可以通过e.stopPropagation()来阻止;
事件委托是指给最高级的元素添加点击时间事件,利用事件冒泡机制,来给其他子元素添加事件
Q:什么是回流和重绘?
A:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。
当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
区别:
他们的区别很大:
回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流
当页面布局和几何属性改变时就需要回流
比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变
Q:js的new操作符到底做了什么?
A:
1、创建了一个空的js对象(即{})
2、将空对象的原型prototype指向构造函数的原型
3、将空对象作为构造函数的上下文(改变this指向)
4、对构造函数有返回值的判断
在new的时候,会对构造函数的返回值做一些判断:
1、如果返回值是基础数据类型,则忽略返回值;
2、如果返回值是引用数据类型,则使用return 的返回,也就是new操作符无效;
/*
create函数要接受不定量的参数,第一个参数是构造函数(也就是new操作符的目标函数),其余参数被构造函数使用。
new Create() 是一种js语法糖。我们可以用函数调用的方式模拟实现
*/
function create(Con,...args){
//1、创建一个空的对象
let obj = {}; // let obj = Object.create({});
//2、将空对象的原型prototype指向构造函数的原型
Object.setPrototypeOf(obj,Con.prototype); // obj.__proto__ = Con.prototype
//3、改变构造函数的上下文(this),并将剩余的参数传入
let result = Con.apply(obj,args);
//4、在构造函数有返回值的情况进行判断
return result instanceof Object?result:obj;
}
Q:bind,apply,call三者的区别
A:1.三者都可以改变函数的this对象指向。
2.三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window。
3.三者都可以传参,但是apply是数组,而bind,call是参数列表,且apply和call是一次性传入参数。
4bind 是返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行 。
Q:forEach和map方法的区别
A:1.map()会分配内存空间存储新数组并返回,forEach()不会返回数据。
2.forEach()允许callback更改原始数组的元素。map()返回新的数组。
Q:什么是闭包?
A:1.闭包是指有权访问另外一个函数作用域中的变量的函数。可以理解为(能够读取另一个函数作用域的变量的函数)
2.闭包内存泄露只在IE中出现
3.闭包的使用场景:当我们需要使一个函数访问外部的变量时,可以使用闭包把目标函数嵌套在一个新函数中,调用该新函数,传入需访问的变量,则目标函数可以访问到传入的变量
Q:原型链是什么?
A:每个构造函数都有一个prototype属性,该属性是函数的原型对象,prototype上有constructor,该属性指向了函数本身;构造函数生成一个实例对象后,该实例对象上有proto属性,它指向了函数的prototype属性(即原型对象);
在访问对象的某个元属性时会先在对象中找是否存在,
如果当前对象中没有就在构造函数的原型对象中找,
如果原型对象中没有找到就到原型对象的原型上找,
直到Object的原型对象的原型是null为止,
这种一层层往上找的关系就叫原型链,主要的特性是属性的共享和继承
prototype与proto的关系
prototype是构造函数的属性,
proto是实例对象的属性,
这两者都指向同一个对象
Q:webpack打包原理
A:
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数。
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译。
- 确定入口:根据配置中的 entry 找出所有的入口文件。
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
- 完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
Q:浅拷贝和深拷贝的区别
A:1.就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝;反之则是深拷贝
浅拷贝的实现方法:
1.Object.assign方法
var obj = {
a: 1,
b: 2
}
var obj1 = Object.assign(obj);
obj1.a = 3;
console.log(obj.a) // 3
2.直接用=赋值
let a=[0,1,2,3,4],
b=a;
console.log(a===b);
a[0]=1;
console.log(a,b);
**深拷贝的实现方法:
1.采用递归去拷贝所有层级属性
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
2.通过JSON对象来实现深拷贝
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}
不足:1.无法拷贝 对象中的方法属性
2.无法拷贝 对象中值为undefined的属性
Q:promise和async/await的区别
A:async函数会隐式地返回一个promise,该promise的reosolve值就是函数return的值
Q:数组去重方法
A:1.indexOf查找
function uniq(array){
var temp = []; //一个新的临时数组
for(var i = 0; i < array.length; i++){
if(temp.indexOf(array[i]) == -1){
temp.push(array[i]);
}
}
return temp;
}
2.includes
function uniq(array){
var temp = []; //一个新的临时数组
for(var i = 0; i < array.length; i++){
if(!array.include(array[i])){
temp.push(array[i]);
}
}
return temp;
}
3.es6 set去重
let set6 = new Set([1, 2, 2, 3, 4, 3, 5])
console.log('distinct 1:', set6)
Q:垃圾回收机制
A:一般来说没有被引用的对象就属于垃圾,浏览器会回收这些对象
Q:防抖和节流是什么?
A:1.防抖:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定时间到来之前,又触发了事件,就重新开始延时。也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于既定时间,那么防抖的情况下只会执行一次。
function debounce(fn, wait) {
var timeout = null; //定义一个定时器
return function() {
if(timeout !== null)
clearTimeout(timeout); //清除这个定时器
timeout = setTimeout(fn, wait);
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
2.节流:当持续触发事件时,保证在一定时间内只调用一次事件处理函数,意思就是说,假设一个用户一直触发这个函数,且每次触发小于既定值,函数节流会每隔这个时间调用一次
//时间戳实现
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this; //this指向window
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
//计时器实现
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
Q:说一下什么是跨域?怎么去解决?
A:跨域是指请求资源的地址与发起请求的地址不符合同源策略时,则为跨域(同源策略,就是指两个地址必须具有相同的协议、域名、端口号)
解决跨域的方法:
1.jsonp跨域:即在页面中动态创建