赞
踩
递归是一种解决复杂的未知次数循环的问题的解决方案。其实表现是在一个函数内部自己调用自己。
如:求n的阶乘 – 求n的阶乘可以不使用递归实现,只是用来演示递归的使用
function factorial(n){
return n == 1 ? 1 : n * factorial(n-1)
}
我们明显看出在factorial
函数内部调用了自己。这就是函数递归。
此时如果我们调用这个函数,求5的阶乘,其调用过程如果
===> factorial(5)
===> 5 * factorial(4)
===> 5 * 4 * factorial(3)
===> 5 * 4 * 3 * factorial(2)
===> 5 * 4 * 3 * 2 * factorial(1)
===> 5 * 4 * 3 * (2 * 1)
===> 5 * 4 * (3 * 2)
===> 5 * (4 * 6)
===> 5 * 24
===> 120
所以这是一个层层递进,层层回归的过程 —— 简称 递归
上面只是一个简单的示例。在面试题中常见的场景主要如下:
function deepClone(value) {
if (value == null) return value;
if (typeof value !== 'object') return value;
if (value instanceof RegExp) return new RegExp(value);
if (value instanceof Date) return new Date(value);
// 我要判断 value 是对象还是数组 如果是对象 就产生对象 是数组就产生数组
let obj = new value.constructor;
for(let key in value){
obj[key] = deepClone(value[key]); // 看一看当前的值是不是一个对象
}
return obj;
}
var arr = [3, 1, 4, 6, 5, 7, 2]; function quickSort(arr) { if(arr.length == 0) { return []; } var cIndex = Math.floor(arr.length / 2); var c = arr.splice(cIndex, 1); var l = []; var r = []; for (var i = 0; i < arr.length; i++) { if(arr[i] < c) { l.push(arr[i]); } else { r.push(arr[i]); } } return quickSort(l).concat(c, quickSort(r)); } console.log(quickSort(arr));
var arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];
var newArray = [];
function getArray(array) {
array.forEach(function(e) {
if (typeof e === "object") {
getArray(e);
} else {
newArray.push(e);
}
});
}
getArray(arr);
function fun(n, o) { console.log(o); return { fun: function(m) { return fun(m, n); } }; } var a = fun(0); // ? a.fun(1); // ? a.fun(2); // ? a.fun(3); // ? var b = fun(0).fun(1).fun(2).fun(3); // ? var c = fun(0).fun(1); // ? c.fun(2); // ? c.fun(3); // ?
x = 1; var obj = { x: 2, dbl: function () { this.x *= 2; x *= 2; console.log(x); console.log(this.x); } }; // 说出下面的输出结果 obj.dbl(); var func = obj.dbl; func(); var funcBind = obj.dbl.bind(obj); funcBind();
function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName();
设计模式是所有的编程语言中都可以实现的一些高级技巧,现在大家代码量还少,可能还无法理解,所以不用尝试自己推导,而是把这些东西掌握使用就好。
学习设计模式,有助于写出可复用和可维护性高的程序
设计模式的原则是“找出 程序中变化的地方,并将变化封装起来”,它的关键是意图,而不是结构。
单例模式是指某个类永远只有一个实例对象,这个模式的好处是只有一个实例对象,那么所有的数据就可以在任何位置共享,便于管理这些需要共享的数据。
代码示例:
var _instance = null;
function Manager(){
if(!_instance){
_instance = new Manager();
}
return _instance;
}
class Manager{
constructor(){
if(Manager._instance){
return Manager._instance;
}
Manager._instance = this;
}
}
1. 定义
也称作观察者模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发 生改变时,所有依赖于它的对象都将得到通知
2. 核心
取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。
在JS中通常使用注册回调函数的形式来订阅
写法有很多,但是思想都一样: 先给对应的事件或者状态注册多个回调函数,当触发这个事件时,调用这些函数。
var event = { add : function(type,fn){ if(!this[type]){ this[type] = [] } this[type].push(fn) }, remove: function(type,fn){ var index = this[type].indexOf(fn); this[type].splice(index,1) }, trriger(type){ if(this[type]){ this[type].forEach(e=>{ e(); }) } } }
设计模式不仅仅只是这两种,详细参考
想要想明白异步编程的执行顺序,首先要知道js代码是如何执行的。此时有一个概念一定要先知道:执行栈
执行栈,也称“调用栈”,是一种拥有 后进先出 的数据结构,被用来存储代码运行时创建的所有执行上顺序。
当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行环境并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行环境并压入栈的顶部。
引擎会执行处于栈顶的执行环境的函数。当该函数执行结束时,执行环境从栈中弹出,控制流程到达当前栈中的下一个执行环境。
让我们通过下面的代码示例来理解:
console.log('全局环境开始');
let a = 10;
function first() {
console.log('函数1');
second();
console.log('两次回到函数1');
}
function second() {
console.log('函数2');
}
first();
console.log('全局环境结束');
上面的代码可以用这样的过程来理解
1.首先是全局的执行环境入栈
2.在全局环境下调用了first函数,再把first函数的环境压入栈中
3.在first函数里面调用了second函数,再把second函数的环境压入栈中
4.second执行完毕,于是把second的执行环境从栈中移除(先进后出,后入先出)
5.回到first的执行环境,再把fist的代码执行完成,从执行栈中再移除
6.最后把全局的执行环境也出栈,整个程序执行完成
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
具体来说,异步执行的运行机制如下。
1.所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
2.主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
3.一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
4.主线程不断重复上面的第三步。
于是就会形成下面这样一个执行模型
而在代码的执行过程中,我们还把所有的分为两个大类,宏任务和微任务
宏任务 | 微任务 |
---|---|
script环境 | Promise的then/catch回调 |
setInterval/setTimeout 定时器 | Object.observe(先忽略) |
requestAnimationFrame 浏览器的帧循环(先忽略) | Proxy(先忽略) |
UI Rendering 浏览器的UI渲染(先忽略) |
Event Loop
中,每一次循环称为tick
,每一次tick
的任务如下:
执行栈在执行完同步任务后,查看执行栈是否为空,如果执行栈为空,就会去执行Task
(宏任务),每次宏任务执行完毕后,检查微任务(microTask
)队列是否为空,如果不为空的话,会按照先入先出的规则全部执行完微任务(microTask
)后,设置微任务(microTask
)队列为null
,然后再执行宏任务,如此循环
以下面的代码为例:
console.log('1'); setTimeout(function() { console.log('2'); new Promise(function(resolve) { console.log('3'); resolve(); }).then(function() { console.log('4') }) }) new Promise(function(resolve) { console.log('5'); resolve(); }).then(function() { console.log('6') }) console.log('7')
一开始的执行过程如下
然后进入循环
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-97yvZzMV-1626498202286)(assets/image-20210331162756345.png)]
所以最终的结果为: 1,5,7,6,2,3,4
setTimeout(function () {
console.log('1');
})
new Promise(function (resolve) {
console.log('2');
for (var i = 0; i < 1000; i++) {
i == 99 && resolve();
}
console.log('3');
}).then(function () {
console.log('4');
})
console.log('5')
console.log('start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
new Promise((resolve) => {
console.log('promise')
resolve()
}).then(() => {
console.log('then1')
}).then(() => {
console.log('then2')
})
console.log('end')
console.log('start') setTimeout(() => { console.log('timer1') Promise.resolve().then(function() { console.log('promise1') }) }, 0) setTimeout(() => { console.log('timer2') Promise.resolve().then(function() { console.log('promise2') }) }, 0) Promise.resolve().then(function() { console.log('promise3') }) console.log('end')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。