【类型、值、变量】数字、文本、布尔值、null和undefined、全局对象、包装对象、不可变的原始值和可变的对象引用、类型转换、变量声明、变量作用域

P44~71(PDF版)、P32~59(书版)

总述:

计算机程序的运行需要对值(value)进行操作。在编程语言中,能够表示并操作的值的类型称为数据类型(type),编程语言最基本的特性就是能够支持多种数据类型。

当程序需要将值保存起来以备将来使用时,便将其赋值给(将值“保存”到)一个变量(variable)。

变量是一个值的符号名称,可通过名称来获得对值的引用。

JavaScript的数据类型分为两类:原始类型(primitive type)和对象类型(object type)。
JavaScript中的原始类型包括数字、字符串和布尔值。

JavaScript中有两个特殊的原始值:null(空)和undefined(未定义),它们不是数字、字符串和布尔值,通常分别代表了各自特殊类型的唯一的成员。

JavaScript中除了数字、字符串、布尔值、null和undefined之外就是对象了。
对象(object)是属性(property)的集合,每个属性都由“名/值对”(值可以是原始值,也可以是对象)构成。
其中有一个比较特殊的对象——全局对象(global object)。

普通的JavaScript对象是“命名值”的无序集合。JavaScript同样定义了一种特殊对象——数组(array),表示带编号的值的有序集合。  

JavaScript还定义了另一种特殊对象——函数。函数是具有与它相关联的可执行代码的对象,通过调用函数来运行可执行代码,并返回运算结果。

如果函数用来初始化(使用new运算符)一个新建的对象,我们称之为构造函数(constructor)。

每个构造函数定义了一类(class)对象——由构造函数初始化的对象组成的集合。
类可看做是对象类型的子类型。除了数组(Array)类和函数(Function)类之外,JavaScript语言核心定义了其他三种有用的类。

日期(Date)类:定义了代表日期的对象;正则(RegExp)类:表示正则表达式,一种强大的模式匹配工具;错误(Error)类:定义了那些表示JavaScript程序中运行时错误和语法错误的对象;
可通过定义自己的构造函数来定义需要的类。

JavaScript解释器有自己的内存管理机制,可自动对内存进行垃圾回收(garbage collection)。这意味着程序可按需创建对象,程序员不必担心这些对象的销毁和内存回收。当不再有任何引用指向一个对象,解释器就会知道这个对象没用了,然后自动回收它所占用的内存资源。

JavaScript是一种面向对象的语言。不严格地讲,这意味着我们不用全局的定义函数去操作不同类型的值,数据类型本身可定义方法(method)来使用值。

例子:要对数组a中的元素进行排序,不必将a传入sort()函数,而是调用a的一个方法sort():

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

方法的定义,从技术上讲,只有JavaScript对象才能拥有方法。然而,数字、字符串和布尔值也可拥有自己的方法。在JavaScript中,只有null和undefined 无法拥有方法的值。

JavaScript的类型可分为原始类型和对象类型,也可分为拥有方法的类型和不能拥有方法的类型,同样可分为可变(mutable)类型和不可变(immutable)类型。

可变类型的值是可修改的。

对象和数组是可变类型:JavaScript程序可更改对象属性值和数组元素的值。

数字、布尔值和数组属于不可变类型。

字符串可看成由字符组成的数组,在JavaScript中,字符串是不可变的:可访问字符串任意位置的文本,但JavaScript并未提供修改已知字符串的文本内容的方法。

JavaScript可自由地进行数据类型转换。
如果在程序期望使用字符串的地方使用了数字,JavaScript会自动将数字转换为字符串。如果在期望使用布尔值的地方使用了布尔值,也会进行相应的转换。

JavaScript变量是无类型的(untyped),变量可被赋予任意类型的值。使用var关键字来声明(declare)变量。

JavaScript采用词法作用域(lexcial scoping)。不在任何函数内声明的变量称做全局变量(global variable),它在JavaScript程序中的任何地方都是可见的。

在函数内声明的变量具有函数作用域(function scope),并且只在函数内可见。

【数字】

JavaScript不区分整数值和浮点数值。JavaScript中的所有数字均用浮点数值表示。

JavaScript采用IEEE 754标准定义的64位浮点格式表示数字,它能表示的最大值:±1.7976931348623157×10,最小值±5×10。

