递归和迭代的区别,优缺点?
递归
定义:栈机制实现,每深入一层,都要去占用一块栈数据区域。
- 优点:简洁,层层推进,分解问题。
- 缺点:易造成内存溢出
迭代
定义
- 优点:
- 缺点:使用比较困难,不易理解。
区别:
- 理论上递归和迭代时间复杂度方面是一样的,但实际应用中(函数调用和函数调用堆栈的开销)
递归比迭代效率要低
。 - 递归的优点 大问题转化为小问题,可以减少代码量,同时代码精简,可读性好, 缺点就是,递归调用浪费了空间,而且递归太深容易造成堆栈的溢出。迭代的好处 就是代码运行效率好,因为时间只因循环次数增加而增加,而且没有额外的空间开销, 缺点就是代码不如递归简洁
/*
迭代 n的阶层
*/
function f(n){
if(0===n) return 0;
let z=1;
for(let i=1; i<=n; i++){
z *= i;
}
return z;
};
/*
递归 n的阶层
*/
function f2(n){
if(1===n) return 1;
return n*f2(n-1);
};
执行上下文
定义:
JavaScript 代码的整个执行过程,分为两个阶段,代码编译阶段
与代码执行阶段
。编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定。执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。
- 执行上下文栈
function first() {
second();
console.log(1);}
function second() {
third();
console.log(2);}
function third() {
console.log(3);}
first(); // 3 2 1
-
代码执行前创建全局执行上下文推入执行栈ECStack = [globalContext];
-
first调用 创建first函数执行上下文推入栈ECStack.push(firstContext);
-
first调用了 second ,创建second函数执行上下文推入栈// 等待second 执行完毕再输出 1ECStack.push(secondContext);
-
second 调用了third,创建third函数执行上下文推入栈// 等待third执行完再输出 2ECStack.push(thirdContext);
-
third执行完毕,输出3,弹出栈ECStack.pop(thirdContext);
-
second执行完毕,输出2,弹出栈ECStack.pop(secondContext);
-
first执行完毕,输出1,弹出栈ECStack.pop();// 此时执行栈中只剩下一个全局执行上下文
以下代码输出什么,为什么?
try {
(async function() { a().b().c() })()
}
catch (e) {
console.log(`执行出错:${e.message}`)
}
Promise {<rejected>: ReferenceError: a is not defined
at <anonymous>:2:25
at <anonymous>:2:39}
VM61:2 Uncaught (in promise) ReferenceError: a is not defined
try
定义:语句标记要尝试的语句块,并指定一个出现异常时抛出的响应。
注:try---catch 捕获不到异步
操作
try语句包含了由一个或者多个语句组成的try块, 和至少一个catch块或者一个finally块的其中一个,或者两个兼有, 下面是三种形式的try声明:
- try...catch
- try...finally
- try...catch...finally
try {
try {
throw new Error("oops");
}
catch (ex) {
console.error("inner", ex.message);
throw ex;
}
finally {
console.log("finally");
}
}
catch (ex) {
console.error("outer", ex.message);
}
result:
"inner opps"
"finally"
"outer opps"
任何给定的异常只会被离它最近的封闭 catch 块捕获一次。
当然,在“inner”块抛出的任何新异常 (因为 catch 块里的代码也可以抛出异常),将会被“outer”块所捕获。
以下代码将输出啥?
let person = { name: "Lydia" };
const members = [person];
person = null;
console.log( members ) // [ { name: 'Lydia'} ]
解释:当两个对象彼此相等时,他们会通过引用进行交互。
但当你将引用一个从变量分配至另一个变量时,其实只是执行了一个复制
操作.
基本数据类型:String、Number、Boolean、null、undefined
按值访问,因为可以操作保存在变量中的实际值。存在栈中
引用数据类型 Object 、 Array、Function、Data....
在栈内存中保存的是对象在堆内存的引用地址。通过这个引用地址查找数据。 栈:引用地址 堆:实际值
注意:当一个对象重新赋值为一个新对象时,并不会影响它之前引用它的对象
, 含义就是这个对象 新开辟了一个空间。
以下代码?
const firstPromise = new Promise((res, rej) => {
setTimeout(res, 500, "one");
});
const secondPromise = new Promise((res, rej) => {
setTimeout(res, 100, "two");
});
Promise.race([firstPromise, secondPromise]).then(res => console.log(res))
result: "two"
原因: 宏任务和微任务的区别 Promise.race() ;
-
哪个结果获得快,就返回哪个的结果,不管结果本身的成功状态还是失败状态
setInterval
setInterval(() => console.log('Hi'), 1000) 返回一个 唯一的id,可用于 clearInterval 关闭定时器
?
(() => {
let x, y
try {
throw new Error()
} catch (x) {
(x = 1), (y = 2)
console.log(x)
}
console.log(x)
console.log(y)
})()
结果: 1 undefined 2 原因:
- catch 代码块接收参数 x。
- 这与之前定义的变量 x 不同 。这个 x 是属于 catch 块级作用域的。
- 然后,我们将块 级作用域中的变量赋值为 1,同时也设置了变量 y 的值。
- 现在,我们打印块级作用域中的变量 x,值为 1。catch 块之外的变量 x 的值仍为 undefined, y 的值为 2。当我们在 catch 块之外执行 console.log(x) 时,返回 undefined,y 返回 2。
以下代码?
const a = {}
const b = { key: 'b' }
const c = { key: 'c' }
a[b] = 123
a[c] = 456
console.log(a[b])
结果: 456 原因:
- Object的key值只有String和Symbol类型。其他类型会隐式转换为string类型。所以a[b]和a[c]其实都等价于a[object].所以456会覆盖123.
以下代码 ?
const obj = { a: 'one', b: 'two', a: 'three' }
console.log(obj)
结果:{ a:"three", b: 'two'} 原因:后面属性值,重复会覆盖前面
以下代码 ?
function getAge(...args) {
console.log(typeof args)
}
getAge(21)
结果:Object 原因:[ 21 ]
以下代码 ?
function Foo(){
this.getName = function (){
console.log(3) ;
return {
getName: getName
}
}
getName = function (){
console.log( 1) ;
}
return this
}
Foo.getName = function (){
console.log(2)
}
Foo.prototype.getName = function (){
console.log(6)
}
var getName = function (){
console.log(4)
}
function getName(){
console.log(5)
}
Foo.getName() ; // 2
getName() ; // 4
console.log(Foo()) ; // window
Foo().getName() ; // 1
getName() ; // 1
new Foo.getName() ; // 2
new Foo().getName() ; // 3