目录
- JavaScript的基本数据类型
- JavaScript的执行环境和作用域链
- JavaScript的垃圾收集机制
一.JavaScript的基本数据类型
ECMAScript5中有5中基本数据类型:Undefined、Null、Boolean、Number和String。还有一种复杂数据类型——Object。
1.typeof操作符
用来检测给定变量的数据类型。
var bool = true;
console.log(typeof bool); //boolean
var number = 23;
console.log(typeof number);//number
var string = "String";
console.log(typeof string); // string
var object = Object();
console.log(typeof object); // object
typeof操作符不是函数,不需要使用圆括号。NULL是什么类型呢?
typeof null
返回值为”Object“。
2.Undefined类型
Undefined类型只是一个值,特殊的Undefined。使用var声明变量未初始化时,这个值就是Undefined。
var message;
console.log(typeof message); // undefined
console.log(type of age); // undefined
结果显示,对于未初始化和未定义的变量使用typeof操作符都会返回undefined。
3.Null类型
Null是另一个只有一个值的数据类型,就是特殊值null。从逻辑角度看,null值表示一个空对象指针。也就是前面为什么typeof null
会返回Object的原因。定义一个空对象指针的时候,可以将指针设为null就可以通过相应的变量判断是否是一个对象指针。
if (car != null) {
// 对car对象执行操作
}
实际上,Undefined值是从null值派生的。因此,当我们判断
null == undefined
时,返回true。
4.Boolean类型
JavaScript中经常使用的类型,该类型只有两个字面量:true
和false
。注意这里区分大小写。
通过Boolean()将其他类型转换为Boolean
类型。
数据类型 | 转为true的值 | 转为false的值 |
---|---|---|
String | 任何非空字符串 | 空字符串"" |
Number | 任何非零数值 | 0或NaN |
Object | 任何对象 | null |
Undefined | n/a | undefined |
5.Number类型
JavaScript中使用IEEE754格式来表示整数和浮点数值。常用的有十进制,八进制和十六进制。
var intNum = 10;
var octalNum = 070; // 56(0-7取值)
var hexNum1 = 0xA; //10 (0-F取值)
浮点数值的最高精度是17位小数,但在算数计算时精度远不如整数。
因为浮点数计算时会出现舍如误差,这是IEEE754数值计算的通病。
数值范围,在JavaScript中,最小能表示的值Number.MIN_VALUE
,最大值为Number.MAX_VALUE
。超出表示范围,会被自动转为Infinity
(无穷)。我们可以使用isFinite()
函数来检验是否在范围之间。
NaN,非数值是一个特殊的数值。表示一个本应该返回数值的操作数未返回数值的情况。NaN有两个特点,1. 任何涉及NaN的操作都返回NaN。2. NaN与任何值都不相等,包括本身。
我们可以使用isNaN()
函数,检测参数是否”不是数值“。
数值转换,JavaScript中提供了三个函数:Number()
、parseInt()
和parseFloat()
。第一个函数可以转换任何数据类型,后两个函数专门用于把字符串转成数值。
var num1 = Number("Hello world!"); // NaN
var num2 = Number(""); // 0
var num3 = Number("00011");//11
var num4 = Number(true);//1
使用Number()转换字符串复杂不够合理。处理字符串常用parseInt()
函数,它会更多的关注是否符合数值模式。
var num1 = parseInt("1234abc"); //1234
var num2 = parseInt("");//NaN
var num3 = parseInt("0xA");//10
var num4 = parseInt("070");//56
注意: 在ECMAScript 3中num4为56,在ECMAScript 5中num4为70。
为了解决这个问题,可以给parseInt()
传第二个参数:用来指定转换的进制。
var num = parseInt("0xAF", 16); // 175
//或者
var num = parseInt("AF", 16);
与parseInt()
类似,parseFloat()
也是从第一个字符开始,直达解析到末尾或者遇到无效的浮点数字。另外,始终会忽略前导的零。
var num = parseFloat("070"); //70
6.String类型
String类型用与表示零或多个16位Unicode字符组成的字符序列。在JavaScript中,使用双引号和单引号表示,两者没有任何区别。
字符串是不可变的,一旦创建就不可以修改。
数值转为字符串有两种方式:toString()
和 String()
。第一个方法,返回数值的字符串表示形式。第二个可以将任何类型的值转为字符串,在不知道转值是否为null和undefined时使用。
7.Object类型
ECMAScript中对象其实就是一组数据和功能的集合。
var object = new Object();
创建一个对象,与Java很相似。Object类型是所有他的实例的基础。也就是,Object类型所具有的任何属性和方法同样存在于更具体的对象中。
Object的每个实例具有的属性和方法:
- constructor : 构造函数。用于创建对象的函数。
- hasOwnProperty(propertyName): 用于检查给定的属性在当前对象实例中是否存在。参数以字符串形式表示
o.hasOwnProperty("name")
。 - isPrototypeof(object):用于检查传入的对象是否是当前对象的原型。
- propertyIsEnumerable(propertyName): 用于检查给定的属性是否能够使用for-in语句。
- toLocaleString():返回对象的字符串表示。
- toString(): 返回对象的字符串表示。
- valueOf(): 返回对象的字符串、数值或布尔值表示。
Object是所有对象的基础,因此所有对象都具有这些基本的属性和方法。
二.JavaScript的作用域
JavaScript的变量和其他语言有很大差别。JavaScript的变量是松散类型的本质,所谓的松散类型就是可以保存任何类型的数据。这个本质决定了它只是保存特定值的名字而已。因为这个属性,变量的值和数据类型可以在脚本的生命周期内改变。
1.基本类型和引用类型
ECMAScript中包含两种不同的数据类型值: 基本类型值和引用类型值。基本类型值是指简单的数据段,引用类型值值那些可能由多个值构成的对象。
动态的属性,只能给引用类型值动态添加属性。
复制,基本类型复制值,引用类型复制指针。
传递参数,所有函数的参数都是按值传递的。基本类型值很好理解,直接传递值。但是,引用类型值也是按值传递的。
function setName(objc) {
objc.name = "Owenli";
}
var person = new Object();
setName(person);
console.log(person.name); //Owenli
创建了一个person对象,传递给setName()函数之后被复制给objc。在函数内部,objc和person引用的是同一个对象。即使是按值传递的,objc也会按引用来访问同一个对象。添加name属性后,person也会有反应,因为person指向的对象在堆中只有一个,并且是全局对象。
这是有人会误以为是引用传递,那么看下一个示例:
function setName(objc) {
objc.name = "Owenli";
objc = new Object();
objc.name = "Tom";
}
var person = new Object();
setName(person);
console.log(person.name);
这段代码输出什么呢? 如果你认为输出 Tom,那就入坑了。其实结果是Owenli。即使在函数内部修改了参数的值,但原始的引用仍然保持不变。实际上,虽然在setName()函数中,将新对象赋给objc,这个变量引用的是局部对象,在函数执行完毕后立即被销毁。
检测类型,之前typeof
操作符用来检测基本类型,但是用来检测引用类型需要使用instanceof
操作符。
2.执行环境和作用域
执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为,每个执行环境都有一个与之关联的变量对象(varable object),函数中定义的多有变量和函数都保存到这个对象中。
每个函数都有自己的执行环境,当执行流进入一个函数,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返给之前的执行环境。
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象最开始只包含一个变量,即arguments对象。
举个
var color = "blue";
function changeColor() {
var anotherColor = "red";
function swapColors() {
var tempColor anotherColor;
anotherColor = color;
color = tempColor;
//访问color,anotherColor和tempColor
}
// 访问color和anotherColor,但不能访问tempColor
swapColors();
}
// 只能访问color
changeColor();
作用域链效果图:
内部环境可以通过作用域链访问所有的外部环境,但是外部环境不可以访问内部环境中的任何变量和函数。环境之间的联系是线性、有序的。每个环境都可以向上搜索作用域链,以查询变量和函数名。
延长作用域链,环境类型只有两种:全局和局部(函数),但还有其他方法来延长作用域链。
- try-catch语句的catch块
- with语句
这两个语句都会在作用域链的前端添加一个变量对象。with
语句,就会指定的对象添加到作用域链中。catch
语句,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
没有块级作用域,和其他语言相比,JavaScript没有块级作用域。例如,
if (true) {
var color = "blue";
}
console.log(color);
在C语言中,会在if语句结束后销毁color。但是在JavaScript中,if中声明的变量会被添加到当前的执行环境中(上面是全局环境)。
for (var i = 0; i < 10; i ++) {
///dosomething
}
console.log(i); // 10
注意:JavaScript中,即使for循环结束,i仍然存在于当前的执行环境中。
变量声明,使用var声明的变量会被添加到最近的环境中。如果声明没有使用var则会被添加到全局环境中。
标识符搜索,搜索过程是从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果局部环境中存在着同名标识符,那么就不会使用位于父级的标志符。
三.垃圾收集
JavaScript具有自动垃圾收集机制,执行环境负责管理代码执行过程中使用的内存。垃圾回收原理:垃圾收集器会周期地找到那些不在使用的变量,然后释放其内存。两种垃圾回收策略:标记清除和引用计数。
1.标记清除
JavaScript中最常用的垃圾收集方法标记清除。当变量进入环境标记为“进入环境”,此状态下变量可能会用到。当变量离开环境时,将其标记为“离开环境”,此时可以被垃圾收集器回收。
2.引用计数
在Objective-C中使用引用计数。关于引用计数可以看《iOS高级编程》第一章。注意引用计数存在循环引用问题。
性能问题,由于垃圾收集器是周期运行的,所以确定时间间隔是一个非常重要的问题。IE曾因为这个出现过性能问题。
管理内存,出于安全角度考虑,给Web浏览器分配的内存数量通常比分配给桌面应用的少。因此,一旦数据不再用,最好通过将其值设置为null来释放其引用——接触引用。解除一个引用不代表立即被回收,只是让其脱离执行环境,以便垃圾收集器下次运行时将其回收。
小结
- 首先,学习javaScript的基本数据类型,有其他编程语言基础,学起来很简单,只需记住不同点即可。
- 其次,学习javaScript的作用域链和执行环境,理解作用域链和执行环境等概念对后期的学习很有帮助,需要充分理解。
- 最后,javaScript的垃圾收集机制,最常用的是标记清除。iOS中使用的是引用计数。
- 下一篇学习JavaScript中对象。
参考
- 《JavaScript高级程序设计》
- 《JavaScript - MDN》
- Tracing garbage collection - wiki