这几天夏日炎炎,适合啃书~
工作之余,抽一切能空余下来的时候啃啃啃~
《javascript高级程序设计》读书笔记(一),咱继续往下走~

第四章 变量,作用域和内存问题

ECMAScript变量可能包含两种不同数据类型的值:基本类型值引用类型值
基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。

基本类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值时保存在内存中的对象,引用类型的值时按引用访问的。

引用类型可以添加属性和方法,这是基本类型做不到的

var person = new Object();
person.name = “her” ;
alert(person.name);         //“her”

日常开发中经常会复制变量。复制变量值需要注意的问题:
如果从一个人变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。

var num1 = 5;
var num2  = num1;

num1中保存的值时5,当使用num1的值初始化num2时,num2中也保存了值5
但是num2中的5与num1中的5时完全独立的。此后,这两个变量可以参与任何操作而不会相互影响。
上图,大家能看的更清楚一点
image

但是当一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。
不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。
复制操作结束后,两个变量实际将引用同一个对象。
因此改变其中一个变量,就会影响另外一个变量

look~~~

var obj1 = new Object();
var obj2 = obj1;
obj1.name = “her”;
alert(obj2.name);         //“her”

obj1和obj2都指向同一个对象。当为obj1添加name属性后,可以通过obj2来访问这个属性,因为这两个变量引用的都是同一个变量。
直接上图简单明了~~
image

ECMAScript中所有函数的参数都是按值传递的。
基本类型值的传递如果基本类型变量的复制一样,而引用类型值的传递,则如果引用类型变量的复制一样。

function addTen(num){
    num += 10;
    return num;
}
var count = 20;
var result = addTen(count);
alert(count);               //20  没有变化
alert(result);              //30

再来一个传递对象的栗子小姐姐

function setName(obj){
        obj.name = “her”;
}
var person = new Object();
setName(person);
alert(person.name);         //“her”

在函数内部,obj和person引用的是同一个对象,换句话说,即使这个对象是按值传递的,obj也会按引用来访问同一个对象,于是乎,当在函数内部为obj添加name后,外部的person也有所放映,因为person只想的对象在堆内存中只有一个,而且是全局对象。

嘿哈~!?再来一个~~~

function setName(obj){
    obj.name = “her”;
    obj = new Object();
    obj.name = “haha”;
}
var person = new Object();
setName(person);
alert(person.name);         //“her”

当在函数内部重写obj时,这个变量引用的就是一个局部对象,而这个局部对象会在函数执行完毕后立即被销毁。

要检测一个变量是不是基本数据类型,tyoeof是个不错的工具。它能确定一个是变量是字符串、数值、布尔值还是undefined。
如果变量的值是一个对象或null,typeof操作符会返回"object"。
虽然检测基本数据类型时typeof是非常得力的,但检测引用类型时,ECMAScript为我们提供了instanceof操作符

alert(person instanceof Object);         //变量person是Object吗?
alert(colors instanceof Array);         //变量colors是Array吗?
alert(pattern instanceof RegExp);         //变量pattern是RegExp吗?

下面正儿八经看看执行环境和作用域吧~

var color = "blue";
function changeColor(){
    if(color === "blue"){
        color = "red";
    }else {
        color = "blue";
    }
}
changeColor();
alert("color is now" + color);

上面例子中,函数changeColor()的作用域包括两个对象:它自己的变量对象(其中定义着arguments对象)和全局环境的变量对象。可以在函数内部访问变量color,就是因为可以在这个作用域链中找到它。

此外,在局部作用域中定义的变量可以在局部环境中与全局变量互换使用。look~

var color = "blue";
function changeColor(){
    var anotherColor = "red";
    function swapColors(){
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        // 这里可以访问color、anotherColor和tempColor
    } 
    // 这里可以访问color和anotherColor,但不能访问tempColor
}
//这里只能访问color
changeColor();

可以用途这样子表示
image

内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。
每个环境都可以向上搜索作用域链,以查询变量和函数名
但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。

那有什么方法可以延长作用域链呢,有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。
具体讲就是当执行流进入下列任何一个语句时,作用域链会得到加长:

  • try-catch语句的catch块;
  • with语句。

javascript没有块级作用域。

for(var i = 0; i < 10; i++){
    doSomething(i);
}
alert(i);      //10

对于有块级作用域的语言来说,for初始化变量的表达式所定义的变量,只会存在于循环的环境
但是对于javascript来说,for语句创建的i即使在for循环执行后,也依旧存在于循环外部的执行环境中。

查询标识符是这个样子滴~

var color = "blue";
function getColor(){
    return color;
}
alert(getColor());         //"blue"

整个例子会有个搜索的过程。首先搜索getColor()的变量对象,查找是否包含color标识符,没找到的话就继续搜索下一个变量(全局环境的变量对象)。过程如下图:
image

搜索过程中,如果存在一个局部的变量的定义,搜索会自动定制,不再进入另一个变量对象。

var color = "blue";
function getColor(){
    var color = "red";
    return color;
}
alert(getColor());         //"red"

最后让我们稍微的来总结一下下~
javascript变量可以用来保存两种类型的值:基本类型值和引用类型值。基本类型的值源自5种基本数据类型:Undefined、Null、Boolean、Number和String。
基本类型值和引用类型值具有以下特点:

  • 基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;
  • 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本;
  • 引用对象的值是对象,保存在堆内存中;
  • 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;
  • 从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象;
  • 确定一个值是那种基本类型可以用typeof操作符,而确定一个值是那种引用类型可以使用instanceof操作符。

  • 执行环境有全局执行环境和函数执行环境之分;
  • 每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用域链;
  • 函数的局部环境不近有权访问函数作用域中的变量,而且有权访问其包含(父)环境,乃至全局环境;
  • 全局环境只能访问在全局环境中定义的变量和函数,而不能直接访问局部环境中的任何数据;

  • 离开作用域的值将自动标记为可以回收,因此将在垃圾收集期间被删除。
  • “标记清除”是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存。
  • 另一种垃圾收集算是是“引用计数”,这种算法的思想是跟踪记录所有值被引用的次数。javascript引擎不再使用这种算法;
  • 当代码存在循环引用现象时,“引用计数”算法就会导致问题。
  • 解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处。为了确保有效地回收内存,应该及时解除不再使用的全局对象、全局对象属性以及循环引用变量的引用。

好啦,今天就到这里啦~
未完待续哟


前端小饼干
1 声望4 粉丝