likes
comments
collection
share

👨面试官的三个问题

作者站长头像
站长
· 阅读数 22

本文采用口语化的表达,弱化代码的表现,尽可能最大化地体现面试的内容

面试开始,现在问你三个问题,看看你的基础水平

什么是执行上下文

执行上下文是 js 代码运行时的环境,决定了代码执行的顺序,以及变量和函数生命周期、访问权限。

执行上下文,还分为全局执行上下文,和函数执行上下文

当 js 代码执行时,会首先创建全局执行上下文,这个上下文在整个代码执行周期都会存在。全局上下文中,存放了很多全局变量,window,document,location,history,Math,Object,Function,String 等等

每当函数被调用,就会创建函数执行上下文。在函数执行上下文中,存放着函数的参数以及函数内变量。等函数执行完毕,函数执行上下文就会被销毁,其中存放的变量也会跟着销毁。

每个执行上下文,里面都有:

  1. 变量环境,用来存放 var 变量,以及 function 变量
  2. 词法环境,用来存放 let 以及 const 变量
  3. outer 指针,用来指父级作用域。
  4. this 指针

父级作用域是词法作用域,是由函数声明的位置决定的,而不是函数调用的位置,所以父级作用域不一定是上一个执行上下文

执行上下文在内存中是以栈的形式存放的,代码执行的第一个执行上下文是全局上下文,被创建的时候被压入栈底。执行到函数的时候,创建函数执行上下文,并将该函数执行上下文压入栈中。等函数执行完成,函数执行上下文就会被弹出栈。并将控制权交还给上一个执行上下文

什么是 this

this 其实是一个模仿面向对象的一个概念,在面向对象中,this 表示方法所处的实例对象。而在 js 中,this这个变量在不同情况下有不同的含义:

  1. 在全局作用域中,this 指向 window,或者 global;严格模式下,this 为 undefined
  2. function函数调用的时候,this 指向 window,或者 global;严格模式下,this 也为 undefined
  3. 对象方法方式调用 function 的时候,this 指向调用它的对象
  4. 对 function 执行 new 操作的时候,this 指向 function 返回的实例对象
  5. 对 function 执行 bind,apply,call 的操作时,this 指向方法绑定的对象
  6. 对于箭头函数,里面的 this 指向父级作用域中的 this 对象
  7. 在 class 中的函数,this 的指向和 function,剪头函数的 this 规则是一致的
  8. 当 function 作为事件的回调函数,其中 this 指向触发事件的 DOM 元素

箭头函数中的 this 指向不能修改,但是 function 函数中 this 指向可以修改。且不同的修改方式优先级不同,下面是优先级从低到高的方式

  1. 普通调用方式
  2. 通过对象方法调用方式
  3. bing、apply、call 强制指定
  4. 构造函数调用

什么是作用域

作用域用来描述变量和函数的可访问范围

作用域有全局作用域,函数作用域,局部作用域之分。

在全局上下文中声明的变量,就拥有全局作用域;在代码的任意位置都可以访问。在函数中声明的变量,就拥有函数作用域;在函数内部可以访问,在函数外部不可访问。

在 ES6 之前,只有这两种作用域,所以在 if,while,for 等内部声明的变量都拥有函数作用域,及在{ }外部也可以访问这些变量。在 ES6 之后,有了 let,const 变量,就支持了局部作用域。即{ }外部不可以访问{}内部变量。

作用域之间可以相互嵌套,代码在查找变量的时候,会优先从当前作用域中查找,然后再去外部作用域查找,直到全局作用域为止。这个查找线路就是作用域链

作用域之间的嵌套关系,在代码编写时候就已经决定了。即一个函数的外部作用域不是函数调用的位置决定的,而是函数声明的位置决定的。所以作用域又叫词法作用域,或者静态作用域。

举个例子:在全局作用域中声明函数 A,又在函数 A 中声明了变量 varA,以及函数 B,并且返回函数 B。在函数 B 的内部有对 varA 的访问。这时候执行函数 A 就会得到函数 B,那么请问,执行函数 B 时,访问 varA 过程是什么?

//全局作用域
var varA = '1'

function A(){
  var varA = '2';
  
  function B(){
    console.log(varA);
  }
  return B;
}

var funcB = A();

funcB(); // 2

首先在函数 B 的作用域中查找 varA,如果没有找到,就会往父级作用域,也就是函数 A 的作用域里找,找到 varA 就会停止查找,并且返回 varA 值。

在执行上下文那里,我们知道,在函数执行完毕后,对应的执行上下文是会被销毁的,而函数 B 还能访问函数 A 里面的varA,这说明函数 A 的执行上下文并没有被销毁。更严格地说没有被完全销毁。

这是因为函数 B 的执行上下文中有一个对它的父级执行上下文--outer,每个执行上下文中都有一个这样的 outer,这也是为什么调用的位置千变万化,还是记得自己的父级作用域是谁,靠的就是这个 outer 变量。

JS 引擎在执行的时候,就发现函数 B 的内部有对 varA 的访问,JS 引擎就会保留 varA,而函数 A 执行上下文中其他的没有被引用的变量全部都要销毁掉,比如 this,arguments 等等

那保留下来用来存放 varA 的内存空间,叫做函数 A 的部分执行上下文,也叫做函数 A 的闭包。

转载自:https://juejin.cn/post/7387305065801334794
评论
请登录