js中一些常见的面试问题。
1.闭包是什么?
一个内部函数可以访问包含它的外部函数中的变量。
延长变量的生命周期,创建私有环境
作用:
①能够访问函数定义时所在的词法作用域(阻止其被回收)。
②私有化变量。
③模拟块级作用域。
④创建模块
缺陷:
①增大内存使用量,使用不当很容易造成内存泄露。
②闭包对脚本性能具有负面影响,包括处理速度和内存消耗。闭包常驻内存
2.如何深拷贝?
最简单方法:JSON.parse(JSON.stringify(obj))
缺点:方法(函数)不能深拷贝
自定义方法:
// 深拷贝
function deepCopyTwo(obj) {
if(obj === null) return null
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj == 'object') {
for (const key in obj) {
//判断obj子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepCopyTwo(obj[key]);
} else {
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
return objClone;
}
3.如何预编译?
题目:说出console.log()输出的值
function fn(a, c) {
console.log(a) // function a() {}
var a = 123
console.log(a) // 123
console.log(c) // function c() {}
function a() {}
if (false) {
var d = 678
}
console.log(d) // undefined
console.log(b) // undefined
var b = function () {}
console.log(b) // function b() {}
function c() {}
console.log(c) // function c() {}
}
fn(1, 2)
步骤:
1.创建AO对象
ao: {}
2.找到形参和变量的声明,作为AO对象的属性名,值为undefined
ao: {
a: undefined
c: undefined
d: undefined
b: undefined
}
3.实参和形参相统一
ao: {
a: 1
c: 2
d: undefined
b: undefined
}
4.找函数式声明,会覆盖变量
ao: {
a: function a() {}
c: function b() {}
d: undefined
b: undefined
}
4.call和apply的作用?
1.改变this指向。
let obj = {
a: 123,
};
function fn(b) {
console.log(this.a); // 123
console.log(b);
}
fn.call(obj, 1);
fn.apply(obj, [2]);
2.数组和对象类型判断
Object.prototype.toString.call([]); -----> [object Array]
Object.prototype.toString.call({}); -----> [object object]
Object.prototype.toString.call(123); -----> [object Number]
3.伪数组转化为数组
数组 = Array.prototype.slice.call(伪数组);
5.隐式转换?
console.log([] == 0) // true
console.log(![] == 0) // true
// 1.关系运算符会将其他数据类型转换成数字
// 2.逻辑非会将其他数据类型用Boolean转换
console.log([] == ![]) // true // Number([].valueOf().toString()) => '' => 0
console.log([] == []) // false
// 1.空数组代表一个空间
console.log({} == !{}) // false // Number({}.valueOf().toString()) => '[object Object]' => NaN
console.log({} == {}) // false
6.new操作符做了什么?
1.首先创建一个新的对象。
2.将新对象的原型_proto_指向构造函数的prototype属性。
3.让构造函数的this指针指向这个新对象。并且执行构造函数的代码(为这个新对象添加属性)。
4.判断构造函数的返回值,如果是值类型返回创建的对象,如果是引用类型则返回这个引用类型的对象。
class Person {
constructor(a, b) {
this.a = a;
this.b = b;
// 引用类型
return Object.create({ a: 3, b: 4, c: 5 });
// 值类型
// return 123;
}
}
let person = new Person(1, 2);
// 返回值是引用类型
console.log(person.a); // 3
console.log(person.b); // 4
console.log(person.c); // 5
// 返回值是值类型
console.log(person.a); // 1
console.log(person.b); // 2
console.log(person.c); // undefined
7.事件循环(EventLoop)?
宏任务:
- setTimeout
- setInterval
- setImmediate (Node独有)
- requestAnimationFrame (浏览器独有)
- I/O
- UI rendering (浏览器独有)
微任务:
- process.nextTick (Node独有)
- Promise
- Object.observe
- MutationObserver
执行流程:
1.执行全局Script同步代码,这些代码中有同步语句和异步语句。同步语句放到调用栈执行。异步语句又分为宏任务和微任务,分别放到宏任务队列和微任务队列中等待执行。
2.执行完毕后,调用栈清空。
3.从微任务队列中取出队首的微任务,放到调用栈中执行。执行过程中遇到的宏任务和微任务,依次放到两队队尾等待执行。
4.重复3操作,直到将所有微任务队列中任务全部执行完毕。
5.微任务队列执行完毕后,取出宏任务队列中位于队首的任务执行,执行过程中遇到的宏任务和微任务,依次放到两队队尾等待执行。
6.宏任务执行完毕后,重复3-5操作,直到所有任务执行完毕。
8.script 标签中 defer 和 async 的区别?
script:会阻碍html文件的解析,只有加载并执行完脚本才会继续解析html。
async script:解析html过程中进行脚本的异步加载,加载成功后立即执行,有可能会阻断html解析。
defer script:完全不会阻碍html解析,解析完成后按顺序执行脚本。
9.什么是事件委托(代理)?有什么好处?
原理:不给每个子节点单独设置事件监听,而给它们的父节点设置事件监听,然后利用事件冒泡原理处理每个子节点的事件。
优点:
1.减少内存消耗和 dom 操作。
2.动态绑定事件。