按JavaScript中的数字格式,能表示的整数范围:从 -9 007 199 254 740 992~9 007 199 254 740 992(即 -2~2),包括边界值。

如果使用超过此范围的整数,无法保证地位数字的精度。然而需要注意:JavaScript中实际的操作(比如数组索引,位操作符等)则是基于32位整数。

1.整型直接量

在JavaScript程序中,用一个数字序列表示一个十进制整数。

例如:0,3,10000000

JavaScript同样能识别十六进制(以16为基数)值。十六进制的直接量是指以“0x”或“0X”为前缀,其后跟随十六进制数串的直接量。

十六进制值是0~9之间的数字和a (A) ~f (F) 之间的字母构成,a~f的字母对应的表示数字10~15。十六进制整型直接量的例子:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

最好不要使用以0为前缀的整型直接量(八进制直接量)。在ECMAScript 6 的严格模式下,八进制直接量是禁止的。

2. 浮点型直接量

浮点型直接量可以含有小数点。采用的是传统的实数写法。一个实数由整数部分、小数点和小数部分组成。

还可以用指数记数法表示浮点型直接量,即在实数后跟字母e或E,后面再跟正负号,其后再加一个整型的指数。表示的数值是由前面的实数乘以10的指数次幂。

例子:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

前面实数部分相同,e23 → 10,E-32 → 10

3.JavaScript中的算术运算

基本算术运算符:加、减、乘、除 → →(+、-、*、/),求余%

复杂运算,通过Math对象的属性定义的函数和常量来实现:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

JavaScript中的算术运算在溢出(overflow),下溢(underflow)或被零整除时不会报错。

当数字运算结果超过了JavaScript所能表示的数字上限(溢出),结果为一个特殊的无穷大(infinity)值,在JavaScript中用Infinity表示;

当负数的值超过了JavaScript所能表示的负数范围,结果为负无穷大,用-Infinity表示。

下溢(underflow)是当运算结果无限接近于零并比JavaScript能表示的最小值还小的时候发生的一种情形。这种情况,JavaScript将会返回0。

当一个负数发生下溢时,JavaScript返回一个特殊的值“负零”。这个值(负零)几乎和正常的零完全一样,JavaScript程序员很少用到负零。

被零整除在JavaScript中并不会报错:它只是简单的返回无穷大(Infinity)或负无穷大(-Infinity)。有一个例外:

零除以零是没有意义的,这种整除运算结果也是一个非数字值,用NaN表示。

无穷大除以无穷大、给任意负数开方运算、算术运算符与不是数字或无法转换为数字的操作数一起使用时都将返回NaN。 

JavaScript预定义了全局变量Infinity和NaN,用来表示正无穷大和非数字值。

在ECMAScript 3 中,这两个值是可读/写的,并可修改。ECMAScript 5修正了这个错误,将它们定义为只读的。

在ECMAScript 3 中Number对象定义的属性值也是只读的。例子:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

JavaScript中的非数字值:它和任何值都不相等,包括自身。即,没有办法通过x==NaN来判断变量是否是NaN。

相反,应使用 x ! =x 来判断,当且仅当x为NaN的时候,表达式的结果才为true。

函数 isNaN()的作用与此类似,如果参数是NaN或者是一个非数字值(比如字符串和对象),则返回true。

JavaScript中有一个类似的函数 isFinite(),在参数不是NaN、Infinity或 -Infinity的时候返回true。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

负零值和正零值几乎一模一样,只在作为除数时不同。

4. 二进制浮点数和四舍五入错误

实数有无数个,但JavaScript通过浮点数的形式只能表示其中有限的个数(18 437 736 874 454 810 627 个),即,当在JavaScript中使用实数的时候,常常只是真实值的一个近似表示。

JavaScript采用了IEEE-754浮点数表示法,是一种二进制表示法,可精确地表示分数,比如1/2,1/8,1/1024,但我们常用的分数(特别金融计算方面),都是十进制分数1/10、1/100等。二进制浮点数表示法并不能精确表示类似0.1这类数字。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

由于舍入误差,0.3和0.2之间的近似差值实际上并不等于0.2和0.1之间的近似差值。这个问题并不只在JavaScript中才会出现:在任何使用二进制浮点数的编程语言中都会出现这个问题。需注意的是,上述代码中x和y的值非常接近彼此和最终的正确值。

5. 日期和时间

