关于函数和变量的处理机制(附面试题)

关于函数和变量的处理机制,属于我们学习js的必备技能,
也是面试经常会遇到的问题

分享一点自己整理的知识点,加个人理解

  1. 浏览器和NODE运行JS代码的时候,会提供一个供代码赖以执行的环境,我们把这个环境称之为“全局作用域”
    浏览器:window
    NODE:global
    window和global代表全局作用域,他们也是全局对象(在JS代码的任何位置,我们都可以基于window.xxx或者global.xxx调取全局对象中的属性和方法[这些属性方法一般都是浏览器内置的])
//=>例如:
window.setTimeout(function(){},1000);
window.alert();
...
  1. 环境创建完成后,JS代码没有立即执行,而是做一些词法解析,其中词法解析中有一个重要的操作就是“变量提升” => 在变量提升这个操作阶段,渲染引擎会把“当前作用域”下所有“带VAR和FUNCTION关键字”的变量进行提前的“声明或者定义”
[声明:declare]
   //=>var [variable name];
   var xxx;  //=>声明就是在创建变量或者函数
   function fn;

[定义:defined]
   //=>[variable name]=[value];
   xxx=100;  //=>定义就是在给声明的变量赋值
   fn(){};

//=>只声明未定义,默认值是:undefined(未定义)
  • 带VAR关键字的在变量提升阶段只声明
  • 带FUNCTION关键字的在这个阶段完成了“声明+定义(赋值)”

    1. 引用数据类型的操作步骤
  • 引用数据类型
    • 对象
      • 普通对象
      • 数组对象
      • 正则对象
      • 日期对象
      • Math
    • 函数

由于引用数据类型存储的值过于复杂(结构复杂及内容较多),渲染引擎会开辟一个新的内存空间,单独来存储这些值,最后把空间的引用地址赋值给对应的变量,后期所有的操作都是基于地址先找到空间,然后对空间中的内容进行操作

[对象数据类型]
   1. 开辟一个内存空间(有一个16进制的地址)
   2. 把对象中的属性名和属性值(键值对)依次存储到内存空间中
   3. 把内存空间的地址赋值给变量

   /*
    * [变量提升]
    *   var obj;
    *
    * [代码执行]
    *   1.开辟一个内存空间(bbbfff000)
    *   2.把键值对存储到空间中
    *      + x : 10
    *      + y : obj.x*3  此时的obj还没有赋值呢,键值对存储完成,才会把地址赋值给obj,此时的obj依然是undefinedundefined是无法调取属性x的,所以报错了
    *   obj =
    */
   var obj={
      x:10,
      y:obj.x*3  //=>Uncaught TypeError: Cannot read property 'x' of undefined  说明此时的obj是undefined
   };
   console.log(obj.y);

[函数数据类型:创建一个函数]
   1. 开辟一个内存空间(有一个16进制的地址)
   2. 把函数体中的代码当做“字符串”存储到内存空间中
      + 函数创建的时候,存储的都是字符串,所以说函数只创建不执行是毫无意义的
      + 变量提升只对当前作用域下的var/function处理,主要原因是函数中存的都是字符串,我们看到的函数中的var/function此时都还是字符呢
      + 当函数执行的时候目的就是要把这堆字符串拿出来执行的
   3. 把内存空间的地址赋值给变量

   /*
    * [全局作用域:变量提升]
    *   function fn = AAAFFF111;
    *   //=>var x;  var y;  这两步没有,原因是此时的他们都还是无意义的字符
    */
   function fn(){
      var x=100;
      var y=200;
      console.log(x+y);
   }
   fn();
  1. 代码执行过程中的一些细节点

    代码执行的时候,如果当前的某个操作在变量提升阶段已经处理过了,渲染引擎很懒,不会进行重复的处理,所以:遇到 var xxx=xxx 只需要给变量赋值即可,不需要重新声明;遇到 function xxx(){…} 直接的跳过即可,因为变量提升阶段函数已经声明+定义了;

  2. 函数执行

    目的:把之前创建函数在内存空间中存储的“代码字符串”变为真正的JS代码执行,从而实现相关的效果

    • 第一步:首先形成一个私有的作用域(给接下来的代码执行提供一个私有的环境)
    • 第二步:到私有作用域中依然要先进性词法解析
    • 先给形参赋值
    • 私有作用域中的变量提升
    • 第三步:在私有作用域中把代码自上而下执行
