前端面试题总结
事件委托的优缺点
- 优点:
- 减少事件注册,节省内存
- 减少对dom更新的操作,只需关注委托的元素就行
- 缺点
- 基于冒泡事件,对于
onfoucs
和onblur
等事件不支持 - 层级过多,冒泡过程中,可能会被某层阻止掉(建议就近委托)
- 基于冒泡事件,对于
DOM和BOM区别
- DOM是文档对象模型,DOM的最根本对象是document:
- BOM是浏览器对象模型,BOM的最核心对象是window对象:window上的方法(open、size);location; history; screen
JavaScript原型,原型链 ? 有什么特点?
- 每个对象都会在其内部初始化一个属性,就是prototype(原型),存放共有属性和方法,当我们访问一个对象的属性时;
- prototype对象默认会开一个对内存,对内存自带constructor,指向当前函数
- 如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,于是就这样一直找下去,也就是我们平时所说的原型链的概念
- 原型链是由原型对象组成;
__proto__
是每个实例上都有的属性,prototype 是构造函数的属性,这两个并不一样,但__proto__
和prototype
指向同一个对象- 关系:instance.constructor.prototype = instance._proto
🔔: jquery,zepto的插件机制就是放方法放在他们的原型上($.fn)
箭头函数和普通(function)函数的区别
箭头函数是普通函数更优雅的简写,和普通函数的区别是:
- 函数体内的
this
对象是函数定义式所在作用域的对象,而不是执行是的对象 - 函数内没有指向当前函数的
arguments
对象,在浏览器环境下输入arugments
会报错, - 不能使用
new
命令,因为new的实现:- 没有自己的
this
,所以无法调用call
,apply
- 没有prototype属性,因为在new命令执行是需要讲函数的
prototype
指向新对象的_proto_
- 没有自己的
ES6 class和普通构造函数的区别?
- Class类似于不同构造函数的语法糖
- Class不用
new
时无法调用的,会报错 - Class不存在变量提升(hoist),这一点与ES5完全不同
- Class实现继承更易读易理解
ES5和ES6继承的区别
- es5:
Sub.__proto__ === Super.prototype
;es6:Sub.__proto__ === Super
,在babel对es6对转换let Sub = Object.create(Super)
- ES5 和 ES6 子类 this 生成顺序不同;es5是先生成子对象实例;es6是先生成父类实例
base64
,体积会增加大概1/3?
为什么文件转原文;总结:
- 将每三个字节作为一组,一共是24个二进制位。
- 将这24个二进制位分为四组,每个组有6个二进制位。
- 在每组前面加两个00,扩展成32个二进制位,即四个字节。
- 最后在将二进制转换为对应的编码,这就是Base64的编码值
参考
堆中垃圾回收算法引用计数法
的算法- 优点:立即回收
- 缺点:时间复杂度比较高、无法回收循环引用的对象
标记清除
算法- 优点:可以解决循环引用问题
- 缺点:不会立即回收垃圾对象,产生空间碎片化,不能得到空间最大化效率的使用
- V8引擎的垃圾回收策略主要基于分代式垃圾回收机制;根据存活时间分为
新生代
和老生代
;新生代
即存活时间较短的对象,使用副垃圾回收器,老生代
即存活时间较长或者占用空间较大的对象(25%),使用主垃圾回收器新生代
用Scavenge算法
,即采用复制的方式实现的垃圾回收算法;当一个对象通过多次复制或生命周期较长或to空间不足时则会被分配到老生代中老生代
应用标记清除的方式进行垃圾回收- 清理后会产生不连续的空间,此时会进行
标记-整理
- 由于老生代要处理的数据比较多,会总成页面卡顿;所以会进行
增量标记算法
,及将任务分成多个小人物,穿插进js中去执行
- 清理后会产生不连续的空间,此时会进行
ES6 模块与 CommonJS 模块的差异
- CommonJS 模块输出的是一个值的浅拷贝,首次执行后就会缓存,再次加载只返回缓存结果,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,可以实现动态引入;ES6 模块是编译时输出接口,因为es6是静态解析,所有可以实现tree-shaking功能
- ES6 输入的模块变量是只读的,无法进行重新赋值,
- CommonJs 动态语法,可以写在判断里面,ES6模块只能放在顶部
AMD与CMD区别
- AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块 (requireJS)
define(['dep'], function(dep) {
// your_export;
});
- CMD推崇就近依赖,只有在用到某个模块的时候再去require (seajs)
define(function(require, exports, module) {
const dep= require('dep');
exports.your_export = /* your_export */;
})
npm 模块安装机制
- 发出npm install命令
- 查询node_modules目录之中是否已经存在指定模块
- 若存在,不再重新安装
- 若不存在
- npm 向 registry 查询模块压缩包的网址
- 下载压缩包,存放在根目录下的.npm目录里
- 解压压缩包到当前项目的node_modules目录
宏任务&微任务
- 宏任务
# | I/O | setTimeout | setInterval | setImmediate | requestAnimationFrame | UI Rendering |
---|---|---|---|---|---|---|
浏览器 | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ |
node | ✅ | ✅ | ✅ | ✅ | ⬜ | ⬜ |
- 微任务
# | process.nextTick | MutationObserver | promise |
---|---|---|---|
浏览器 | ⬜ | ✅ | ✅ |
node | ✅ | ⬜ | ✅ |
为什么Promise要引入微任务?
问题的本身就是promise的回掉什么时候执行;
- 如果放在同步任务中则会造成
阻塞,造成cpu利用率低
; - 如果放在宏任务队列则当
任务队列非常长;则会造成回掉迟迟无法执行;导致应用卡顿
; - 而放在微任务中则可以解决
cpu浪费和回掉函数执行的实时性
堆和栈的区别
- 申请方式不同
- 栈是系统自动分配,而堆则是程序员自己申请空间
- 申请大小不同
- 栈是在系统自动分配后大小就固定好了的,能从栈获得的空间较小。而堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大
参考
纯函数介绍满足以下调教的函数:
- 相同输入总是会返回相同的输出
- 不产生副作用
- 不依赖于外部状态
cmemoize函数及使用
memoize简单讲就是将计算结果缓存起来;对于需要大量的计算和递归操作来说,可以加快速度;比如:阶乘、斐波那契数组
代码执行
1.输出结果
const a = {}
const b = { key: 'b' }
const c = { key: 'c' }
a[b] = 123
a[c] = 456
console.log(a[b])
答案
答案: 456
原因:对象的键被自动转换为字符串
,然而,当字符串化一个对象时,它会变成 "[Object object]"
。因此这里说的是,a["Object object"] = 123
。然后,c 是另外一个对象,这里也有隐式字符串化,于是,a["Object object"] = 456。, 所以最后输出结果就是456
2.单击该段落时,日志输出结果
<div onclick="console.log('div')">
<p onclick="console.log('p')">
Click here!
</p>
</div>
答案
答案: p
,div
原因:在事件传播期间,有三个阶段:捕获、目标和冒泡
。默认情况下,事件处理程序在冒泡阶段
执行(除非将 useCapture
设置为 true
)。它从嵌套最深的元素向外传播
3.JavaScript里什么情况下a==!a为true呢
答案
答案: []
原因:比较规则如下:
- 如果一边是布尔值,则转成数值
- 如果一边是字符串则转成数值
- 如果一边是对象,则转成原始值(例如:
[] == false
判断);步骤(内置的[ToPrimitive]函数)- 调用
valueOf()
,如果是原始值则进行比较 - 调用
toString()
,如果是原始值则进行比较 - 否则最后则抛出
TypeError
异常
比较时还要遵循以下规则
- 调用
- null 和 undefined 是相等的。
- 要比较相等性之前,不能将null和undefined转换成其他任何值
- 如果有一个操作数是NaN,则相等操作符返回false, 而不相等操作符则返回true, NaN != NaN
4. arguments相关运行结果
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 2,
show: function(fn) {
this.length = 3;
fn();
// arguments.number = 1000
arguments[0]();
}
};
obj.show(fn);
答案
答案: 10、1
原因: 可以将arguments[0]()
看成arguments.0()
;所以这里的this
其实是函数的参数,也就是 arguments
的个数;
5.打印输出结果
function foo() {
getName = function() {console.log(1);}
return this
}
foo.getName = function(){console.log(2);}
foo.prototype.getName = function(){console.log(3);}
var getName = function(){console.log(4);}
function getName() {
console.log(5);
}
foo.getName()
getName()
foo().getName()
getName()
new foo.getName()
new foo().getName()
new new foo().getName()
答案
答案: 2,4,1,1,2,3,3
原因:无
小红包免费领
小礼物走一走
部分内容来源于网络,如有侵权,请留言或联系994917123@qq.com;访问量:waiting;访客数:waiting