Date()构造函数 用来表示日期和时间的对象。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

【文本】

字符串(string)是一组又16位值组成的不可变的有序序列,每个字符通常来自于Unicode字符集。

JavaScript通过字符串类型来表示文本。字符串的长度(length)是其所含16位值的个数。

JavaScript字符串(和其数组)的索引从零开始:第一个字符的位置是0,第二个字符的位置是1。

空字符串(empty string)长度为0,JavaScript中并没有表示单个字符的“字符型”。要表示一个16位值,只需将其赋值给字符串变量即可,这个字符串长度为1。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

1.字符串直接量

在JavaScript程序中的字符串直接量,是由单引号或双引号括起来的字符序列。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

在ECMAScript 3中,字符串直接量必须写在一行中,而在ECMAScript 5中,字符串直接量可拆分成数行,每行必须以反斜杠(\)结束,反斜杠和行结束符都不算是字符串直接量的内容。如果希望在字符串直接量中另起一行,可使用转义字符 \n:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

当使用单引号来界定字符串时,遇到英文中的缩写和所有格写法时,必须使用fanxie反斜线(\)来转义所有的撇号。

JavaScript代码和HTML代码混杂一起时。各自使用独立的引号风格。

2.转义字符

反斜线(\)后面加一个字符:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

3.字符串的使用

(+)作用于字符串时,表示字符串连接。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

要确定一个字符串的长度——其包含的16位值的个数,比如要得到字符串s的长度:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

在JavaScript中字符串是固定不变的,类似replace()和toUpperCase()的方法都返回新的字符串,原字符串本身并没发生改变。

在ECMAScript 5 中,字符串可当做只读数组,除了使用chartAt()方法,也可使用方括号来访问字符串的单个字符(16位值): 

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

4.模式匹配

RegExp()构造函数,用来创建文本匹配模式的对象。这些模式成为“正则表达式”(regular expression),JavaScript采用Perl中的正则表达式语法。

String和RegExp对象均定义了利用正则表达式进行模式匹配和查找与替换的函数。

RegExp和Date一样,是一种具有实用API的特殊对象,它的语法复杂,API丰富。

RegExp是一种非常强大和常用的文本处理工具。

尽管RegExp并不是JavaScript语言中的基本数据类型,但它们依然具有直接量写法,可直接在JavaScript程序中使用。

在两条斜线之间的文本构成了一个正则表达式直接量。第二条斜线之后可跟随一个或多个字母,用来修饰匹配模式的含义。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

【布尔值】

布尔值指代真或假、开或关、是或否。这个类型只有两个值,保留字true和false。

JavaScripty程序中的比较语句的结果通常都是布尔值。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

任意JavaScript值都可转换为布尔值,以下值会被转换成 false:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

所有其他值,包括所有对象(数组)都会转换成true。

三个重要的布尔运算符:&&、||、!

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

【null和undefined】

null(关键字),常用来描述“空值”。对null执行typeof预算,结果返回字符串“object”,即,可将null认为是一个特殊的对象值,含义是“非对象”。

但实际上,通常认为null是它自有类型的唯一一个成员,它可表示数字、字符串和对象是“无值”。

JavaScript还有第二个值表示值的空缺。用未定义的值表示更深层次的“空值”。它是变量的一种取值,表明变量没有初始化,如果要查询对象属性或数组元素的值时返回undefined则说明这个属性或元素不存在。如果函数没有返回任何值,则返回undefined。

undefined是预定义的全局变量(它不是关键字),它的值就是“未定义”。

在ECMAScript 3中,undefined是可读/写的变量,可给它赋任意值。这个错误在ECMAScript 5中做了修正,undefined在该版本中是只读的。

尽管null和undefined是不同的,但它们都表示“值的空缺”,两者往往可互换。相等运算符“==”认为两者是相等的,要使用严格相等运算符“===”来区分它们。

null和undefined都不包含任何属性和方法。使用“.”或“[ ]”来存取这两个值的成员或方法都会产生一个错误类型。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

  

【全局对象】

全局对象在JavaScript中有着重要的用途:全局对象的属性是全局定义的符号,JavaScript程序可直接使用。当JavaScript解释器启动时(或者任何浏览器加载新页面的时候),它将创建一个新的全局对象,并给它一组定义的初始属性:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

