数据类型
分为两类:原始类型、对象类型。其中,原始类型包含数字、字符串、布尔值、null、undefined。对象是除了原始类型之外的所有。
对象是属性的集合,每个属性都由“键/值对”构成。
数字
- 不区分整数和浮点数,所有的数字都是浮点数
- 数字直接出现在程序中,我们称之为数字直接量;值得注意的是,数字直接量可以是十进制和十六进制,但不能是八进制
- 浮点数可以使用传统的实数写法,也可以使用指数记数法(也就是在实属后面添加e或者E,在跟一个整数,如6.02e23标识的就是6.02*10^23)
Math函数 | 函数解释 |
---|---|
Math.pow(2,53) | 2的53次方 |
Math.round(.6) | 四舍五入 |
Math.ceil(.6) | 向上取整 |
Math.floor(.6) | 向下取整 |
Math.abs() | 求取绝对值 |
Math.max() | 求最大值 |
Math.min() | 求取最小值 |
Math.random() | 生成一个在0~1之间的伪随机数 |
Math.PI | 圆周率 |
Math.E | 自然对数的底数 |
Math.sqrt(3) | 3的平方根 |
Math.pow(3,1/3) | 3的立方根 |
Math.sin() | 三角函数 |
Math.log(10) | 10的自然对数 |
Math.(100)/Math.LN10 | 以十为底100的对数 |
Math.exp(3) | e的三次幂 |
无穷大使用Infinity和-Infinity表示,比如在被零整除的时候会产生。但是,如果你调皮的运算 0/0 ,JavaScript会返回一个NaN,类似的,无穷大除以无穷大,对任意负数做开方运算,或者算术运算符与不是数字或者无法转换成为数字的操作数一起的时候也将返回NaN。
关于NaN
NaN属于JavaScript中的一股泥石流,一个颜色不一样的炮仗:它和任何值都不相等,和自己也不相等。
那么,如何判断一个值是不是NaN呢,从前面的内容来看,不能使用x==NaN来判断x是不是NaN。那我们可以反其道而行之,判断x!=x,如果返回的是true,那就表明x就是NaN。在这里,JavaScript也提供了isNaN()函数来进行判断,如果是非数字,都会返回true,相较而言,前一种方法还是比较好。
var str2 = NaN;
var fn1 = function(){};
var fn2 = fn1 + 1;
console.log(str2 != str2);
console.log(isNaN(str2));
console.log(fn2 != fn2);
console.log(isNaN(fn2));
/*分别输出true,true,false,true*/
关于正负零
可以说大部分情况下他们是一样的,除了……做除数的时候
var zero = 0;
var negz = -0;
console.log(zero === negz);
console.log(1/zero === 1/negz);
/*true,false*/
二进制浮点数与四舍五入错误
JavaScript采用的是二进制表示法,所以你想逼他精确的表示十进制数字,它就敢胡来,我们看看下面这个例子。
var x = 0.3-0.2;
var y = 0.4-0.3;
console.log(x == y);
/*调皮地返回了一个false。*/
文本
字符串有索引值,从0开始。空字符串的长度是0。字符串由引号包含起来,但是要注意的是双引号也单引号之前可以嵌套,但是不能错位。
转义字符
反斜杠(\) 加一个字符是一个转义字符的格式。
字符串的常见使用
字符串的连接,使用+;
字符串长度的获取:str.length;
表达式 | 解释 |
---|---|
var str = “hello , world” | 创建字符串str |
str.charAt(0) | 获取第一个字符 |
str.charAt(str.length-1) | 获取最后一个字符 |
str.substring(1,4) | 获取2~4个字符串 |
str.slice(1,4) | 同上 |
str.slice(-3) | 获取最后三个字符 |
str.indexOf(“l”) | 获取l第一次出现的位置 |
str.lastIndexOf(“l”) | 获取l最后一次出现的位置 |
str.indexOf(“l”,3) | 获取l在位置3之后首次出现的位置 |
str.split(“,”) | 通过“,”来分割字符串 |
str.replace(“h”,”H”) | 将h替换成H |
str.toUpperCase() | 全文大写 |
str.toLowerCase() | 全文小写 |
模式匹配
JavaScript定义了RegExp()构造函数,用来创建表示文本匹配模式的对象,正则表达式这里不做深入了解。
布尔值
布尔值表示真或假,有两个保留字:true、false;
能转换成false的值有:null、undefined、0、-0、NaN、“”(空字符串),而所有的其他值都能够转换成为true。
null与undefined
null表示空值,undefined表示未定义值。虽然他们都表示值的空缺,但是还有区别。null表示这里不应该存在值,undefined表示这里应该存在值,但是并没有定义值,这个时候就是undefined。
当给一个元素不赋值的时候是undefined,如果要给这个元素赋一个空值,要赋值null,哈哈哈,是不是很绕。
console.log(typeof(undefined));
console.log(typeof(null)); ///特别要注意null的类型,这是js最初留下来的一个问题,一直沿用至今
console.log(undefined == null);
console.log(undefined === null);
/*输出:undefined、object、true、false*/
疑问:
这里显得了null的typeof是object,那么为什么它又归类于原始类型?读友如有,见解可在评论处解惑。
包装对象
js对象是一组复合值,它是属性或者已命名值的集合,通过“.”来引用属性值,当属性质是一个函数的时候,称其为方法,通过“.m()”来调用对象的m方法
字符串虽然不是对象,但是也同样具有属性和方法。这是为什么呐?因为JavaScript会将字符串通过new String(s)的方法转换成对象。同样的,数字和布尔值也各有方法:Number()和Boolean()
既然可以转换成对象,那么我们试一下给对象创建一个属性并赋值
var str = 'abcdefghigk';
str.pro = 1;
console.log(str.pro);
/*undefined*/
为什么呐?
值得注意的是,这些方法的调用均来自于一个临时对象,访问他们的属性时会造成错误;如果企图给属性赋值,则会忽略这个操作,修改只是发生在临时对象身上,而临时对象并没有继续保留下来。
因此,上述代码的过程就是在第二行的时候创建一个临时对象赋值为4后销毁,在第三行有创建一个新的临时对象,这是为undefined
延伸
如果我就是想要对字符串、数字、布尔值这些存取属性怎么办?这涉及到了一个新知识——包装对象。
包装对象:存取字符串、数字布尔值的属性时创建的临时对象。String()、Number()、Boolean()用来显示创建包装对象,这样我们就可以将临时对象存放起来了。
var s = "abcdefghigk";
var S = new String(s);
S.len = 1;
console.log(S.len);
/*1*/
不可变的原始值与可变的对象引用
var a = 'hello';
var b=a;
a = a.toUpperCase();
console.log(a); //HELLO
console.log(b); //hello
解释如图
var obj1 = {x:1,y:2};
var obj2 = obj1;
obj1.x = obj1.x + obj1.y;
console.log(obj1); //{x:3,y:2}
console.log(obj2); //{x:3,y:2}
console.log(obj1 === obj2); //true,因为这里两个对象使用的是一个基对象
解释如图
关于比较:对象的比较并非值的比较:即使两个对象包含相同的属性及相同的值,他们也是不相等的,各个索引元素完全相等的两个数组也不相等。
var o = {x:1}, p = {x:1};
console.log(o===p); //false;
var a = [],b=[];
console.log(a === b); //false;
类型转换
显式转换
Boolean()、Number()、String()、Object()都是显示转换方法,当不通过new来调用这些函数时,他们的转换如上图所示,
需要注意:
除了null、undefined之外的任何值都具有toString()方法,这个方法实行结果与String()方法一致;
如果将null或者undefined转换成对象,就会抛出错误,但是object()函数就不会,:它只会简单的返回一个新创建的空对象;
在以数字表示的字符串中,当转换成数字的时候,允许在开头或结尾添加空格。但若为其他任意非空格字符都将不被当成为数字直接量的一部分,从而转换成NaN
var obj = new Object(undefined);
console.log(obj); //Object {},并没有报错
var str = ' 123456';
console.log(Number(str)); //123456
str = '123456 ';
console.log(Number(str)); //123456
str = '123 456';
console.log(Number(str)); //NaN
延伸
区别toString()与String()
惯用转换法
- x + “”//等价于String(x);
- +x //等价于Number(x),也可以写成x-0
- !!x //等价于Boolean(x),注意是双叹号
Number类转换方法
- toString(基数)
- toFixed()根据小数点后的指定位数将数字转换成字符串;toFixed(3),表示小数点后三位保留并转换为字符串
- toExponential()实用指数记数法将数字转换为字符串,其中小数点前只有一位,小数点后的位数由参数决定
- toPrecision()将指定的有效数字位数转换成字符串;如toPrecision(10)转换成十位有效数字
- Number()只能转换十进制,并且必须合法;parseInt()和parseFloat()属于从前向后解析,不考虑后面字符串时候合法,会跳过前导空格,尽可能多得解析字符。
- parseInt()可以接受第二个参数:
parseInt(‘077’,8); //转换成八进制 - String()和valueOf()可以做到对象到字符串和对象到数字的转换
对象转换为原始值
对象到字符串的转换
toString()方法
表达式 | 结果 |
---|---|
({x:1,y:1}).toString() | [object object] |
[1,2,3].toString() | “1,2,3” |
(function(x){f(x);}).toString() | “(function(x){f(x);})” |
/\d+/g.toString() | “/\d+/g” |
new Date(2017,2,1).toString() | Wed Mar 01 2017 00:00:00 GMT+0800 (中国标准时间) |
valueOf():如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正的表示一个原始值,所以它只是简单的返回对象本身,而不是返回一个原始值
对象到字符串的转换过程:对于字符串,优先调用toString()方法,如果返回一个原始值,JavaScript就将这个值转换成字符串,并返回结果,如果没有这个方法,转而调用valueOf()方法,如果存在这个方法,就调用,如果返回值是原始值,JavaScript就将这个值转换成为字符串,然后返回。否则,抛出错误。
对象到数字的转换过程:对于数字,优先调用valueOf()方法,如果有,就返回原始值,将这个字符串转换成为数字,然后返回这个数字,如果没有就调用toString(),返回一个原始值,JavaScript将其转换成数字并返回,否则,会抛出错误。
变量声明
使用关键字var来声明。
可以同时声明多个变量,并且可以将变量的初始赋值与声明合写。
前面已经讲过,没有初始赋值的就是undefined。
值得一提的是,JavaScript每日有指定变量的数据类型,它的变量可以是任意类型。
关于重复声明与遗漏声明
var a = 1;
var a = 2;
console.log(a);
///重复声明在JavaScript中是合法的,如果重复声明并初始化,这就相当于一条简单的赋值语句。
console.log(b);
///遗漏申明浏览器报错,b is not defined。
变量作用域
全局变量拥有全局作用域,在任何地方都是由定义的,然而,函数内部声明的变量只有在函数体内才会有定义。显而易见,函数也是局部变量。
在函数体内,局部变量的优先级高于同名的全局变量。也就是说,当局部变量和全局变量重名的时候,局部变量会覆盖全局变量的值。
var scope = "global";
function fn1(){
var scope = "local";
return scope;
}
console.log(fn1());
/*返回local*/
我们这都知道,如果一个变量在声明的时候没有写关键字var,那么这个变量就会成为全局变量
scope = "global";
function fn1(){
scope = "local";
myscope = "local";
return [scope,myscope];
}
console.log(fn1());
console.log(scope);
console.log(myscope);
/*返回分别是[local,local]、local、local。可以看出,本来作为局部变量的变量全部变成了全局变量,打乱了整个命名空间*/
函数作用域与声明提前
JavaScript的函数作用域是指函数内部声明的所有变量在函数体内是始终可见的,这也就意味着变量甚至可以在声明之前使用,这种特性被称为“声明提前”,表示所有声明的变量(不包括赋值)都被提前到函数体的顶部。
scope = "global";
function fn1(){
console.log(scope); //undefined
var scope = "local";
console.log(scope); //local
}
fn1();