面试之道

167 阅读4分钟

递归和迭代的区别,优缺点?

递归

定义:栈机制实现,每深入一层,都要去占用一块栈数据区域。

  1. 优点:简洁,层层推进,分解问题。
  2. 缺点:易造成内存溢出
迭代

定义

  1. 优点:
  2. 缺点:使用比较困难,不易理解。

区别:

  • 理论上递归和迭代时间复杂度方面是一样的,但实际应用中(函数调用和函数调用堆栈的开销)递归比迭代效率要低
  • 递归的优点 大问题转化为小问题,可以减少代码量,同时代码精简,可读性好, 缺点就是,递归调用浪费了空间,而且递归太深容易造成堆栈的溢出。迭代的好处 就是代码运行效率好,因为时间只因循环次数增加而增加,而且没有额外的空间开销, 缺点就是代码不如递归简洁

/*
迭代 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 代码的整个执行过程,分为两个阶段,代码编译阶段代码执行阶段。编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定。执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。

  1. 执行上下文栈
    
function first() {
  second();
  console.log(1);}
function second() {
  third();
  console.log(2);}
function third() {
  console.log(3);}

first(); // 3 2 1

  1. 代码执行前创建全局执行上下文推入执行栈ECStack = [globalContext];

  2. first调用 创建first函数执行上下文推入栈ECStack.push(firstContext);

  3. first调用了 second ,创建second函数执行上下文推入栈// 等待second 执行完毕再输出 1ECStack.push(secondContext);

  4. second 调用了third,创建third函数执行上下文推入栈// 等待third执行完再输出 2ECStack.push(thirdContext);

  5. third执行完毕,输出3,弹出栈ECStack.pop(thirdContext);

  6. second执行完毕,输出2,弹出栈ECStack.pop(secondContext);

  7. 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声明:

  1. try...catch
  2. try...finally
  3. 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 原因:

  1. catch 代码块接收参数 x。
  2. 这与之前定义的变量 x 不同 。这个 x 是属于 catch 块级作用域的。
  3. 然后,我们将块 级作用域中的变量赋值为 1,同时也设置了变量 y 的值。
  4. 现在,我们打印块级作用域中的变量 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 原因:

  1. 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