1、React/Vue 在列表组件中写key的作用是什么?
不带有key,并且使用简单的模板时,可以更高效的复用节点,但这并不是key的作用。
key的作用是为了提升diff 同级比较的效率;
消除就地复用带来的副作用;
2、['1', '2', '10'].map(parseInt)
输出结果
[1,NaN,2]
parseInt(str,n)
把第一个参数看作是一个数的n进制的表示,而返回的数值则是十进制;
如parseInt('123',5);//将'123'当做5进制的数,返回十进制 1*5^2 + 2*5^1 + 3*5^0 = 38
如果第一个参数中不存在n进制的表示数字,则返回能解析的部分,parseInt('15',2) = 1
3、防抖和节流
防抖
高频事件触发,但在n秒内只触发一次,n秒内再次触发则重新计时;
function unshake(fn){
let timer = null;
return function(){
clearTimeout(timer);
timer = setTimeout(()=>{
fn.apply(this,arguments);
clearTimeout(timer);
},500)
}
}
节流
高频事件触发时,n秒内只触发一次,所以节流会稀释函数的执行频率
function throttle(fn){
let canRun = true;
return function(){
if(!canRun) return;
canRun = false;
setTimeout(()=>{
fn.apply(this,arguments);
canRun = true;
},500)
}
}
4、深度克隆
function deepClone(val,map = new WeakMap()){
//如果传入的值是null或者是基本类型,则直接返回数值本身;
if(val===null || typeof val !== 'object') return val;
//如果克隆的值之前克隆过,则直接返回之前的值;
if(map.has(val)) return map.get(val);
//判断克隆的是数组还是对象
let clone = Array.isArray(val) ? [] : {};
//开始克隆并存储
map.set(val,clone);
let keys = Reflect.ownKeys(val);//返回对象的所有属性
let len = keys.length;
while(len--){
clone[keys[len]] = deepClone(val[keys[len]],map);
}
return clone;
}
5、setTimeout、Promise、async/await的区别
setTimeout
setTimeout的回调函数是放到宏任务队列里;(等到执行栈清空后执行)Promise
Promise本身执行时是同步的,then产生的回调函数是放到微任务队列里async/await
async函数表示函数里可能会有异步的方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行await后面的表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行
6、JS异步解决方案的发展历程以及优缺点
回调函数
优点
1、解决了同步问题
缺点
缺乏顺序性:回调地域导致的调试困难
嵌套函数耦合性高;
嵌套过多很难处理错误
不能return
Promise
优点
1、解决了回调地狱的问题
缺点
一旦开始就无法取消
不知道内部进行到哪个阶段
错误需要通过回调函数来捕获
Async/await
终极的异步解决方案
优点
代码清晰,处理了地域回调的问题
缺点
await将异步变同步,多个异步操作如果没有依赖关系,使用await会导致性能下降
7、自己实现new
思路
1、创建一个新对象,并把这个对象的__proto__指向传入fn的原型
2、把函数中的this指向创建的新对象,并让函数执行
3、函数执行返回的是否是对象,如果是则返回,如果不是则返回新建的对象;
function _new(fn){
let obj = Object.create(fn.prototype);
let arg = [].slice.call(arguments,1);
let res = fn.apply(obj,arg);
return res instanceOf Object ? res : obj;
}
8、Recat中setState更新(同步异步)
这里的异步代表的是多个state更新会合并到一起进行批量更新;
如果是由React引发的事件处理(比如onClick点击事件),调用setState不会同步更新this.state,除此之外的setState调用会同步更新this.state。所谓”除此之外”指的是绕过React通过原生的addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用在
React的setState函数中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放在队列中等待批量更新,而isBatchingUpdates默认为false,表示setState会同步更新this.state,但是还有一个batchedUpdates函数,这个函数会把isBatchingUpdates改为true,而当React在调用事件处理函数之前就会先调用batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state
9、图片转base64
//网图转base64
function getBase64(url,cb) {
var img = new Image();
img.crossOrigin = 'Anonymous';//使用跨域图像
img.onload = function() {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var width = img.width, height = img.height;
canvas.width = width;
canvas.height = height;
context.drawImage(img,0,0,width,height);
var src = canvas.toDataURL("image/png");
cb(src);
};
img.src = url;
}
//本地图片转base64
function getBase64(file,cb) {
var reader = new FileReader();
reader.onload = function(){
cb(this.result);
}
reader.readAsDataUrl(file);
}
10、上传图片前选择并预览
FileReader 方法预览
//本地图片转base64
function getBase64(file,cb) {
var reader = new FileReader();
reader.onload = function(){
cb(this.result);
}
reader.readAsDataUrl(file);
}
createObjectURL方法预览
function getObjectURL(file) {
var url = null ;
// 浏览器兼容处理
if (window.createObjectURL != undefined) { // basic
url = window.createObjectURL(file) ;
} else if (window.URL != undefined) { // mozilla(firefox)
url = window.URL.createObjectURL(file) ;
} else if (window.webkitURL != undefined) { // webkit or chrome
url = window.webkitURL.createObjectURL(file) ;
}
return url ;//转化出的图片为blob模式图片
}
11、ES5/ES6的继承除了写法以外还有什么区别
继承的机制不同
ES5:先新建子类的实例对象this,再将父类的属性加到子类上,由于父类的内部属性无法获取,导致无法继承原生的构造函数。(比如,Array构造函数有一个内部属性[[DefineOwnProperty]],用来定义新属性时,更新length属性,这个内部属性无法在子类获取,导致子类的length属性行为不正常。)
ES6:先有父类构造出实例对象this,然后在子类的构造函数中修改this,允许继承原生构造函数定义子类, 因为ES6是先新建父类的对象this,然后再用子类的构造函数修饰this,使得父类的所有行为都可以继承。
12、事件循环在浏览器和node中的区别
浏览器中:
执行所有
同步js代码→ 执行所有微任务→渲染页面→执行一个宏任务→执行宏任务产生的微任务→渲染页面→执行下一个宏任务→执行宏任务产生的微任务→循环……
node中:
node10以前
执行一个阶段的所有任务→执行完
nextTick队列里的任务→执行所有的微任务node11以后
和浏览器做了统一
13、模块化进程
IIFE:最开始采用自执行函数形成私有作用域来编写模块化,避免变量冲突
AMD:采用requireJs编写模块化,特点:必须提前进行声明
CMD:采用Sea.js来编写模块化,特点:可以动态导入模块
CommonJS:nodeJS中自带的模块化(require的导入方式)
UMD:兼容AMD,CommonJS模块化语法;
ES6 Modules:ES6引入的模块化,支持import导入另一个JS
14、发送数据埋点时为什么使用1×1像素的透明gif图?
1、gif图片的体积最小
2、没有跨域问题(image支持 )
3、图片请求不占用ajax请求限额;
4、不会阻塞页面加载,只需要new Image()就行了,通过onerror和onload检测发送状态;
15、DOCTYPE的作用
告诉浏览器的解析器,用什么文档标准来解析文档。如果DOCTYPE不存在或者格式不正确,文档将以兼容模式呈现。
16、兼容模式和标准模式的区别
标准模式的渲染方式和JS引擎的解析方式都以该浏览器最新的标准去运行。在兼容模式中,页面以宽松的向后兼容的方式显示,模拟老式浏览器的行为以防站点无法工作。
17、为什么HTML5不需要DTD?
DTD的作用是用来定义文档中的规则。因为HTML5以前都是基于SGML的,所以需要通过制定DTD来制定文档的规则。而HTML5不是基于SGML的,所以不需要DTD;
18、前端性能优化
页面内容方面
1、使用CSS雪碧图、图片小于1M时,使用base64来减少HTTP请求,避免请求过多。
2、通过设置缓存策略,对常用不变的资源进行缓存
3、非必要资源采用延迟加载的方式来请求。
4、使用代码移除(Tree-shaking**),作用域提升(Scope hoisting)和代码分割(Code-splitting)来减少有效负载;
服务器方面
1、使用CDN服务,来提高用户对于资源的请求速度
2、服务器开启Gzip、Deflate等方式对于传输的资源进行压缩,减小文件体积
3、尽可能减少cookie的大小,并且通过将静态资源分配到非主域名下,来避免请求静态资源时携带不必要的Cookie(也叫cookie隔离)