第一道:用递归实现1-100数字的相加
// 用递归实现1-100数字的相加
function add(num1, num2) {
var num = num1 + num2;
if (num2 < 100) {
return add(num, num2 + 1)
} else {
return num
}
}
add(1, 2); // 5050
第二道:Object.defineProperty() (对象属性定义)
- 语法:Object.defineProperty(obj, prop, descriptor)
- 参数一:obj ( 要定义属性的对象 )
- 参数二:prop ( 要定义或修改的属性的名称或 Symbol )
- 参数三:descriptor ( 要定义或修改的属性描述符 )
descriptor分别有以下描述符:(enumerable 、enumerable、value、writable、get、set)具体作用参考MDN描述解释,主要看一下enumerable描述符。
const obj = {
name: 'chuan',
message: 'Hello World'
}
Object.defineProperty(obj, 'name', {
enumerable: false
})
for (let item in obj) {
console.log(item); // message
}
为什么上面 item 只打印了 message一个属性名?那其实就是因为我们为obj对象的描述符 enumerable 设置为false的原因,判断一个属性是否可枚举的一个标准是看:对象的这个属性能否被for in循环给遍历出来,当我们设置为false的时候表示不可枚举,那么就不会被for in遍历出来。
而描述符 get,set就是在vue2中我们经常能够知道的,数据双向绑定的实现,对数据进行劫持。在vue3数据双向绑定原理从Object.defineproperty 变更为了 Proxy 代理。
第三道:Proxy
描述:Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
简单来说就是对目标对象的操作之前提供了拦截,可以修改一些操作的默认行为,可以不直接操作对象背身,而是通过操作对象的代理对象间接来操作对象。
// 简单举个例子
const obj = {
name: 'chuan'
}
let proxyObj = new Proxy(obj, {
get: function (target, prop) {
console.log(prop);
return prop in target ? target[prop] : 18
},
set: function (target, prop, value) {
target[prop] = value;
}
})
console.log(proxyObj.name); // 'chuan'
console.log(proxyObj.age); // 18
proxyObj.age = 20;
console.log(proxyObj.age) // 20
这儿推荐一篇比较好的文章可以详细看看proxy的作用和亮点(初探 Vue3.0 中的一大亮点——Proxy ! - 简书)
第四道:怎么理解Promise对象?
Promise 对象有以下两个特点:
- 对象的状态不受外界影响。Promise 对象共有三中状态 pending、fulfilled、rejected。状态值只会被异步结果决定,其他任何操作无法改变。
- 状态一旦成型,就不会再变,且任何时候都可得到这个结果。状态值会由 pending 变为 fulfilled 或 rejected,这时即为resolved。
Promise 的缺点有如下三个缺点:
- Promise 一旦执行便无法被取消;
- 不可设置回调函数,其内部发生的错误无法捕获。
- 当处于 pending 状态时,无法得知其具体发展到了哪个阶段。
Promise 中常用的方法有:
-
Promise.prototype.then():Promise实例的状态发生改变时,会调用then内部的回调函数。then方法接受两个参数(第一个为resolved状态时时执行的回调,第一个为rejected状态时时执行的回调)。
-
Promise.prototype.catch():.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
-
Promise.all:当有一个ajax请求,它的参数需要另外2个甚至更多请求都有返回结果之后才能确定,那么这个时候,就需要用到Promise.all来帮助我们应对这个场景。Promise.all接收一个Promise对象组成的数组作为参数,当这个数组所有的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。
-
Promise.race:与Promise.all相似的是,Promise.race都是以一个Promise对象组成的数组作为参数,不同的是,只要当数组中的其中一个Promsie状态变成resolved或者rejected时,就可以调用.then方法了。而传递给then方法的值也会有所不同,大家可以再浏览器中运行下面的例子与上面的例子进行对比。
第五道:JavaScript中的垃圾回收机制
参考回答:
- 必要性:由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他 们进行动态的存储分配。JavaScript 程序每次创建字符串、数组或对象时,解释器都必 须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以 便他们能够被再用,否则,JavaScript 的解释器将会消耗完系统中所有可用的内存,造 成系统崩溃。
这段话解释了为什么需要系统需要垃圾回收,JS 不像 C/C++,他有自己的一套垃圾回收 机制(Garbage Collection)。JavaScript 的解释器可以检测到何时程序不再使用一个对象 了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用 的内存释放掉了。例如:
var a="hello world";
var b="world";
var a=b;
//这时,会释放掉"hello world",释放内存以便再引用 垃圾回收的方法:标记清除、计数引用。
- 标记清除:这是最常见的垃圾回收方式,当变量进入环境时,就标记这个变量为”进入环境“,从逻 辑上讲,永远不能释放进入环境的变量所占的内存,永远不能释放进入环境变量所占用 的内存,只要执行流程进入相应的环境,就可能用到他们。当离开环境时,就标记为离 开环境。
-
引用计数法:另一种不太常见的方法就是引用计数法,引用计数法的意思就是每个值没引用的次数, 当声明了一个变量,并用一个引用类型的值赋值给改变量,则这个值的引用次数为 1,; 相反的,如果包含了对这个值引用的变量又取得了另外一个值,则原先的引用值引用次 数就减 1,当这个值的引用次数为 0 的时候,说明没有办法再访问这个值了,因此就把 所占的内存给回收进来,这样垃圾收集器再次运行的时候,就会释放引用次数为 0 的这 些值。
第六道:如何理解前端模块化?
前端模块化就是复杂的文件编程一个一个独立的模块,比如 JS 文件等等,分成独立的 模块有利于重用(复用性)和维护(版本迭代),这样会引来模块之间相互依赖的问题, 所以有了 commonJS 规范,AMD,CMD 规范等等,以及用于 JS 打包(编译等处理)的 工具 webpack