javascript变量对象 函数调用栈 作用域 闭包等细解!

简介:

说明

下面代码演示基于window系统chrome浏览器环境,版本号为63.0.3239.132,32位!相关结果可能会有一点出入,请也实际为准!

相关代码调试的过程中查看结果的步骤:

  • 打开浏览器控制台,切换到sources板块,并选择相应的源文件;

  • 在对应的源文件代码左边的行号上打上断点;

  • 然后刷新浏览器,浏览器会在对应打断点的代码出停止执行,此时我们根据需要按f11键一步一步的运行代码,并同时查看代码的调用栈,作用域等情况,主要查看source板块下最右边子板块的Call Stack和Scope项。

相关概念梳理

其实从我自身出发,我觉的如果需要更好地理解变量对象的话,那么需要对以下概念有一个比较基本的理解会更方便些!

  • 函数调用栈

为了理解函数调用栈,我们先写一段代码:


  function fn1(){
    console.log('fn1');
    fn2();
  };
  function fn2(){
    console.log('fn2');
  };
  fn1();

我们在fn2函数调用的地方打上断点,然后刷新浏览器,查看Call Stack选项,会看到下面的结果:


  fn1
  (anonymous)

此时再按一次f11键,此时Call Stack显示结果如下:


  fn2
  fn1
  (anonymous)

这里说明一下,anonymous指代全局匿名调用函数环境,而fn1和fn2分别指代fn1函数作用域和fn2作用域环境。

我们梳理一下浏览器的调用过程:1 js进入全局匿名函数环境,把这个所谓的匿名函数推入调用栈;2 发现此时又调用了fn1,于是把fn1函数推入调用栈,此时fn1在anonymous的上面; 3 紧接着,发现调用了fn2,于是把fn2推入调用栈,于是得到了上面的结果。

如果后续我们继续按f11键调试,会发现Call Stack会依次出现先面的结果:


  fn2
  fn1
  (anonymous)

  fn1
  (anonymous)

  (anonymous)

也就是说最后只剩下了全局匿名函数环境,这里强调一下,anonymous环境将会伴随着程序运行一直存在,除非你关闭了浏览器。

于是我们总结得到这样的结果:存在这样一个调用栈,默认推入一个全局匿名函数在栈底,当此时再调用其它全局函数的时候,会把该函数推入栈,并在anonymous上面,如果该函数内部继续调用了其它函数,那么同样道理,会把其它函数推入栈,放在该函数上面,最后当函数在调用完成的过程中,会依次退出该调用栈,退出的过程中会把权限交给上一层函数,最后又回归了只剩下全局匿名函数环境。于是我们说了这么多,其实这就是函数调用栈!

函数调用栈你可以理解为函数调用前后包含关系:先进后出,后进先出!它描述了代码执行的先后顺序以及当前代码执行控制权限的拥有者关系等。

  • 函数作用域

我们都知道javascript是没有块级作用域的,只有函数作用域,怎么理解?我们看下面的代码:

代码1


for(var k = 0;k<10;k++){
  //...
};
console.log(k);//输出10

我们发现for循环代码块执行完了之后,依然能得到k的值。再看下面的代码:

代码2


function fn3(){
  var a = 'a';
};
fn3();
console.log(a);//报错 a is not defined

我们发现在函数里面定义的变量a,在函数外面是拿不到的,这就是函数作用域能做到的。

函数作用域能让我们定义一些函数内部使用的与外部环境同名的变量而不会跟外部环境冲突,我们用的较多的地方就是即时函数,如下:


var a = 'outer';
(function(){
  var a = 'inner';
  conosle.log(a);//输出inner
})();
console.log(a);//输出outer

当然了在es6及后续版本javascript中,我们可以使用let和const标志符来定义块级变量,这里不作讨论!

这里再做一下扩展,作用域中涉及到最多的一个概念就是作用域链,这个是什么意思呢!看下面的代码:


let a = 'a';
function fn4(){
  let b = 'b';
  return a+b;
};
let c = fn4();
console.log(c);//输出'ab'

作用域链描述的是一种函数在执行的过程中查找变量的方式,具体来说:函数执行,如果遇到某变量,会首先在自身作用域环境查找改变量,如果不存在,会向上一层作用域查找变量,也就是函数调用栈中当前执行函数的下一层函数环境查找变量,依次类推,直到到全局环境中查找,如果在全局中都没有找到变量的话,那么就会报错!

其实原型链也是一种描述实例属性查找的过程,跟作用域链类似!

  • 闭包

恐怕接触过javascript的开发者,听到最多与其有关的概念就是闭包了。而且网上有很多文章和书籍都对闭包进行了说明,其实我觉的理解闭包并不难哈!我们来看段代码吧:


