JavaScript语言核心—类型、值和变量

计算机程序的运行需要对值(value)(比如数字3.14或者文本“hello shinygang”)进行操作。在编程语言中,能够表示并操作的值的类型被称作数据类型(type),编程语言最基本的特性就是能够支持各种数据类型。当程序需要把值保存起来以备将来使用是,便将其赋值给一个变量(variable)。变量是一个值的符号名称,可以通过名称来获取对值的引用。变量的工作机制是编程语言的另一个基本特性。

JavaScript的数据类型分为两类:原始数据类型(又称基本数据类型)(primitive type)和对象类型(object type)。

  • JS中,原始数据类型(基本数据类型)包括数字、字符串、布尔值、null(空)和undefined(未定义)。后面两者通常分别代表了各自特殊类型的唯一的成员。
  • JS中,除了基本数据类型之外的就是对象了。对象(object)是属性(property)的集合,每个属性都是“名/值对”(值可以是原始值,比如数字、字符串、布尔值,也可以是对象)构成。
  • JS的类型可以分为原始类型和对象类型,也可以分为可以拥有方法的类型和不可拥有方法的类型,同样可分为可变(mutable)类型和不可变(immutable)类型。可变类型的值是可修改的。对象和数组属于可变类型:JS程序可以更改对象属性值和数组的元素值。数字、字符串、布尔值、null和undefined属于不可变类型---比如,修改一个数值的内容本身是说不通的。
  • JS可以自由地进行数据类型的转换。比如在程序期望使用字符串的地方使用了数字,JS会自动将数字转换为字符串。JS中灵活的类型转换规则对"判断相等"的定义亦有影响。
  • JS变量是无类型的(untyped),变量可以被赋予任何类型的值,同样一个变量可以重新赋予不同类型的值。

1、数字

和其他编程语言不同,JS不区分整数值和浮点数值。JS中的所有数字都采用浮点数值表示。按照JS中的数字格式,能够表示的整数范围是:-253~253,包含边界值。

1.1、整型直接量

  • 在JS程序中,使用一个数字序列表示一个十进制整数。例如:
0   3     1000000
  • 除了十进制的整型直接量,JS同样能识别十六进制(以16为基数)值。所谓十六进制的直接量是指以“0x”或“0X”为前缀,其后跟随十六进制数串的直接量。十六进制值是09之间的数字和a(A)-f(F)之间的字母构成,af的字母对应的表示数字10~15。例如下面的例子:
0xff  >>> 15*16+15=255(十进制)
  • 尽管ES标准不支持八进制直接量,但JS的某些实现可以允许才有八进制(基数为8)形式表示整数。八进制直接量以数字0开始。其后跟随一个有0~7之间的数字组成的序列。例如:
0377  >>> 3*8*8+7*8+7=255

由于八进制的支持度限制,最好不要使用以0为前缀的整型直接量。ES6的严格模式下,八进制直接量是明令禁止的

1.2、浮点型直接量

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

3.14
2345.789
.33333333333333333333333333333
6.02e23             // 6.02*10^23
1.47223423E-32      // 1.47223423*10^-32

1.3、JS中的算术运算

JS程序是使用语言本身提供的算术运算符来进行数字运算的。这些运算符包括加法运算符(+)、减法运算符(-)、乘法运算符(*)、除法运算符(/)和求余运算符(%)。除了最基本的运算符外,JS还支持更加复杂的运算符,这些复杂的运算符通过作为Math对象的属性定义的函数和常量来实现。

Math.pow(2,53)              // 2的53次幂
Math.round(.6)              // 1.0,四舍五入
Math.ceil(.6)               // 1.0,向上求整
Math.floor(1.6)             // 1.0,向下取整
Math.abs(-5.2)              // 5.2,求绝对值
Math.max(0,4,5)             // 5,返回最大值
Math.min(1,2,3)             // 1,返回最小值
Math.random()               // 取0~1之间的随机数
Math.PI                     // π,圆周率
Math.E                      // e,自然对数的底数
Math.sqrt(3)                // 3的平方根
Math.pow(9, 1/3)            // 9的立方根
Math.sin(0)                 // 三角函数
Math.log(10)                // 10的自然对数
Math.exp(3)                 // e的三次幂
  • 在JS中以Infinity表示无穷大,同样地,以-Infiinity表示负无穷大。
  • 被零整除在JS并不报错:它返回无穷大(Infinity)或负无穷大(-Infinity)。但有一个例外,零除以零没有意义的,这样的运算结果返回非数字(NaN)。其中,Infinity和NaN在ES5开始都是只读的。
  • JS中的非数字值有点特殊:它不等于任何值包括自己。也就是说,没办法通过x==NaN来判断变量x是否是NaN,相反,应该使用x!=x来判断。当且仅当x为NaN的时候,表达式的结果才为true。函数isNaN()的作用与此类似,如果参数是NaN或者是一个非数字值,则返回true。JS中有一个类似的函数isFinite(),在参数不是NaN、Infinity或-Infinity的时候返回true