不在任何函数内的JavaScript代码——可使用JavaScript关键字this来引用全局对象:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

当初次创建的时候,全局对象定义了JavaScript中所有的预定义全局值。这个特殊对象同样包含了为程序定义的全局值。

如果代码声明了一个全局变量,这个全局变量就是全局对象的一个属性。

【包装对象】

JavaScript对象是一种复合值:它是属性或已命名值的集合。通过“.”符号来引用属性值。当属性值是一个函数时,称其为方法。通过o.m()来调用对象o中的方法。

字符串也具有属性和方法:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

当运行这段代码时,t 的值是undefined。第二行代码创建一个临时字符串对象,并给其len属性赋值为4,随即销毁这个对象。

第三行通过原始的(没有被修改过)字符串值创建一个新字符串对象,尝试读取其len属性,这个属性自然不存在,表达式求值结果为undefined。

这段代码说明了在读取字符串、数字和布尔值的属性值(或方法)的时候,表现得像对象一样。但如果你试图给其属性赋值,则会忽略这个操作:

修改只是发生在临时对象上,而这个临时对象并未继续保留下来。

存取字符串、数字或布尔值的属性时创建的临时对象称为包装对象,它只是偶尔用来区分字符串和字符串对象、数字和数值对象以及布尔值和布尔对象。

通常,包装对象只是被看做是一种实现细节,而不用特别关注。由于字符串、数字和布尔值的属性都是只读的,并不能给它们定义新属性,它们有别于对象。

需注意,可通过String(),Number(),或Boolean()构造函数来显式创建包装对象:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

JavaScript会在必要时将包装对象转换为原始值,因此上段代码中的对象S、N和B常常——但不总是——表现的和值s、n和b一样。

“==”等于运算符将原始值和包装对象视为相等,但“===”全等运算符将它们视为不等。通过typeof运算符可看到原始值和其包装对象的不同。

【不可变的原始值和可变的对象引用】

JavaScript中的原始值(undefined、null、布尔值、数字和字符串)与对象(包括数组和函数)有着根本区别。

原始值是不可更改的:任何方法都无法更改(或突变)一个原始值。

字符串中所有的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

原始值的比较是值的比较,只有在它们的值相等时它们才相等。

如果比较两个单独的字符串,当且仅当它们的长度相等且每个索引的字符都相等时,JavaScript才认为它们相等。

对象和原始值不同,首先,它们是可变的——它们的值是可以修改的:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

对象的比较并非值的比较:即使两个对象包含同样的属性及相同的值,它们也是不相等的。各个索引元素完全相等的两个数组也不相等。   

 笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

我们通常将对象称为引用类型(reference type),以此来和JavaScript的基本类型区分开来。

按照术语的叫法,对象值都是引用(reference),对象的比较均是引用的比较:

当且仅当它们引用同一个基对象时,它们才相等。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

将对象(或数组)赋值给变量,仅仅是赋值的引用值:对象本身并没复制一次。如果你想得到一个对象或者数组的副本,则必须显式复制对象的每个属性或数组的每个元素。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

如果要比较两个单独的对象或者数组,则必须比较它们的属性或元素。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

【类型转换】

当JavaScript期望使用一个布尔值时,你可提供任意类型值,JavaScript将根据需要自行转换类型。一些值(真值)转换为true,其他值(假值)转换为false。

如果JavaScript期望使用一个字符串,它把给定的值将转换为字符串。如果JavaScript期望使用一个数字,它把给定的值将转换为数字(如果转换结果无意义的话将返回NaN)。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

 1.转换和相等性

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

需特别注意:一个值转换为另一个值并不意味着两个值相等。

比如在期望使用布尔值的地方使用了undefined,它将转换为false,但这并不表明undefined==false。

JavaScript运算符和语句期望使用多样化的数据类型,并可互相转换。

if语句将undefined转换为false,但“==”运算符并不试图将其操作数转换为布尔值。

2.显式类型转换

尽管JavaScript可自动做许多类型转换,但有时仍需要做显式转换,或者为了使代码变得清晰易读而做显式转换。

做显式类型转换最简单的方法:使用Boolean()、Number()、String()或Object()函数。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

需注意:除了null或undefined之外的任何值都具有toString()方法,这个方法的执行结果通常和String()方法的返回结果一致。如果试图把null或undefined转换为对象,则会抛出一个类型错误(TypeError)。Object()函数在这种情况下不会抛出异常:它仅简单地返回一个新创建的空对象。