重要的知识点:
  - 在每一个作用域(全局和函数执行产生的私有的)的词法解析阶段(变量提升或者形参赋值都在这个阶段),声明过的变量或者函数都是当前作用域私有的
    + 全局作用域下声明的变量是“全局变量”:在JS任何位置都可以使用(但是用多了会产生一些冲突和冗余“全局变量污染”)
    + 在私有作用域下,“形参”以及“声明的变量函数”都是“私有变量”:只能在当前作用域下使用,和外界的变量(即使重名)是互不干扰的

  - 私有作用域代码执行过程中,遇到一个变量或者函数,但它不是当前作用域私有的变量,此时相当于在私有作用域中“操作上级作用域中的变量”
    + 代码执行遇到变量,首先验证是否是自家私有的,是私有的则和外面没有半毛钱关系,不是私有的进行第二步
    + 第二步是向当前作用域的上级作用域查找,看是否是上级作用域中私有的,如果是,那么我们此处操作的就是上级作用域中的变量,如果不是,则继续向上查找...
    + 如果找到window都没有发现哪个作用域有这个变量,说明当前变量是不存在,如果是获取这个变量值则会报错,如果是设置变量的值,相当于给window全局对象设置了一个新的属性
  =>我们把这种向上级作用域一级级查找的过程称为“作用域链”
  1. 变量提升的细节和意义
    [意义]
    可以让我们开发者在代码执行之前使用变量或者函数(尤其是函数:代码执行之前已经创建完成了),不会发生错误
//=>创建变量带VAR和不带VAR的区别
  1.在私有作用域中,带VAR的变量会在变量提升阶段进行声明,属于私有变量(和外界无关联);如果不带VAR也不是形参,则它不是私有的变量,这样和外界有关了!

  var x=10,
      y=20;
  function fn(){
    var x=100; //=>[私有变量]
    y=200; //=>[全局变量]
  }
  fn();
  console.log(x,y); //=>10,200

  2.在全局作用域下,带VAR和不带VAR有一些区别
    + 带VAR的是全局变量(也相当于给全局对象增加了一个属性),存在变量提升
    + 不带VAR的只是给全局对象设置一个属性而已,没有所谓的变量提升
//=>在变量提升阶段,遇到相同的变量名(函数名),不会重复声明,但是需要重复赋值,最后赋的值会替换掉之前赋的值

作用域

1.如何查找当前作用域的上级作用域

函数执行会形成私有作用域(A),A的上级作用域是谁和他在哪执行没关系,只和在哪定义的有关系;在哪个作用域下定义的,A的上级作用域就是谁!

2.堆栈内存

JS中有两大内存:堆内存(Heap)、栈内存(Stack)
堆内存作用:用来存储内容的(对象存储的是键值对,函数存储的是代码字符串)
栈内存作用:也可以被称为作用域,是代码解析和执行的环境

3.堆栈内存释放问题

栈内存的释放:函数执行会形成一个私有的栈内存,一般函数执行完成,栈内存会自己释放(优化性能),除非栈内存中的某一个东西(例如:栈中开辟的堆内存)被栈内存以外的变量(或者是其它的东西)占用了,此时的栈内存就不能被释放掉;全局作用域(栈内存)是在一开始加载JS的时候诞生的,当页面关闭的时候会自动释放掉!

堆内存释放:只要没有变量占用这个堆内存,浏览器就会在空闲的时候把它释放掉,所以项目中我们尽可能把不被使用的堆内存手动释放掉,例如:obj=null;

  1. 函数执行会形成一个私有的作用域(栈内存),私有栈中的私有变量被作用域保护起来(和外界的变量互不干扰),而且一旦当前栈内存中的某个东西被外面占用了,当前栈内存就不会被释放掉,我们可以在栈内存中存储一些信息(这些信息也不会被销毁)… 我们把函数执行的这种“保护”、“保存”特性称之为“闭包”!!!
综上 : 我们来看两道题(附答案)
var i= 2,
    x = 5;
var fn = function(x){
    x+=3;
    return function(y){
    console.log((x++)+y+(--i));
    }
};
var f = fn(1);
f(2);            //7
fn(3)(4);       //10
f(5);           //9

为了帮助大家理解,画了一张图,看图能更清晰的理解整个题的运行过程
这里写图片描述

第二题

console.log(x, y);//=>undefined undefined
var x = 10,
    y = 20;

function fn() {
    console.log(x, y, z);//=>undefined 20 报错 (假如没有报错,下面继续执行)
    var x = y = 100;
    z = 30;
    console.log(x, y, z);//=>100 100 30  
}

x = fn(20);
console.log(x, y, z);//=>undefined 100 30

希望本篇博文能更好的帮助大家学习~~~ q(≧▽≦q)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值