10/0=Infinity
0/0=NaN
0/0!=0/0            >>> true
0/0==0/0            >>> false
isNaN(0/0)          >>> true
isNaN('aaa')        >>> true
isNaN(11)           >>> false
0==-0               >>> true
Infinity==-Infinity >>> false

1.4、二进制浮点数和四舍五入错误

S中的数字具有足够的精度,并可以几期近似于0.1。但事实是,数字不能精确表述的确带来了一些问题。

var x = 0.3 - 0.2;
var y = 0.2 - 0.1;
x == y                  >> false
x == 0.1                >> false
y == 0.1                >> true

在JS的真是运行环境中,0.3-0.2=0.09999999999999998

由于舍入的误差,0.3和0.2之间的近似差值实际上并不等于0.2和0.1之间的近似差值。这个问题不止在JS中才会出现,理解这一点很重要:在任何使用二进制浮动数的编程语言中都会有这个问题,同样需要注意的是,上述代码中x和y的值非常接近彼此和最终的正确值。这种计算结果可以胜任大多数的计算任务:这个问题也只有在比较两个值是否相等的时候才会出现。

1.5、日期和时间

JS语言核心包括Date()构造函数,用来创建表示日期和时间的对象。这些日期对象的方法为日期计算提供了简单的API。日期对象不像数字那样是基本数据类型。

let d1 = new Date(2011,11,11) >> 2011-11-11
let d2 = new Date(2011,11,11,11,11,11)      >> 2011-11-11 11:11:11
let now = new Date()    >> 当前日期和时间
let diffs = now-d1;     >> 日期减法:计算时间间隔的毫秒数

2、文本

字符串(String)是一组由16位值组成的不可变的有序序列,每个字符通常来自于Unicode字符集。JS通过字符串类型来表示文本。字符串的长度(length)是其所含16位值的个数。

2.1、字符串直接量

在JS程序中的字符串直接量是由单引号或者双引号括起来的字符序列。单引号和双引号没啥区别。

'' // 空字符串:它包含0个字符
'shingang'
"hello shinygang"

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

"shinygang\nnewlines"  这里定义了一个显示为两行的字符串
"one\
two"   // 这里用两行定义单行字符串,只在ES5中可用

2.2、转义字符

在JS字符串中,反斜杠\有着特殊的用途,反斜杠后加一个字符,就不再表示她们的字面含义了

\0              >> NUL字符(\u0000)
\b              >> 退格符(\u0008)
\t              >> 水平制表符(\u0009)
\n              >> 换行符(\u000A)
\v              >> 垂直制表符(\u000B)
\f              >> 换页符(\u000C)
\r              >> 回车符(\u000D)
\"              >> 双引号(\u0022)
\'              >> 单引号或撇号(\u0027)
\\              >> 反斜线(\u005C)
\xXX            >> 由两位十六进制数XX指定的Latin-1字符
\uXXXX          >> 由4位十六进制数XXXX指定的Unicode字符

目前,除了上面列出的的规则,其他字符前面出现反斜杠,则忽略转义规则。(当然,JS语言将来的版本可能定义新的转义符)

2.3、字符串的使用

JS内置功能之一就是字符串链接。

name = 'shiny' + 'gang'     // 生成字符串'shinygang'

JS中字符串是固定不变的,类似replace()方法都返回新的字符串,原字符串没有发生改。下面列举字符串常用方法:

let s = "shinygang"
s.charAt(0)             >> 'c':第一个字符
s.substring(1,4)        >> 'hin':第2~4个字符
s.substr(1,4)           >> 'hiny':从第2个字符开始,获取4个字符长度。
s.slice(1,4)            >> 'hin':第2~4个字符
s.slice(-3)             >> 最后3个字符
s.indexOf('h')          >> 1:首次出现h的下标
s.lastIndexOf('h')      >> 1:h最后出现的位置
s.indexOf('n',5)        >> 7:在位置5之后首次出现n的下标
s[0]                    >> s:下标为0的字符串

2.4、模式匹配-正则表达式