JavaScript中某些运算符会做隐式的类型转换,有时用于类型转换。

如果“+”运算符的一个操作数是字符串,它将会把另一个操作数转换为字符串。一元“+”运算符将其操作数转换为数字。

一元“!”运算符将其操作数转换为布尔值并取反。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

Number类定义的toString()方法可接收表示转换基数(radix)的可选参数,如果不指定此参数,转换规则将是基于十进制。也可将数字转换为其他进制数。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

当处理财务或科学数据的时候,在做数字到字符串的转换过程中,想控制输出中小数点位置和有效数字位数,决定是否需要指数记数法。

Number类为这种数字到字符串的类型转换场景定义了三个方法。

toFixed() 根据小数点后的指定位数将数字转换为字符串,它从不使用指数记数法。

toExponetial() 使用指数记数法将数字转换为指数形式的字符串,其中小数点前只有一位,小数点后的位数则由参数指定(即有效数字位数比指定的位数要多一位)。

toPrecision() 根据指定的有效数字位数将数字转换为字符串。如果有效数字的位数少于数字整数部分的位数,则转换成指数形式。

所有三个方法都适当地进行四舍五入或填充0。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

如果通过Number()转换函数传入一个字符串,它将试图将其转换为一个整数或浮点数直接量,这个方法只能基于十进制进行转换,且不能出现非法的尾随字符。

parseInt()函数和parseFloat()函数(它们是全局函数,不从属于任何类的方法)更加灵活。

parseInt()只解析整数,而parseFloat()可解析整数和浮点数。

如果字符串前缀是“0x”或者“0X”,parseInt()将其解释为十六进制数,parseInt()和parseFloat()都会跳过任意数量的前导空格,尽可能解析更多数值字符,并忽略后面的内容。

如果第一个非空格字符是非法的数字直接量,将最终返回NaN:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

3.对象转换为原始值

对象到布尔值的转换:所有的对象(包括包括数组和函数)都转换为true。

对于包装对象:new Boolean(false)是一个对象而不是原始值,它将转换为true。

对象到字符串和对象到数字的转换是通过调用待转换对象的一个方法来完成的。麻烦的是,JavaScript对象有两个不同的方法来执行转换。

值得注意:这里提到的字符串和数字的转换只适用于本地对象(native object)。宿主对象(例如由Web浏览器定义的对象)根据各自的算法可转换成字符串和数字。

所有的对象继承了两个转换方法。第一个 是toString(),作用是返回一个反映这个对象的字符串。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

很多类定义了更多特定版本的toString()方法。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

另一个转换对象的函数是valueof()。如果存在任意原始值,它就默认将对象转换为表示它的原始值。

对象是复合值,且大多数对象无法真正表示为一个原始值,因此默认的valueof()方法简单地返回对象本身,而不是返回一个原始值。

数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的valueof()只是简单返回对象本身。日期类定义的valueof()方法会返回它的一个内部表示:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

JavaScript中对象到字符串的转换经过了如下这些步骤:

如果对象具有toString()方法,则调用这个方法。如果它返回一个原始值,JavaScript将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。

如果对象没有toString()方法,或者这个方法并不返回一个原始值,那么JavaScript会调用valueof()方法。如果存在这个方法,则JavaScript调用它。如果返回值是原始值,JavaScript将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。

否则,JavaScript无法从toString()或valueof()获得一个原始值,这时它将抛出一个类型错误异常。

对象到数字的转换过程中,JavaScript做了同样的事情,只是它会首先尝试使用valueof()方法:

如果对象具有valueof()方法,后者返回一个原始值,则JavaScript将这个原始值转换为数字(如果需要的话)并返回这个数字。

否则,如果对象对象具有toString()方法,后者返回一个原始值,则JavaScript将其转换并返回。

否则,JavaScript抛出一个类型错误异常。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

【变量声明】   

使用一个变量之前应当先声明。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

如果未在var声明语句中给变量指定初始值,那么虽然声明了这个变量,但在给它存入一个值之前,它的初始值是undefined。

在for和for/in循环中同样可使用var语句:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

JavaScript变量可以是任意数据类型,在JavaScript中首先将数字赋值给一个变量,随后再将字符串赋值给这个变量,这是合法的:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

重复的声明和遗漏的声明

