前一篇文章是关于HTML/CSS面试题的,链接见这里,今天聊一聊一些高频出现的JavaScript面试题。
对原型和原型链是如何理解的?
- JavaScript由对象构成,每一个对象(除null外)都和另一个对象相关联(通过__proto__属性),“另一个对象”就是原型。即任何一个对象都有原型这个属性。
- 继承原型依靠"原型链"(prototype chain)模式来实现继承
- 所有 JavaScript 对象都从原型 (prototype) 继承属性和方法,可以尝试打印一下String.prototype、Array.prototype ...
- 日期对象继承自 Date.prototype。数组对象继承自Array.prototype。函数对象继承自Function.prototype。日期对象、数组对象和函 数对象都继承自Object.prototype,其位于原型继承链的顶端
- 可以用 object.prototype.name = value 修改自己创建的原型,不要修改 JavaScript 标准对象的原型
JavaScript解释器的执行顺序和原理
- Js引擎(浏览器)将执行的任务分为
同步任务
和异步任务
,同步任务就是在主线程上按顺序执行,上一个任务不完成,下一个任务就无法进行,是线程阻塞的。而异步任务则处于“任务队列”中,不会造成阻塞线程。 - 运行机制:程序开始后,主线程先执行同步任务,碰到异步任务先放到任务队列(TO DO LIST,又称事件队列)中,如setTimeout(),然后继续执行。等同步任务执行完毕,JS引擎便去查看任务队列有没有可以执行的异步任务,有的话,将异步任务转为同步任务,开始执行,执行完该同步任务后继续查看任务队列,这个过程是一直循环的,也就是所谓的事件循环。(通过任务队列,单线程的JS实现了所谓的"多线程")
-
加入宏任务,微任务:JS解释器执行顺序为,同步任务(先执行宏任务,在执行微任务),遍历异步队列,执行异步任务。
- 宏任务:由宿主环境发起,如
setTimeout
、setInterval
等 - 微任务:由JavaScript引擎发起的,如
Promise.then()
等
- 宏任务:由宿主环境发起,如
基础类型和引用类型在存储方式和拷贝上的区别?
存储方式:
- 基本数据类型:key和value存储在栈内存中
- 引用数据类型:key存在栈内存中,value存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值
拷贝:
- 深拷贝与浅拷贝的概念只存在于引用数据类型
- 浅拷贝只会将对象的各个属性进行依次复制,并不会进行递归复制,也就是说只会赋值目标对象的第一层属性。深拷贝不同于浅拷贝,它不只拷贝目标对象的第一层属性,而是递归拷贝目标对象的所有属性。
- 浅拷贝对于目标对象第一层为基本数据类型的数据,就是直接赋值,即「传值」;而对于目标对象第一层为引用数据类型的数据,就是直接赋存于栈内存中的堆内存地址,即「传址」,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变。而深复制则是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。
什么是闭包,如何利用闭包?
闭包
是指有权访问父作用域中的变量的函数
用处: 可以实现封装,属性私有化 / 防止污染全局变量
数组的for、forEach和map的区别是什么
- for循环可以中途跳出,而 forEach 不可以,break 命令或 return 命令都不能生效
- 相比普通的for循环,forEach的优势在于对稀疏数组的处理,会跳过数组中的空位
- forEach改变原数组,map不改变原数组而是返回一个新数组
如何写一个数据请求?
- 传统的AJAX请求,利用XMLHttpRequest发送请求,获取数据。为了兼容性,应该用jQuery的AJAX
$.ajax({
method: 'POST',
url: '/api',
data: { username: 'admin', password: 'root' }
})
.done(function(msg) {
alert( 'Data Saved: ' + msg );
});
- 在ES6中,新增了 fetch 方法:
fetch(url, {
method : 'get',
})
.then(response => response.json())
.then(res => console.log(res))
.catch(err => console.log("Oops, error", err))
- 利用第三方axios库:
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
用JavaScript代码实现斐波那契数列
斐波那契数列的排列是:1,1,2,3,5,8,13,21,34,55,89,144 ...
输入任意的index,返回对应位置的斐波那契数
实现如下:
getFibonacci = (n) => {
let e1 = 0;
let e2 = 1;
let target = 0;
for(let i=1; i<=n; i++){
e1 = e2;
e2 = target;
target = e2 + e1;
}
return target;
}
谈谈你对MVC、MVP和MVVM的理解,具体在写代码中的体验
Model-View-Controller
:M(数据保存)、V(用户界面)、C(业务逻辑)
- View 传送指令到 Controller
- Controller 完成业务逻辑后,要求 Model 改变状态
- Model 将新的数据发送到 View,用户得到反馈
- 所有的通信都是单向的
Model-View-Presenter
:
- 各部分之间的通信,都是双向的
- View 与 Model 不发生联系,都通过 Presenter 传递
- View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而Presenter非常厚,所有逻辑都部署在那里
Model-View-ViewModel
:
- 基本上与 MVP 模式完全一致,只是把Presenter变成了ViewModel
MVVM的优点:
- 双向绑定,当Model变化时,View和ViewModel会自动更新,保持了数据一致性
- 简化了控制器
- View的功能进一步强化,可以像Model一样有自己的ViewModel
- 可以对View或ViewController的数据处理部分抽象出来。减轻Model的负担
MVVM的缺点:
- 数据绑定使得bug很难被调试
- 双向绑定不利于代码重用
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。