JS定义了RegExp()构造函数,用来创建表示文本匹配模式的对象。 ExgExp并不是JS的基本类型。和Date一样,它只是一种具有实用API的特殊对象。尽管RegExp并不是语言中的基本数据类型,但是它们依然具有直接量写法,可以直接在JS程序中使用。在两条斜线之间的文本构成了一个正则表达式直接量。第二条斜线之后也可以跟随一个或多个字母,用来修饰匹配模式的含义。

/^HTML/         >> 匹配以HTML开始的字符串
/[1-9][0-9]*/   >>匹配非0数字

RegExp定义了很多有用的方法,字符串同样具有可以接收RegRxp参数的方法。例如:

var s = "shinygang: 1,2,3"
var pattern = /\d+/g    // 正则表达式
pattern.test(s)         // true,匹配成功
s.search(pattern)       // 10,首次匹配成功的位置
s.match(pattern)        // ['1','2','3']:所有匹配组成的数组
s.replace(pattern, '#') // 'shinygang, #,#,#'
s.split(/\D+/)  // ['','1','2','3']:用非数字字符截取字符串

3、布尔值

布尔值指代真或假、开或关、是或否。这个类型只有两个值,保留字true和false。 任意JS的值都可以转换为布尔值。下面这些值会被转换传给你false

undefined
null 
0
-0
NaN
""      // 空字符串

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

4、null和undefined

  • null是JS语言的关键字,它表示一个特殊值,常用来描述"空值"。对null执行typeof,返回字符串'object',也就是说,可以将null认为是一个特殊的对象值。
  • JS还有第二个值来表示空缺。用未定义的值表示更深层次的“空值”。它是变量的一种取值,标明变量没初始化,如果要查询对象属性或数组元素的值时返回undefined则说明这个属性或元素不存在。如果函数没有任何返回值,则返回undefined。
typeof null         >> 'object'
typeof undefined    >>  'undefined'
null == undefined   >> true
null === undefined  >> false

5、全局对象

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

  • 全局属性,比如undefined、Infinity和NaN
  • 全局函数,比如isNaN()、parseInt()和eval()
  • 构造函数,比如Date()、RegExp()、String()、Object()和Array()
  • 全局对象,比如Math和JSON
    在代码的最顶级--不在任何函数内的JS代码--可以使用JS关键字this来引用全局对象:
var global = this;     // 定义一个应用全局对象的全局变量  

6、包装对象

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

只要引用了字符串的属性,JS就会将字符串通过调用new String()的方式转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性应用结束,这个新创建的对象就会销毁。 同字符串一样,数字和布尔值也具有各自的方法:通过Number()和Boolean()构造函数创建一个临时对象,这些方法的调用均是来自这个临时对象。

var s = 'test';     // 创建一个字符串
s.len = 4;          // 给它设置一个属性
var t = s.len;      // 查询这个属性

当运行这段代码时,t的值是undefined。因为第二行代码创建了一个临时字符串对象,并给其len属性赋值为4,随即销毁这个对象。第三行通过原始的字符串值创建一个新的字符串对象,尝试读取其len属性,自然不存在。
存取字符串、数字或布尔值的属性时创建的临时对象称作保证对象,它只是偶尔用来区分字符串值和字符串对象、数字和数值对象以及布尔值和布尔对象。 由于字符串、数字和布尔值的属性都是只读的,并且不能给它们定义新属性,因此你需要明白它们是有别于对象的。
==等于运算符将原始值和其包装对象视为相等,但===全等运算符将它们视为不等。通过typeof运算符可以看到原始值和其包装对象的不同

var s = 'shinygang';
var y = new String(s);
s == y;             >> true
s === y;            >>  false
typeof s;           >> string
typeof y;           >> object

7、不可变的原始值和可变的对象引用

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

  • 原始值不可更改的:任何方法都无法更改一个原始值。
  • 原始值的比较是值的比较:只有在它们的值相等时它们才相等。
  • 对象和原始值不同,首先,它们是可变的--它们的值是可修改的。
  • 对象的比较并非值的比较:即使两个对象包含同样的属性及相同的值,它们也是不相等的。各个索引元素完全相等的两个数组也不相等。
var o = {x: 1}, p = {x: 1};
0 === p;            >> false
var a = [], b = [];
a === b;            >> false

我们通常将对象成为引用类型,以此来和基本类型区分开来。对象的比较均是引用的比较:当且仅当它们引用同一个基对象时,它们才相等。

var a = [];
var b = a;
b[0] = 1;
a[0];           >> 1:变量a也会修改
a === b;        >> true: a和b引用同一个数组,因此它们是相等
posted @ 2017-11-01 11:08  唐岗  阅读(300)  评论(0)    收藏  举报