使用var语句重复声明变量是合法且无害的。如果重复声明带有初始化器,那么这就和一条简单的赋值语句类似。

如果你试图读取一个没有声明的变量的值,JavaScript会报错。

在ECMAScript 5严格模式下,如果给一个未声明的变量赋值也会报错。

然而从历史上讲,在非严格模式下,如果给一个未声明的变量赋值,JavaScript实际会给全局对象创建一个同名属性,而且它工作起来像一个正确声明的全局变量。

这意味着你可以侥幸不声明全局变量。但这是一个不好的习惯且会造成很多bug,因此,你应始终使用var来声明变量。  

【变量作用域】

一个变量作用域(scope)是程序源代码中定义这个变量的区域。

全局变量具有全局作用域,在JavaScript代码中的任何地方都是有定义的。

然而在函数内声明的变量只在函数体内有定义。它们是局部变量,作用域是局部性的。函数参数也是局部变量,它们只在函数体内有定义。

在函数体内,局部变量的优先级高于同名的全局变量。如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所遮盖。

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

函数定义是可以嵌套的。由于每个函数都有自己的作用域,因此会出现几个局部作用域嵌套的情况:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

 1.函数作用域和声明提前

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

JavaScript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的。这意味着变量在声明之前甚至已经可用。

JavaScript这个特性被非正式地称为声明提前(hoisting) ,即JavaScript函数里声明的所有变量(但不涉及赋值)都被“提前”至函数体的顶部:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

在具有块级作用域的编程语言中,在狭小的作用域里让变量声明和使用变量的代码尽可能靠近彼此,通常讲,这是一个非常不错的编程习惯。

由于JavaScript没有块级作用域,因此一些程序员特意将变量声明放在函数体顶部,而不是将声明靠近放在使用变量之处。

这种做法使得他们的源代码非常清晰地反映了真实的变量作用域。  

2.作为属性的变量

当声明一个JavaScript全局变量时,实际上是定义了全局对象的一个属性。

当使用var声明一个变量时,创建的这个属性是不可配置的,即这个变量无法通过delete运算符删除。

如果你没有使用严格模式并给一个未声明的变量赋值的话,JavaScript会自动创建一个全局变量。

以这种方式创建的变量是全局对象的正常的可配值属性,并可以删除它们:

笔记《JavaScript 权威指南》(第6版) 分条知识点概要2—类型、值、变量-LMLPHP

JavaScript全局变量是全局对象的属性,这是在ECMAScript规范中强制规定的。对于局部变量则没有如此规定,但局部变量被当做跟函数调用相关的某个对象的属性。

ECMAScript 3规范称该对象为“调用对象”(call object),ECMAScript 5规范称为“声明上下文对象”(declarative environment record)。

JavaScript允许使用this关键字来引用全局对象,却没有方法可引用局部变量中存放的对象。这种存放局部变量的对象的特有性质,是一种对我们不可见的内部实现。

3.作用域链

JavaScript是基于词法作用域的语言:通过阅读包含变量定义在内的数行源码就能知道变量的作用域。

全局变量在程序中始终都是有定义的。局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的。

如果将一个局部变量看做是自定义实现的对象的属性的话,那么可换个角度解读变量作用域。

每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。

这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。

当JavaScript需要查找变量x的时候(这个过程称做“变量解析”),它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,

如果第一个对象中不存在名为x的属性,继续查找下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性x,那么就认为这段代码的作用域链上不存在x,

并最终抛出一个引用错误(ReferenceError)异常。

在JavaScript的最顶层代码中(即不包含任何函数定义内的代码),作用域链由一个全局对象组成。

在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。在一个嵌套的函数体内,作用域链上至少有3个对象。

当定义一个函数时,它实际上保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链上,同时创建

一个新的更长的表示函数调用作用域的“链”。对嵌套函数来讲,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。

内部函数在每次定义的时候都有微妙的差别——在每次调用外部函数时,内部函数的代码都是相同的,而且关联这段代码的作用域链也不相同。

第一遍边看边理解,有很多不太理解的地方,后面再回顾,先快速的地过完第一遍。还有很多其他的书要读,所有第一遍都快速过掉,能理解多少是多少,能记住多少是多少,同时还要花很多时间去实战,做案例练习……

路漫漫其修远兮,吾将上下而求索。

04-25 11:36