文章目录
第三章 类型、值和变量
- 数据类型分类
- 原始类型(primitive type)
- 数字
- 字符串
- 布尔值
- 特殊的原始类型
- null
- undefined
- 对象类型(object type):属性的集合,每个属性都由名/值对构成。(值可以是原始类型,也可以是对象类型)
- 特殊对象
- 全局对象
- 数组对象:一般对象都是无序的集合,但数组对象是带有编号的有序的集合
- 函数:含有可执行代码的对象
- 类:!!!
- 日期类(Date)
- 正则类(RegExp)
- 错误类(Error)
- 类:!!!
- 特殊对象
- 原始类型(primitive type)
对象类型是可以调用自己的方法
比如数组对象a排序时可以a.sort()
值得注意的是,原始类型中数字、字符串、布尔值也可以拥有自己的方法,那么只有null和undefined没办法拥有方法
- 变量:无类型的(untyped),可以使用var关键字来赋予变量任意类型的值
- 全局变量:不在函数内声明的变量
- 局部变量:在函数内声明的变量
3.1数字
- JavaScript中所有数字均用浮点数值表示,并不区分整数值和浮点数值
- 数字直接量:直接出现在JavaScript程序中的数字
值得注意的是,在
12
前面加上负号我们可以得到它的负数-12
,但这个负号是一元求反运算符,它并不是数字直接量的组成部分。
3.1.1 整型直接量
- 十进制整型直接量:
0,1,2,3,1024,65536
- 十六进制整型直接量:
0Xff,0xff //15*16+15=255(十进制)
3.1.2 浮点型直接量
- 语法表示 [digits][.digits][(E|e)[(+|-)]digits]
例如:
3.14
1.33
.3333
6.02e23 //6.02*10^23
1.4728233E-32 //1.4728233*10^-32
3.13 JavaScript中的算术运算
-
上溢(overflow)、下溢(underflow)、被零整除不会报错
- 上溢结果会为
infinity
、-infinity
//表示无穷大 - 下溢结果会为
0
,-0
//很少用到-0 - 被零整除、无穷大相除、给负数开方、非数字的操作数会用NaN表示 //表示
not-a-number
,“非数字”
- 上溢结果会为
-
JavaScript中非数字值(包括
NaN
、字符串和对象)的特殊性:它和任何值都不相等,包括自己
也就是说,没办法通过
x=NaN
来判断x
是否为NaN
但可以通过x!=x
来判断,当x
等于NaN
的时候,表达式的结果为true
,这是因为NaN
的特殊性所决定的。
- 负零值的特殊性:除了作为除数之外,它和正零值相等
var zero = 0; //正常的零值
var negz = -0; //负零值
zero == negz; //=>true,这说明正零值和负零值相等
1/zero==1/negz; //=>false,这说明正无穷大和负无穷大不相等
3.1.4 二进制浮点数和四舍五入错误
- JavaScript采用IEEE-754浮点数表示法,这是一种二进制法,可以精确表示
$1/2$
,$1/8$
,$1/1024$
,遗憾的是,对于$1/10$
,$1/100$
这样的十进制的分数却不能精确表示
//对于这样简单的数字也不能精确表达
var x=.3-.2 //3分钱-2分钱
var y=.2-.1 //2分钱-1分钱
x==y //=>false,两个值并不相等
x==.1 //=>false,说明.3-.2!=.1
y==.1 //=>true,说明.2-.1==.1
/*
*原因是因为0.3-0.2和0.2-0.1的值进行四舍五入之后不相等
*验证了Java语言,一样的结果
*最好的解决方法是:用整数来计数
*/
3.1.5 日期和时间
- JavaScript语言核心包括Date()构造函数,用来创建表示日期和时间的对象。
3.2 文本
- 字符串(string)是一组16位值组成的不可变的有序序列
- 每个字符通常来自于Unicode字符
- 字符集
- 采用UTF-16编码的Unicode字符集
- 常用Unicode字符通过16位内码表示,不能表示则用两个16位值组成的一个序列(亦称“代理项对”)表示,这以为这这些不能表示的字符串长度为2
var p="π"; `
p.length //=>1,p包含一个16位值
var e="e";
e.length //=>2,e由UTF-16编码后包含两个16位值:"\ud835\udc52"
- JavaScript不会对字符串做标准化处理,不会保证字符串是合法的UTF-16格式,此后果是一定程度上会导致乱码
3.2.1 字符串直接量
- 可以用单引号也可以用双引号
""空字符,长度为0
"hello"
'testing'
- 单引号可以包含双引号,双引号也可以包含单引号
'name="myform"'
"Wouldn't you prefer O'Reilly's book?"
- ECMAScript3中,字符串直接量必须写在一行,另起一行可以用转义字符\n
- ECMAScript中,字符串直接量可以拆分成数行,每行必须以反斜线“\”结束
"two\nlines"
"one\
long\
line"
- 编码风格:JavaScript代码和HTML代码有时会混合在一起,最好是写JavaScript字符串直接量时使用单引号,写HTML字符串直接量时使用双引号
<button onclick="alert('Thank you')">Click Me</button>
3.2.2 转义字符(escape sequence)
- 当我们使用单引号表示字符串直接量时,如果字符串直接量中也需要用到单引号的话,这时候转义字符就会很有用了
var s='I can\'t tell you!'
3.2.3 字符串的使用
- 两个字符串相加用“+”
- 字符串还提供许多可以调用的方法
3.2.4 模式匹配
- JavaScript定义了
RegExp()
构造函数,用来创建“正则表达式”
/*
*用两条斜线来包括一个正则表达式直接量
*第二条斜线之后跟随一个及以上的字母,用来修饰正则表达式的含义
*/
/^HTML/ //匹配以HTML开始的字符串
/[1-9][0-9]*/ //匹配一个非零数字,后面是任意个数字
/\bjavascript\b/i //匹配单词“javascript”,忽略大小写
3.3 布尔值
true
和false
- 假值和真值
- 假值:类型转化为布尔值时为
false
的值
1.undefined
2.null
3.0
4.-0
5.NaN
- 真值:假值以外都为真值
- 假值:类型转化为布尔值时为
if(i!==null)...
//这种情况下,只有当i不等于null时语句结果才为true,进而执行if后面的代码
if(i)
//在这种情况下,只有当i不等于undefined、null、0、-0、NaN时语句结果才为true
- 布尔运算符
- &&:与(AND)
- ||:或(OR)
- !:非(NOT)
3.4 null和undefined
null
:可以表示数字、字符串和对象是“无值”的一个特殊的对象undefined
:更深层次的“空值”,可表示变量还没有初始化,也可以表示对象属性或数组元素不存在- 不同点
- 性质不一样
null
是关键字undefined
是全局变量- 它的值就是“undefined”(在ECMAScript5后,undefined只能读)
- 类型是undefined类型,说明它是这个类型里面唯一的成员
- 用处不一样
null
表示程序级的、正常的或意料之中的值的空缺undefined
表示系统级的、出乎意料的或雷士错误的值的空缺
- 性质不一样
- 相同点
- 含义相等:都表示值的空缺(如果用“==”比较的话两者是相等的)
- 都为假值:转化为布尔值时都为假值
3.5 全局对象(global object)
- JavaScript解释器启动时(或者任何Web浏览器加载新页面时),它将创建一个新的全局对象,全局对象包含
- 全局属性:比如
undefined
、Infinity
和NaN
- 全局函数:比如
isNaN()
、parseInt()
和eval()
- 构造函数:比如
Date()
、RegExp()
、String()
、Object
和Array()
- 全局对象:比如
Math
和JSON
- 全局属性:比如
全局对象的初始属性不是保留字,但他们应该当做保留字来对待
3.6 包装对象
- 对象
- 属性:与JavaScript对象相关的值,可用“
.
”符号来引用属性值 - 方法:属性的子集,当属性值为函数时,称其为“方法”
- 属性:与JavaScript对象相关的值,可用“
- 字符串也同样具有属性和方法
var s="Hello World!";
var word=s.substring(s.indexOf("")+1,s.lenth);
字符串既然不是对象,为什么它也会有属性?
答:只有引用字符串s的属性时,JavaScript会调用new String(s)
创建一个字符串的包装对象,来处理字符串属性的引用,引用结束后这个包装对象就销毁了。
- 包装对象:存取字符串、数字和布尔值的属性时创建的临时对象称为包装对象
var s="test";
s.len=4;
var t=s.len;
console.log("t="+t);
//控制台输出:t=undefined
//这是因为4赋给的是一个新创建的包装对象,而s.len又是作为另一个包装对象赋给t
3.7 不可变的原始值和可变的对象引用
- 引用类型:对象的别称,用来和JavaScript的基本类型区别开来
- 对象的复制、比较都是引用的复制、比较
var a=[1,2,3];
var b=a;
b===a; //=>true,说明a和b引用同一个数组对象
- 如果真的想得到对象的副本,就必须显式赋值对象的每个属性
var a=['a','b','c'];
var b=[];
for (var i=0;i<a.lenth;i++){
b[i]=a[i];
}
- 如果真的想比较,则必须比较它们的属性或元素
3.8 类型转换
值 | 字符串 | 数字 | 布尔值 | 对象 |
---|---|---|---|---|
undefined |
"undefined" |
NaN |
false |
throws TypeError |
null |
"null" |
0 |
false |
throws TypeError |
"" |
0 |
false |
new String("") |
|
"1.2" |
1.2 |
true |
new String("1.2") |
|
"one" |
NaN |
true |
new String("one") |
|
{} |
参考3.8.3节 | 参考3.8.3节 | true |
|
[] |
"" |
0 |
true |
|
[9] |
"9" |
9 |
true |
|
['a'] |
使用join() 方法 |
NaN |
true |
|
function(){} |
参考3.8.3节 | NaN |
true |
3.8.1 转换和相等性
4.9.1节详细讲解了 "="运算符做了哪些类型转换,同样介绍了 "="恒等运算符并未做任何类型转换
3.8.2 显式类型转换
- 为什么要做显式转换?不得已或使代码易读
- 最简单的方法:使用
Boolean()
、Number()
、String()
和Object()
var a=Number("3");
var b=String(false);
var c=Boolean([]);
var d=Object(3);
console.log(a); //结果为:3
console.log(b); //结果为:"false"(或使用false.toString()方法)
console.log(c); //结果为:true
console.log(d); //结果为:new Number(3)
值得注意的是,除了null和undefined之外任何值都具有
toString()
方法
- Number类的toString()方法可以指定转换基数(范围是2~36进制)
Java无此特性
binaryString=a.toString(2);
octalString=a.toString(8);
hexString=a.toString(16);
console.log(binaryString); //结果为:"10001"
console.log(octalString); //结果为:"21"
console.log(hexString); //结果为:"11"
- Number类处理小数时
toFixed()
方法:可指定小数点后位数,默认toExponential()
方法:使用指数计数法,小数点前只有一位,可指定小数点toPrecison()
方法:上述两种方法的体现,可指定有效位数,位数小于数字整数部分时会转换为指数计数形式。
var n=123456.789;
console.log(n.toFixed()); //123457
console.log(n.toFixed(0)); //123457
console.log(n.toFixed(2)); //123456.79
console.log(n.toFixed(5)); //12345.78900
console.log(n.toExponential()); //1.23456789e+5
console.log(n.toExponential(1)); //1.2e+5
console.log(n.toExponential(3)); //1.235e+5
console.log(n.toPrecision()); //123456.789
console.log(n.toPrecision(4)); //1.235e+5
console.log(n.toPrecision(7)); //123456.8
console.log(n.toPrecision(10)); //123456.7890
parseInt()
函数和parseFloat()
函数(它们是全局函数,不从属于任何类的方法)更加灵活。parseInt()
只解析整数,而parseFloat()
则可以解析整数和浮点数。如果字符串前缀是"0x
“或者”0X
",parseInt()
将其解释为十六进制数,parseInt()
和parseFloat()
都会跳过任意数量的前导空格,尽可能解析更多数值字符,并忽略后面的内容。如果第一个非空格字符是非法的数字直接量,将最终返回NaN
:
parseInt("3 blind mice")//=>3
parseFloat("3.14 meters")//=>3.14
parseInt("-12.34")//=>-12
parseInt("0xFF")//=>255
parseInt("0xff")//=>255
parseInt("-0XFF")//=>-255
parseFloat(".1")//=>0.1
parseInt("0.1")//=>0
parseInt(".1")//=>NaN:整数不能以"."开始
parseFloat("$72.47");//=>NaN:数字不能以"$"开始
parseInt()可以接收第二个可选参数,这个参数指定数字转换的基数,合法的取值范围是2~36,例如:
parseInt("11",2);//=>3(1*2+1)
parseInt("ff",16);//=>255(15*16+15)
parseInt("zz",36);//=>1295(35*36+35)
parseInt("077",8);//=>63(7*8+7)
parseInt("077",10);//=>77(7*10+7)
3.8.3 对象转换为原始值
- 对象–>布尔值:所有的对象(包括数组和函数)都转换为
true
- 对象–>字符串\数字:
- toString()方法:
- valueOf()方法:如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值,因此默认的valueOf()方法简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的valueOf()方法只是简单返回对象本身。日期类定义的valueOf()方法会返回它的一个内部表示:1970年1月1日以来的毫秒数。
这里提到的字符串和数字的转换规则只适用于本地对象(native object)。宿主对象(例如,由Web浏览器定义的对象)根据各自的算法可以转换成字符串和数字。
3.9 变量声明
- 关键字
var
声明变量
<!-- 可以声明一个或多个 -->
var i;
var j,k;
<!-- 可以将变量的初始赋值和声明合写在一起 -->
var message="hello";
var i=0,j=0,k=0;
如果声明了变量但为赋值的话,那么它的初始值就为
undefined
。
3.10 变量作用域
- 变量的作用域:程序源代码中定义这个变量发挥作用的区域
- 全局变量拥有全局作用域
- 局部变量只有局部作用域
局部变量的优先级高于同名的全局变量
3.10.1 函数作用域或声明提前
- 函数作用域:函数体中,不管变量在哪里声明,它在整个函数体中都算是有定义的
JavaScript没有像C语言这样的块级作用域的概念,取而代之的是函数作用域
function f1(){
for(var k=0;k<10;k++){ //k在整个函数体内都算是有定义的,而不仅仅是在循环内
console.log(k)
}
cosole.log(k); //k已经定义了,输出10
}
- 声明提前:变量在声明之前就可以使用了,不过值可能为
undefined
var scope = "global";
function f2(){
console.log(scope);//输出“undefined”,而不是“global”
var scope="local";
console.log(scope);//输出“local”
}
3.10.2 作为属性的变量
- 当声明一个JavaScript全局变量时,实际上是定义了全局对象的一个属性。
当使用var声明一个变量时,创建的这个属性是不可配置的(见6.7节),也就是说这个变量无法通过delete运算符删除。可能你已经注意到了,如果你没有使用严格模式并给一个未声明的变量赋值的话,JavaScript会自动创建一个全局变量。以这种方式创建的变量是全局对象的正常的可配值属性,并可以删除它们:
var truevar=1;//声明一个不可删除的全局变量
fakevar=2;//创建全局对象的一个可删除的属性
this.fakevar2=3;//同上
delete truevar//=>false:变量并没有被删除
delete fakevar//=>true:变量被删除
delete this.fakevar2//=>true:变量被删除
3.10.3 作用域链
-
换个角度来解读变量作用(将局部变量也看做对象的属性):每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。当JavaScript需要查找变量x的值的时候(这个过程称做“变量解析”(variable resolution)),它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,JavaScript会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性x,那么就认为这段代码的作用域链上不存在x,并最终抛出一个引用错误(ReferenceError)异常。
-
只有全局变量
graph LR
A(全局对象)-->B(var global1)
A-->C(var global2)
A-->D(var global3)
- 不含嵌套的函数体内
graph TD
A(定义函数参数和局部变量的对象)-->B(全局对象)
- 含有嵌套函数的函数体
graph TD
A(定义嵌套函数参数和局部变量的对象)-->B
B(定义函数参数和局部变量的对象)-->C(全局对象)