JS数据类型分为两类:基本数据类型和对象类型
基本数据类型包括数字、字符串、布尔类型、null和undefined
对象(object)是属性的集合,每个属性都由键值对构成;普通JS对象是”命名值“的无序集合,特殊对象数组(array)表示带编号的有序集合。
2.1数字
JS不区分整数值和浮点数值,所有数字均用浮点数值表示。运算符包括+、- 、*、/和%
更加复杂的运算通过Math对象的属性定义的函数和变量来实现:
Math.pow(2,53) //2的53次幂 Math.round(.6) //=1.0 四舍五入 Math.ceil(.6) //=1.0 向上求整 Math.floor(.6) //=0.0 向下求整数 Math.abs(-5) //=5 绝对值 Math.max(x,y,z) //最大值 Math.min(x,y,z) //最小值 Math.random() //生成一个大于等于0小于1.0的随机数 Math.PI // 圆周率∏ Math.E // 自然对数的底e Math.sqrt(3) //3的平方根 Math.sin(0) // 三角函数 还有Math.cos和Math.atan Math.log(10) //10的自然对数 Math.log(100)/Math.LN10 //以10为底100的对数 Math.exp(3) //e的三次幂
正无穷大 Infinity Number.POSITIVE_INFINITTY +1 Number.MAX_VALUE+1 1/0
负无穷大 -Infinity -1/0 -Number.MAX_VALUE-1
NaN 0/0
Number.MIN_VAULE/2 发生下溢,结果为0
-Number.MIN_VAULE/2 -1/Infinity 负零
非数字值NaN它和任何值都不相等包括自身,无法通过x==NaN来判断x是否是null,相反,应当使用x!=x来判断,当且仅当x为NaN时候,x!=x才为true,或使用函数isNaN()进行判断,如果参数是NaN或非数字值(比如字符串和对象),则返回true
2.1.1 时间和日期
使用Date()函数,来创建表示日期和时间的对象。
var then = new Date(2019,11,20); // 2019年11月20日 var later = new Date(2019,11,20,20,15,30); // 2019年11月20日20时15分39秒 var now = new Date(); // 当前日期和时间 var elapsed = now - then; // 计算时间间隔的毫秒数 later.getFullYear() //2019 later.getMonth() //11 later.getDate() //20 later.getHours() //20 later.getHours() //12 使用UTC表示的时间,基于时区
2.2文本
字符串(string)是由一组16位值组成的不可变的有序序列,每个字符通常来自Unicode字符集,
字符串的长度是其所含16位数的个数
var e = 'e' e.length // 1
2.2.1字符串直接量
字符串直接量是由单引号或双引号括起来的字符序列
ES5中字符串可以拆成数行,当每行必须以反斜杠(\
)结束,反斜杠和行结束符(\n
)都不算字符串直接量的内容
注意使用单引号界定字符串时,撇号是同一个字符,必须使用反斜杠进行转义 ,例如:can't—>'can\'t'
加号(+) 可以用于字符串拼接
2.2.3字符串的使用
var s = 'Hello,world' s.charAt(0) //H s.charAt(s.length-1) // d s.slice(1,4) //第2-4个字符'ell' s.substring(1,4) //第2-4个字符 s.slice(-3) // 最后3个字符 s.indexOf('l') //第一次出现'l'的位置 index of s.lastIndexOf('l') //最后一次出现'l'的位置 s.indexOf('l',3) //'l'在位置3及以后出现的位置 s.split(',') // ["Hello", "world"] s.replace('l','L') // "HeLlo,world" 替换第一个, 返回新值 s.replace('/l/','L') // 'Hello,world' s.toUpperCase() // "HELLO,WORLD" 返回新值
2.2.4匹配模式
/^HTML/ //匹配以HTML开始的字符串 /[1-9][0-9]*/ //匹配一个非零数字,后面是任意个数字 /\bjavascript\b/i 匹配单词'javascript',忽略大小写 var text = 'testing:1,2,3' var pattern = /\d+/ //非全局匹配 pattern.test(text) //true text.search(pattern) // 8 首次匹配成功的位置 text.match(pattern) // ["1", index: 8, input: "testing:1,2,3", groups: undefined] var pattern = /\d+/g //全局匹配 text.match(pattern) // ["1", "2", "3"] text.replace(pattern,'#') // "testing:#,#,#" text.split(/\D+/) // ["", "1", "2", "3"] 用非数字字符截取字符串
2.3布尔值
下面这些值会被转换成false
undefined null 0 -0 NaN ''
布尔值包含toString()方法,使用这个方法将字符串转换为’true‘或'false'
false.toString() "false"
布尔运算符 与(&&) 或(||) 非(!)
if ((x == 0 && y == 0))|| !(z == 0){ }
2.4null和undefined
nulls是js的关键字,表示一个特殊值,常用来描述’空值‘,通常认为null
是它自有类型的唯一成员,它可以表示数字、字符串和对象是"无值"的
undefined第二个值来表示值的空缺,用未定义的值表示更深层次的’空值‘。
它是变量的一种取值,表明变量没有初始化,如果要查询对象属性或数组元素的值时返回undefinde则说明这个属性或元素不存在;如果函数没有返回任何值,则返回undefined。引用没有提供实参的函数,形参的值也只会得到undefined,undefined是预定义的全局变量(它和null不一样,它不是关键字),它的值就是’未定义‘。
尽管null和undefined是不同的,但它们都表示”值的空缺“,两者往往可以转换。运算符”==''认为两者是相等的(要使用严格相等运算符’===‘来区别),在希望值是布尔类型的地方它们的值都是假值,null和undefined都不包含任何属性和方法。
2.5全局对象
全局对象的属性是全局定义的符号,js出现可以直接使用。当js解释器启动时,它将创建一个新的全局对象,并给它一组定义的初始属性:
全局属性:例:undefined、Infinity和NaN
全局函数:比如isNaN()、parseInt()
构造函数:比如:Date()、RegExp()、String()、Object()和Array()
全局对象,比如Math和JSON
全局对象的初始值并不是保留字,但它们应当当作保留字来对待
2.6包装对象
JS对象是一种复合值:它是属性或已命名值的集合。通过'.'来引用属性值,当属性值是一个函数的时候,称其为方法。通过o.m()调用对象中的方法。
var s = 'hell world'; var word = s.slice(s.indexOf(" ")+1,s.length)
字符串既然不是对象,为什么它会有属性呢?只要引用了字符串的s的属性,JS就会将字符串值通过调用new String(s)的方式转换成对象,这个对象继承了字符串的方法,并用来处理属性的引用。一旦引用结束,这个新建的对象就会销毁。
同字符串一样,数字和布尔值也具有各自的方法:通过Number()和Boolean()构造函数创建一个临时对象,这些方法的调用均是来自于这个临时对象。null和undefined没有包装对象:访问它们的属性会造成一个类型错误。
var s = 'test'; // 创建一个字符串 s.len = 4; //给它设置一个属性 var t = s.len; // 查询这个属性
第二行创建一个临时字符串对象,并给其len属性赋值为4,随即销毁这个对象。第三行通过原始的(没有修改过)字符串值创建一个新的字符串对象,尝试读取其len属性,这个属性自然不存在,结果为undefined;这段代码说明了在读取字符串、数字和布尔值的属性值(或方法)的时候,表现的像对象一样。但如果试图给其赋值,则会忽略这个操作:修改只是发生在临时对象身上,而这个临时对象并未保留下来。
存取字符串、数字或布尔值的属性时创建的临时对象称作包装对象,它只是偶尔用来区分字符串值和字符串对象、数字和数值对象以及布尔值和布尔对象。
var s = 'test'; // 一个字符串 var S = new String(s); // 一个字符串对象 typeof(s) "string" typeof(S) "object" s == S true s === S false
2.7不可变的原始值和可变的对象引用
JS的原始值(undefined、null、布尔值、数字和字符串)与对象(包括数组和函数)有着根本区别。原始值是不可更改的:任何地方都无法改变一个原始值。
原始值的比较是值的比较:只有在它们的值相等时它们才相等。
对象的比较并非值的比较:即使两个对象包含同样的属性以及相同的值,它们也不是相等的。
通常将对象称为引用类型,来和js的基本类型区分开来,对象值都是引用,对象的比较均是引用的比较:当且仅当它们引用同一个基对象时,它们才相等。
2.8类型转换
10 + ' objects' // '10 objects' '3' * '2' // 6 var n = 1 - 'x'; //NaN n + ' objects' // 'NaN objects'
2.8.1转换和相等性
JS可以做灵活的类型转换,因此’==‘运算符也随相等的含义灵活多变。例如下面这些比较结果均是true:
null == undefined '0' == 0 // 在比较前字符串转换成数字 0 == false // 比较前布尔值转换成数字 '0' == false // 比较前字符串和布尔值都转换成数字
2.8.2显示类型转换
显示类型转换最简单的方法就是使用Boolean()、Number()、String()或Object()函数
Number('3') String(false) // "false" Boolean([]) // true Obejct(3) // new Number(3)
2.8.3 对象转换为原始类型
对象到布尔值的转换非常简单:所有的对象(包括数组和函数)都转换为true。对于包装对象亦是如此:new Boolean(false)是一个对象而不是原始值,它将转换为true
对象到字符串和对象到数字的转换是通过调用待转换对象的一个方法完成的。
2.9变量声明
JS使用一个变量前应当先声明,可以一个var关键字声明多个变量,var s,n;
重复的声明变量是合法且无害的,读取一个没有声明的变量的值,JS会报错
2.10变量作用域
全局变量拥有全局作用域,在js代码中的任何地方都是有定义的。而函数内声明的变量只在函数体内有定义。他们是局部变量,作用域是局部性的,函数的参数也是。
在全局作用域编写代码时可以不写var,但声明局部变量时必须使用var语句,否则当函数体内使用同名变量将会对全局变量进行修改
var s = 1; function f(){ s = 2 }; f(); console.log(s) //2
2.10.1函数作用域和声明提前(变量提升)
类似C语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码之外是不可见的,称之为块级作用域。JS没有块级作用域,取而代之的是函数作用域:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内部都是有定义的。甚至变量在声明之前已经可用。变量提升(声明提前),即JS函数里声明的所有变量(但不涉及赋值)都被提前至函数体的顶部。
var scope = 'global'; function f(){ cosole.log(scope); //输出‘undefined’,而不是’global' var scope = 'local'; //变量在这里赋初始值,但变量本身在函数体内任何地方均是有意义的 console.log(scope); // 输出:‘local’ }
函数作用域的特性,局部变量在整个函数体始终是有定义的。上述过程等价于:将函数内的变量声明“提前”至函数体顶部,同时变量初始化留在原来的位置:
var scope = 'global'; function f(){ var scope; cosole.log(scope); scope = 'local'; console.log(scope); }
2.10.2作为属性的变量
当声明一个JS全局 变量时,实际上是定义了一个全局对象的一个属性,当使用var声明一个变量时,创建这个属性是不可配置的,也就是说这个变量无法通过delete运算符删除,如果没有使用严格模式对一个未声明的变量进行赋值的话,JS会自动创建一个全局变量。以这种方式创建的变量是全局对象的正常的可配置属性,并可以删除它们:
var truevar = 1; fakevar = 2; // 创建全局对象的一个可删除的属性 this.fakevar = 3; // 同上 delete truevar //false:变量没有被删除 delete fakevar // true:变量被删除 delete this.fakevar2 // 同上
JS全局变量是全局对象的属性,this关键字引用全局对象,却没有方法可以引用局部变量中存放的对象。
2.10.3作用域链
当JavaScript需要查找变量x的值的时候(这个过程称做 “变量解析 (variable resolution)), 它会从链中的第一个对象开始查找, 如果这个对象有一个名为x的属性, 则会直接使用这个属性的值, 如果第一个对象中不存在名为x的属性, JavaScript会继续查找链上的下一个对象。 如果第二个对象依然没有名为x的属性, 则会继续查找下一个对象, 以此类推。 如果作用域链上没有任何一个对象含有属性X, 那么就认为这段代码的作用域链上不存在X,并最终抛出一个引用错误(ReferenceError)异常。
在JavaScript的最顶层代码中(也就是不包含在任何函数定义内的代码), 作用域链由 一
个全局对象组成。 在不包含嵌套的函数体内, 作用域链上有两个对象, 第一个是定义函数参数和局部变址的对象, 第二个是全局对象。 在一个嵌套的函数体内, 作用域链上至少有三个对象。 理解对象链的创建规则是非常重要的。 当定义一个函数时, 它实际上保存一个作用域链。 当调用这个函数时, 它创建一个新的对象来存储它的局部变量, 并将这个对象添加至保存的那个作用域链上, 同时创建一个新的更长的表示函数调用作用域的 "链" 。对千嵌套函数来讲, 事情变得更加有趣, 每次调用外部函数时, 内部函数又会重新定义一遍。 因为每次调用外部函数的时候, 作用域链都是不同的。 内部函数在每次定义的时候都有微妙的差别一在每次调外部函数时, 内部函数的代码都是相同的, 而且关联这段代码的作用域链也不相同。