前一篇文章是关于 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 很难被调试
- 双向绑定不利于代码重用