变量,作用域,和内存的问题
javascript
高级程序设计读书笔记(四)
变量,作用域,和内存的问题
变量
不存在定义一个变量就保存这种数据类型的值的规则,一个变量的值和数据类型可以在脚本的生命周期内改变。
- 基本类型的值
- 引用类型的值
基本类型的值
基本数据类型是按照值访问的,可以操作保存在变量里面实际的值
- 字符串
- 数值
-
NaN
:特殊的数值型;isNaN()
:返回是否是不是数值 -
isNaN()
被对象调用的时候,会首先调用对象的valueOf()
,看其是否可以转为数值;不能,则基于这个返回值在调用toString()
,再看是否是数值
-
- 布尔值
undefined
-
null
- 只要意在保存的变量还没有真正保存对象,就应该明确的让该变量保存
null
,体现出null
是空对象指针。
- 只要意在保存的变量还没有真正保存对象,就应该明确的让该变量保存
字符串,数值和布尔值都有
toString()
方法,但undefined
和null
没有此方法,但是可以使用String()
,这个方法可以传入基数
,输出二进制或者八进制或者十六进制的字符串值
数据类型 | 转为true的值 | 转为false的值 |
---|---|---|
Boolean | true | false |
Number | 任何非0数字,包括无穷大 | 0和NaN |
String | 任何非空字符串 | ' '(空字符串) |
Object | 任何对象 | null |
Undefined | 不适用 | undefined |
在内存中占据固定大小的空间,因此保存在栈内存
里面
引用类型的值
是保存在内存中的对象,是按照引用来访问的,因为,
JS
是不允许直接访问内存中的位置,也就不能直接操作对象的内存空间,实际上是操作对象的引用
引用类型的值是对象,保存在堆内存
中
-
Object类型
对象其实就是数据和功能的集合,
Object类型
是所有它的实例的基础,其所具有的任何的属性和方法同样都存在更具体的对象中。
两种值的不同点
-
当定义一个引用类型的变量,可以为其动态的添加属性和方法,但是基本类型的变量就不可以,虽然操作是不会出现错误,返回
undefined
-
基本类型的变量的复制
var num1=5; var num1=num2;
- num1和num2是相互独立的,互不影响,在内存空间里面分配不同的空间
-
引用类型值的复制:
var obj1=new Person(); var obj2=obj1;
- 此时的obj2和obj1是指向同一个地址空间,之间的复制是把引用,也就是指向这个对象的内存空间的指针复制了一份,当操作obj1的属性的时候,obj2也受到影响
传递参数
ECMAScript
中所有函数的参数都是按值传递
的。就是把值,从外部的变量,复制一份给函数中参数变量。传递基本类型的值给函数中的参数的时候,被传递的值被复制给函数参数aguments
对象的一个元素,局部变量;给参数传递引用类型的时候,就是把这个值在内存中的地址复制给arguments
中的一个局部变量,所以这个局部变量变化会反映在函数的外部。
function setName(obj){
obj.name='icessun';
obj=new Object();
obj.name='icessun1'; //将新的对象赋值给obj对象并设置name属性
}
var person=new Object();
setName(person);
alert(person.name); // 弹出icessun 这说明参数的传递是按照值来的
上面的代码说明,参数是按照值传递的,因为在函数setName
中,重新赋值一个对象给obj
,并且改变了其name
属性,要是按照引用传递的话,那么后面弹出来的是icessun1
;结果却不是这样,这说明:
函数内部改变了参数的值,但是原始的引用保持不变;因为在函数内部重写参数时,这个变量引用就是一个局部变量,会在函数执行完毕立即销毁。
类型检测
-
typeof
- 字符串 ------>
string
var n; console.log(typeof n); // undefined
- 数值 ------->
number
- 布尔值 -------->
boolean
-
undefined
------->undefined
- 字符串 ------>
unll == undefined; true
typeof
是检测上面这几种类型的最佳工具,但是对于null
和对象
都返回object
,因为null
表示一个空对象指针,所以typeof null
为Object
;检测函数的时候,会返回function
;这是区分函数和其他对象的方法,函数是一个对象,不是一种数据类型。
对于未声明的变量和为初始化的变量,执行typeof
操作都返回undefined
var message; // 未初始化
// var age; 未声明
console.log(typeof message); // undefined
console.log(typeof age); // undefined
-
instanceof
:检测什么类型的对象(引用类型)
variable instanceof constructor
返回true/false
Object
Array
-
RegExp
如果变量是给定引用类型的实例,那么instanceof
之后返回的结果都是true
;基本类型不是对象,都返回false
。所有的引用类型的值都是Object
的实例。
执行环境和作用域
执行环境定义了变量和函数有权访问的其他数据。决定了各自的行为。某个执行环境中的所有代码执行完毕,该环境就被销毁,保存在里面的所有的变量和函数定义也随之销毁。每个函数都有自己的执行环境,当执行这个函数的时候,会把这个函数推入到一个环境栈里面,直到函数执行完成,再从环境栈里面弹出,交到之前的执行环境。
- 作用域链
- 保证对执行环境有权访问的所有变量和函数的有序访问;其前端,始终是当前执行代码的所在环境的变量对象
环境是函数,则这个活动对象就是arguments对象
。下一个变量对象来自包含环境(包含当前变量对象的外部环境),就这样一直延续到全局执行环境,故全局的执行环境是作用域链的最后一个对象。
- 保证对执行环境有权访问的所有变量和函数的有序访问;其前端,始终是当前执行代码的所在环境的变量对象
var color='blue';
function changeColor(){
if(color==='blue'){
color='red';
}else{
color='blue';
}
}
changeColor();
console.log('Color is now'+color);
上面的代码中,函数的作用域链包含两个对象:自己的变量对象arguments对象
和全局环境的变量对象。可以在函数内部访问到全局的变量,因为在其的作用域链上面可以找到。
作用域链:
- window
- color
- changeColor()
- arguments
每一个环境都可以向上搜索作用域链,查询变量和函数名,但是不能向下搜索作用域链;函数参数也是函数执行环境中内部的变量,局部变量。
-
延长作用域链
-
try-catch语句
中的catch块
with语句
-
-
没有块级作用域
在其他的类
C
语言,由花括号封闭的代码块都有自己的作用域,因而支持根据条件来定义变量。但是JavaScript
没有块级作用域
- 声明变量
使用
var
声明的变量会自动添加到最接近的环境中
:在函数内部,最接近的就是函数的局部环境;在with
语句中,最接近的是函数环境;如果初始化变量时没有使用var
,则会自动把该变量添加到全局环境中。
- **注意**:
在`javascript`中,由`for`语句创建的变量`i`(由`var`声明的),即使循环结束,也依旧存在循环外部的执行环境中。
- 作用域向上搜索
在执行环境中查找一个变量的时候,在局部环境里面找到,就直接使用;否者,继续向上的作用域链查找,没有找到,说明该变量没有声明。
垃圾回收
javascript
中有自动垃圾回收机制;原理就是:找到那些不再需要继续使用的变量,然后释放其占用的内存。垃圾回收机制会按照固定的时间间隔,周期性的去执行这一操作。
- 标记清除
给存储在内存中的变量都加上标记,然后去掉环境中的变量标记以及被环境中的变量引用的变量的标记;之后再加上的标记的变量是当做准备删除的变量,因为环境中的变量已经无法访问到这些变量了;最后,垃圾回收器完成垃圾的回收。 - 计数清除
记录每一个值被引用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候,引用次数为1,如果同一个值又被赋给其他的变量,该值引用次数就加1,包含对这个值引用的变量又取得另外一个值,则这个值的引用次数减1,当次数为0的时候就说明不能访问到,应该回收;坏结果:循环引用。
IE9
把BOM
和DOM
对象都转换成真正的JavaScript
对象。就避免了垃圾回收的问题,也消除了常见的内存泄漏现象。
一般来说:一旦数据不再使用,最好手动将其设置为null
,来释放其引用,适合大多数的全局变量和全局对象的属性。局部变量会在离开执行环境的时候自动解除引用。解除引用,是让值脱离执行环境,等待垃圾回收运行将其回收。
网友评论