JavaScript 变量、作用域及内存


Posted in Javascript onApril 08, 2015

JS变量是松散型的(不强制类型)本质,决定了它只是在特定时间用于保存特定值的一个名字而已;
由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变;

一 变量及作用域
1.基本类型和引用类型

// JS变量包含两种不同的数据类型的值:基本类型值和引用类型值;

// 1.基本类型值:保存在栈内存中的简单数据段;即这种值完全保存在内存中的一个位置;
// 基本类型值包含:Undefined|Null|Boolean|Number|String;
// 这些类型在内存中占有固定大小的空间;它们的值保存在栈空间,我们按值来访问;

// 2.引用类型值:保存在堆内存中的对象(可能由多个值构成),即变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保存对象;
// 引用类型的值的大小不固定,因此不能保存在栈内存,必须保存在堆内存中;但可以将引用类型的值的内存地址保存在栈内存中;
// 当查询引用类型的变量时,先从栈内存中读取内存地址,然后通过地址找到堆内存中的值;=>按引用访问;

2.动态属性

// 定义基本类型值和引用类型值的方式相似:创建一个变量并为该变量赋值;
// 但当这个值保存到变量中以后,对不同类型值可以执行的操作则不一样;
  var box = new Object();           // 创建引用类型;
  box.name = 'lee';              // 新增一个属性;
  console.log(box.name);           // =>lee;

  var box = 'lee';              // 创建基本类型
  box.age = 15;                // 给基本类型添加属性;
  console.log(box.age);            // =>undefined;

3.复制变量值

// 在变量复制方面,基本类型和引用类型也有所不同;
// 基本类型赋值的是值本身;
  var box = 'lee';              // 在栈内存中生成一个box'lee';
  var box2 = box;               // 在栈内存中再生成一个box2'lee';
  // box和box2完全独立;两个变量分别操作时互不影响;

// 引用类型赋值的是地址;
  var box = new Object();          // 创建一个引用类型;box在栈内存中;而Object在堆内存中;
  box.name = 'lee';             // 新增一个属性;
  var box2 = box;              // 把引用地址赋值给box2;box2在栈内存中;
  // box2=box,因为它们指向的是同一个对象;
  // 如果这个对象中的name属性被修改了,box.name和box2.name输出的值都会被修改掉;

4.传递参数

// JS中所有函数的参数都是按值传递的,即参数不会按引用传递;
  function box(num){             // 按值传递,传递的参数是基本类型;
    num +=10;               // 这里的num是局部变量,全局无效;
    return num;
  }
  var num = 50;
  var result = box(num);
  console.log(result);           // 60;
  console.log(num);             // 50;

  function box(num){
    return num;
  }
  console.log(num);             // num is not defined;

  function box(obj){
    obj.name = 'lee';
    var obj = new Object();       // 函数内部又创建了一个对象,它是局部变量;但在函数结束时被销毁了;
    obj.name = 'Mr';           // 并没有替换掉原来的obj;
  }
  var p = new Object();
  box(p);                 // 变量p被传递到box()函数中之后就被复制给了obj;在函数内部,obj和p访问的是同一个对象;
  console.log(p.name);           // =>lee;

  // JS函数的参数都将是局部变量;也就是说,没有按引用传递;

5.检测类型

// 要检测一个变量的类型,通过typeof运算符类判断;
// 多用来检测基本类型;
  var box = 'lee';
  console.log(typeof box);          // =>string;

// 要检测变量是什么类型的对象,通过instanceof运算符来查看;
  var box = [1,2,3];
  console.log(box instanceof Array);     // =>true;
  var box2 = {};
  console.log(box2 instanceof Object);
  var box3 = /g/;
  console.lgo(box3 instanceof RegExp);
  var box4 = new String('lee');
  console.log(box4 instanceof String);   // =>true;是否是字符串对象;

  var box5 = 'string';
  console.log(box5 instanceof String);   // =>false;
  // 当使用instanceof检查基本类型的值时,它会返回false;

6.执行环境及作用域

// 执行环境:定义了变量或函数有权访问的其他数据,决定了它们各自的行为;
// 在Web浏览器中,全局执行环境=window对象;
// 因此所有的全局变量和函数都是作为window对象的属性和方法创建的;
  var box = 'blue';             // 声明一个全局变量;
  function setBox(){
    console.log(box);           // 全局变量可以在函数里访问;
  }  
  setBox();                 // 执行函数;
  // 全局的变量=window对象的属性;
  // 全局的函数=window对象的方法;

// PS:当执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁;
// 如果是在全局环境下,需要程序执行完毕,或者网页被关闭才会销毁;

