JavaScript 面试题
一、作用域和作用域链
1、块级作用域
2、函数作用域
3、全局作用域
作用域链:当前作用域链查找所需变量,这个作用域没找到,就去父级作用域找,直到找到window,形成作用域链
同一作用域重复声名 | 提升作用域顶部 | |
let | × | × |
const | × | × |
var | √ | √ |
二、说一说this?
执行上下文属性,指向最后一次调用这个方法的对象
箭头函数没有this
改变this指向的方法:
call(this,x,x,x)
bind(this,x,x,x) =>不会立即执行
apply(this,[x,x,x])
三、什么是Promisse?
解决回调地狱问题,异步编程解决方案
状态:pending进行中,resolved已完成,reject已拒绝
缺点:无法取消,创建立即执行
四、DOM和BOM?
DOM:文档对象模型
BOM:浏览器对象模型(location,navigator)
五、ajax的理解?
创建一个XMLHttpRequest对象
open,打开链接
发送请求
ajax | axios | fetch |
XMLHttpRequest封装 | 基于Promisse封装 | 基于Promisse设计,没有XMLHttpRequest,原生js |
服务器400,500不会reject,网络报错才会抛出异常 |
六、原型、原型链
原型:每个对象都有__proto__属性,指向了创建当前对象的构造函数原型
原型链:当在对象查找某个属性,不存在时,就在原型对象去找,这个对象又有自己的原型,形成原型链
Object.prototype.__proto__ = null
类型判断:
typeof: 基本类型
instance of: 可以判断对象类型,判断对象原型中是不是能找到prototype,判断是构造函数
Object.prototype.toString.call(x) 获取数据类型
七、async,await?
async返回一个promise对象
八、创建对象方式?
工厂模式,原型模式,构造函数
九、对象继承的方式有哪些?
原型链
寄生继承
构造函数
组合继承
十、内存泄漏?
意外的全局变量和一直在内存中
定时器,闭包
十一:什么是闭包
通过作用域链内部函数变量访问外部函数变量,现在自身作用域找,没有找到就会向上层作用域找,形成了闭包
优点:数据私有化,不被全局污染
缺点:由于变量无法回收,使用频繁,内存损耗太大,内存泄漏
主要:防抖和节流等
十二、深浅拷贝?
这里有个概念就是基本类型和引用类型
基本类型 | 引用类型 |
string、boolean、number、undefined、null、symbol | Array、Object、Date、RegExp、function |
存在栈中 | 存在堆中 |
自动分配,自动释放 | 大小不固定,手动释放或垃圾回收 |
浅拷贝: 对象的浅拷贝,拷贝的仅仅是“引用地址”,不是值
let a = [0,1,2,3,4],
b = a;
console.log(a === b); // true
a[0]=1;
console.log(a, b); // a: [1,1,2,3,4] b:[1,1,2,3,4]
深拷贝:把引用地址和值一起拷贝过来,一个对象的值改变,另一个对象的值不受影响
注意:在 JavaScript 中,当对象中的属性值为 undefined
时,在执行 JSON.stringify(obj)
时,这些属性会被忽略,因此最终的 JSON 字符串中不会包含这些属性。对象中的属性值为 null 没问题
深浅拷贝方法:
JSON.parse(JSON.stringify(obj))
JQuery 的 $.extend()
Object.assign(target, ...sources) 一层为深拷贝,多层为浅拷贝
ES6 的扩展运算符(...)一层为深拷贝,多层为浅拷贝
lodash: _.deepClone()
JSON.parse(JSON.stringify(obj))注意问题:
对象属性值为函数,序列化后该属性会丢失。
let obj = {
name: 'String',
foo: function () {
console.log(`${ this.name }是一个小菜鸟!`)
}
}
console.log(JSON.stringify(obj)); // {"name":"String"}
'
运行运行
对象属性值为undefined,序列化后该属性会丢失。
let obj = {
name: undefined
}
console.log(JSON.stringify(obj)); // {}
'
运行运行
对象属性值为正则表达式,序列化后该属性为{}
let obj = {
name: 'String',
zoo: /^i/ig,
foo: function () {
console.log(`${ this.name }是一个小菜鸟!`)
}
}
console.log(JSON.stringify(obj)); // {"name":"String","zoo":{}}
'
运行运行
数组含对象,对象属性值为undefined,序列化后该属性也会丢失。
let arr = [
{
name: undefined
}
]
console.log(JSON.stringify(arr)); // [{}]
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;
}
十三、new 发生了什么?
1、创建了新对象
2、链接到原型
3、绑定this
4、返回新对象