function fn5(){
  let a = 0;
  return function(){
    a++;
    console.log(a);
  };
};
let fn = fn5();
fn();

我们在fn调用的地方打一个断点,随后按一次f11键进入fn执行环境,看下Scope项结果,会发现Scope下有一项子项:


Closure(fn5)
|_ a

我可以明确的告诉你,此时的fn5对于fn来说就是一个闭包(你可以理解是一个环境概念,该环境维护了一些内部返回的匿名函数用到的一些外部变量)!

梳理一下,闭包指得是某个函数调用之后,自身执行环境已不复存在,但返回了一个函数,由于该返回函数内部用到了外部函数里面的一些变量,并又该内部函数又赋值给了其它变量,导致虽然外部函数不存在了,但是它引用的那些外部变量却不能回收的一个环境(闭包)。

闭包用好了,可以保证好多变更的作用域周期得以提升,减少变量命名冲突,但是过多的使用闭包,也会存在内存泄漏的问题,因为你的很多变量都没有被垃圾回收器回收。

进入正题,变量对象

为了让你对变量对象整体有个最初的概念。在详细介绍之前,我对变量对象概念作如此表述:变量对象指函数执行过程中,函数自身用到数据从哪里来的,函数怎么管理这些数据的等等,其实变量对象里面保存了函数在执行过程中所有用到的数据以及某个时刻值等!

时间仓促,后续待更新……


原文发布时间为:2018年06月21日
原文作者: 掘金

本文来源: 掘金 如需转载请联系原作者


相关文章
|
9月前
|
自然语言处理 JavaScript 前端开发
解析JavaScript函数调用栈:理解执行上下文与调用堆栈
解析JavaScript函数调用栈:理解执行上下文与调用堆栈
121 0
|
存储 前端开发 JavaScript
JavaScript基础系列(3):调用栈在JavaScript引擎中扮演了一个什么样的角色
也就是说我测试电脑当时递归了11421次左右之后栈溢出了,11421这个数字根据电脑配置不同可能有一些出入。Maximum call stack size exceeded超出最大调用栈的大小了。
79 0
|
消息中间件 JavaScript 前端开发
js的EventLoop事件循环机制调用栈、微任务、消息队列执行顺序优先级
js的EventLoop事件循环机制调用栈、微任务、消息队列执行顺序优先级
91 0
|
Web App开发 JavaScript 前端开发
浏览器原理 07 # 调用栈:为什么JavaScript代码会出现栈溢出?
浏览器原理 07 # 调用栈:为什么JavaScript代码会出现栈溢出?
133 0
浏览器原理 07 # 调用栈:为什么JavaScript代码会出现栈溢出?
|
JavaScript
js基础笔记学习261调用栈1
js基础笔记学习261调用栈1
37 0
js基础笔记学习261调用栈1
|
JavaScript
js基础笔记学习262调用栈2
js基础笔记学习262调用栈2
61 0
js基础笔记学习262调用栈2
|
存储 Web App开发 自然语言处理
深入理解JavaScript-执行上下文与调用栈
深入理解JavaScript-执行上下文与调用栈
113 0
深入理解JavaScript-执行上下文与调用栈
|
消息中间件 JavaScript
js的EventLoop事件循环机制调用栈、微任务、消息队列执行顺序优先级
js的EventLoop事件循环机制调用栈、微任务、消息队列执行顺序优先级
114 0
|
JavaScript 前端开发 Java
JS 调用栈机制与 ES6 尾调用优化介绍
调用栈的英文名叫做Call Stack,大家或多或少是有听过的,但是对于js调用栈的工作方式以及如何在工作中利用这一特性,大部分人可能没有进行过更深入的研究,这块内容可以说对我们前端来说就是所谓的基础知识,咋一看好像用处并没有很大,但掌握好这个知识点,就可以让我们在以后可以走的更远,走的更快! 目录 数据结构:栈 调用栈是什么?用来做什么? 调用栈的运行机制 调用栈优化内存 调用栈debug大法 数据结构:栈 栈是一种遵从后进先出(LIFO)原则的有序集合,新元素都靠近栈顶,旧元素都接近栈底。 生活中的栗子,帮助一下理解:
179 0
JS 调用栈机制与 ES6 尾调用优化介绍
|
JavaScript 前端开发
如何利用JavaScript的arguments对象实现用代码打印调用栈的需求
如何利用JavaScript的arguments对象实现用代码打印调用栈的需求
90 0
如何利用JavaScript的arguments对象实现用代码打印调用栈的需求