// PS:每个执行环境都有一个与之关联的变量对象,就好比全局的window可以调用全局变量和全局方法一样;
// 局部的环境也有一个类似window的变量对象,环境中定义的所有变量和函数都保存在这个对象中;
// (我们无法访问这个变量对象,但解析器会处理数据时后台使用它);
  var box = 'blue';
  function setBox(){
    var box = 'red';           // 这里是局部变量,在当前函数体内的值是'red';出了函数体就不被认知;
    console.log(box);
  }  
  setBox();
  console.log(box);

// 通过传参可以替换函数体内的局部变量,但作用域仅限在函数体内这个局部环境;
  var box = 'blue';
  function setBox(box){           // 通过传参,将局部变量替换成了全局变量;
    alert(box);              // 此时box的值是外部调用时传入的参数;=>red;
  }
  setBox('red');
  alert(box);

// 如果函数体内还包含着函数,只有这个内函数才可以访问外一层的函数的变量;
// 内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数;
  var box = 'blue';
  function setBox(){
    function setColor(){
      var b = 'orange';
      alert(box);
      alert(b);
    }
    setColor();              // setColor()的执行环境在setBox()内;
  }
  setBox();
  // PS:每个函数被调用时都会创建自己的执行环境;当执行到这个函数时,函数的环境就会被推到环境栈中去执行,而执行后又在环境栈中弹出(退出),把控制权交给上一级的执行环境;

  // PS:当代码在一个环境中执行时,就会形成一种叫做作用域链的东西;它的用途是保证对执行环境中有访问权限的变量和函数进行有序访问;作用域链的前端,就是执行环境的变量对象;

7.延长作用域链

// 有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除;
// with语句和try-catch语句;这两个语句都会在作用域链的前端添加一个变量对象;
// with语句:会将指定的对象添加到作用域链中; 
// catch语句:会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明;
  function buildUrl(){
    var qs = '?debug=true';
    with(location){            // with语句接收的是location对象,因此变量对象中就包含了location对象的所有属性和方法;
      var url = href+qs;        // 而这个变量对象被添加到了作用域链的前端;
    };
    return url;
  }

8.没有块级作用域

// 块级作用域:表示诸如if语句等有花括号封闭的代码块,所以,支持条件判断来定义变量;
  if(true){                 // if语句代码块没有局部作用域;
    var box = 'lee';           // 变量声明会将变量添加到当前的执行环境(在这里是全局环境);
  }
  alert(box);

  for(var i=0; i<10; i++){         // 创建的变量i即使在for循环执行结束后,也依旧会存在与循环外部的执行环境中;
    var box = 'lee';
  }
  alert(i);
  alert(box);

  function box(num1,num2){
    var sum = num1+num2;         // 此时sum是局部变量;如果去掉var,sum就是全局变量了;
    return sum;
  }
  alert(box(10,10));
  alert(sum);                // sum is not defined;访问不到sum;
  // PS:不建议不使用var就初始化变量,因为这种方法会导致各种意外发生;

// 一般确定变量都是通过搜索来确定该标识符实际代表什么;搜索方式:向上逐级查询;
  var box = 'blue';
  function getBox(){
    return box;              // 此时box是全局变量;如果是var box='red',那就变成局部变量了;
  }
  alert(getBox());              
  // 调用getBox()时会引用变量box;
  // 首先,搜索getBox()的变量对象,查找名为box的标识符;
  // 然后,搜索继续下一个变量对象(全局环境的变量对象),找到了box标识符;
// PS:变量查询中,访问局部变量要比全局变量更快,因为不需要向上搜索作用域链;

二 内存问题

// JS具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存;它会自行管理内存分配及无用内存的回收;

// JS最常用的垃圾收集方式就是标记清除;垃圾收集器会在运行的时候给存储在内存中的变量加上标记;
// 然后,它会去掉环境中正在使用的变量的标记,而没有被去掉标记的变量将被视为准备删除的变量;
// 最后,垃圾收集器完成内存清理工作,销毁那些标记的值并回收他们所占用的内存空间;

// 垃圾收集器是周期性运行的,这样会导致整个程序的性能问题;
// 比如IE7以前的版本,他的垃圾收集器是根据内存分配量运行的,比如256个变量就开始运行垃圾收集器,这样就不得不频繁地运行,从而降低了性能;

// 一般来说,确保占用最少的内存可以让页面获得更好的性能;
// 最佳方案:一旦数据不再使用,将其值设置为null来释放引用,这个做法叫做解除引用;
  var o = {
    name:'lee';
  };
  o = null;               // 解除对象引用,等待垃圾收集器回收;

三 小结

1.变量
// JS变量可以保存两种类型的值:基本类型值和引用类型值;它们具有以下特点:
// 1.基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;
// 2.从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本;
// 3.引用类型的值是对象,保存在堆内存中;
// 4.包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;
// 5.从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向用一个对象;
// 6.确定一个值是哪种基本类型可以使用typeof操作符;而确定一个值是哪种引用类型可以使用instanceof操作符;

2.作用域
// 所有变量都存在于一个执行环境(作用域)中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量;
// 1.执行环境有全局执行环境和函数执行环境之分;
// 2.每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用域链;
// 3.函数的局部环境不仅有权访问函数作用域中的变量,而且有权访问其父环境,乃至全局环境;
// 4.变量的执行环境有助于确定应该合适释放内存;

3.内存
// JS自动垃圾收集机制
// 1.离开作用域的值将被自动标记为可以回收,因此将在垃圾收集期间被删除;
// 2.为了确保有效地回收内存,应该及时解除不再使用的全局对象/全局对象属性以及循环引用变量的引用;

Javascript 相关文章推荐
多浏览器支持的右下角浮动窗口
Apr 01 Javascript
jquery 图片 上一张 下一张 链接效果(续篇)
Apr 20 Javascript
javascript学习笔记(十三) js闭包介绍(转)
Jun 20 Javascript
详解Javacript和AngularJS中的Promises
Feb 09 Javascript
jQuery针对input的class属性写了多个值情况下的选择方法
Jun 03 Javascript
Node.js如何自动审核团队的代码
Jul 20 Javascript
微信小程序 location API实例详解
Oct 02 Javascript
微信小程序实现图片预加载组件
Jan 18 Javascript
使用jquery给新生的th绑定hover事件的实例
Feb 10 Javascript
vue.js实现数据动态响应 Vue.set的简单应用
Jun 15 Javascript
使用pkg打包Node.js应用的方法步骤
Oct 19 Javascript
vue实现点击按钮“查看详情”弹窗展示详情列表操作
Sep 09 Javascript
JavaScript Function函数类型介绍
Apr 08 #Javascript
JavaScript 模块化编程(笔记)
Apr 08 #Javascript
JavaScript DOM事件(笔记)
Apr 08 #Javascript
javascript笛卡尔积算法实现方法
Apr 08 #Javascript
JavaScript获取指定元素位置的方法
Apr 08 #Javascript
javascript跨域原因以及解决方案分享
Apr 08 #Javascript
JavaScript 里的类数组对象
Apr 08 #Javascript
You might like
apache和php之间协同工作的配置经验分享
2013/04/08 PHP
8个PHP数组面试题
2015/06/23 PHP
PHP后端银联支付及退款实例代码
2017/06/23 PHP
laravel5.1框架model类查询的实现方法
2019/10/08 PHP
PHP7新特性
2021/03/09 PHP
基于jQuery的日期选择控件
2009/10/27 Javascript
js取消单选按钮选中示例代码
2013/11/14 Javascript
原生js仿jq判断当前浏览器是否为ie,精确到ie6~8
2014/08/30 Javascript
jQuery的animate函数实现图文切换动画效果
2015/05/03 Javascript
jquery简单实现图片切换效果的方法
2015/05/12 Javascript
关于cookie的初识和运用(js和jq)
2016/04/07 Javascript
jQuery的promise与deferred对象在异步回调中的作用
2016/05/03 Javascript
Javascript基础_简单比较undefined和null 值
2016/06/14 Javascript
JavaScript用JSONP跨域请求数据实例详解
2017/01/06 Javascript
vue.js的安装方法
2017/05/12 Javascript
nodejs之get/post请求的几种方式小结
2017/07/26 NodeJs
javascript系统时间设置操作示例
2019/06/17 Javascript
layui 富文本编辑器和textarea值的相互传递方法
2019/09/18 Javascript
Vue-cli打包后如何本地查看的操作
2020/09/02 Javascript
[59:00]DOTA2-DPC中国联赛 正赛 Ehome vs PSG.LGD BO3 第一场 3月7日
2021/03/11 DOTA
Python自动连接ssh的方法
2015/03/07 Python
一步步解析Python斗牛游戏的概率
2016/02/12 Python
python实现邮件发送功能
2019/08/10 Python
利用python实现AR教程
2019/11/20 Python
Tensorflow tf.nn.atrous_conv2d如何实现空洞卷积的
2020/04/20 Python
彻底解决Python包下载慢问题
2020/11/15 Python
Solaris操作系统的线程机制
2015/07/28 面试题
几个常见的消息中间件(MOM)
2014/01/08 面试题
采购部经理岗位职责
2014/02/10 职场文书
《童年》教学反思
2014/02/18 职场文书
常务副总经理岗位职责
2014/04/12 职场文书
和解协议书
2014/04/16 职场文书
酒后驾车标语
2014/06/30 职场文书
2014年社区党建工作汇报材料
2014/11/02 职场文书
高中同学会致辞
2015/08/01 职场文书
你有一份《诚信考试承诺书》待领取
2019/11/13 职场文书