数据类型
JavaScript 数据类型分为:原始类型 和 对象类型。
原始类型 | 对象类型 |
---|---|
数字 (6.02e23 ; Infinity):均用浮点数值表示 | 普通对象 |
字符串 (‘ name = ” myform ” ’) | 数组 |
布尔值 | 函数 |
null (空值) | |
undefined(未定义的值,表示更深层次的空值) |
操作数极限:小数点前后17位
1.数字[Date | Math]
非数字值(not-a-number): NaN 和任何值都不相等,包括自身
因此无法通过 x == NaN 来判断变量 x 是否是 NaN
应当使用 x !== x 来判断,当且仅当 x 为 NaN 时返回 true
isNaN() : 判断是否是非数字,参数是NaN,字符串或者对象时返回 true
(会隐式类型转换)
var a = isNaN('10'); // false : isNaN(Number('10'));发生隐示类型转换
isFinite() : 检测无穷数,参数不是NaN,Infinity,-Infinity的时候返回 true
# | isNaN() | isFinity() |
---|---|---|
number | false | true |
Infinity | false | false |
string | true | false |
‘’ | false | true |
boolean | false | true |
null | false | true |
undefined | true | false |
{} | true | false |
[1] | false | true |
[‘a’] | true | false |
function | true | false |
- 数字,布尔值,null,“ ”,[纯数字数组] 都是数字值。
- 数字,布尔值,null,“ ”,[纯数字数组] 都是有穷数值。
2.字符串 [String | RegExp]
可读性上:可以使用 ’ \ ’ 来让字符串折行,不算做字符串直接量的内容
显示上:可以使用 ’ \n ’ 来让字符串折行显示
当字符串有单引号包裹时,里面想显示单撇号需要转义” \ ’ “
console.log('O\'Reilly\'s');
---- O'Reilly's
2.1 字符串拼接
’ + ’ 用于字符串拼接
msg = "Hello," + "world";
---- "Hello, world"
可以使用索引来查看字符串中的单个字符
var a = 'hello' ; console.log(a[0]);
---- "h"
2.2 模式匹配-正则表达式
3.布尔值
- 任意JavaScript值都可以转化为布尔值
- 会转化成 ‘false’ 的值是:undefined、null、0、NaN、”空”
4.undefined
- 查询对象属性或数组元素的值时返回undefined 说明这个属性或元素不存在。
- 若函数没有返回值,则返回undefined
- 引用没有提供实参的函数形参的值,返回undefined
- undefined是预定义的全局变量
- undefined是系统级的,出乎意料的或类似错误的值的空缺
- null是程序级的,正常的或在意料之中的空缺
全局对象(待深入)
全局对象的属性是全局定义的符号,程序可以直接使用。
当JS解释器启动时(或者浏览器加载新页面时),它将创建一个全局对象,并定义一组初始属性:
- 全局属性
- 全局函数
- 构造函数
- 全局对象
包装对象
var obj = new String(a)
var obj = new Number(b)
var obj = new Boolean(c)
对象可以通过 ‘.’ ‘[]’ 来调用属性,字符串不是对象,为什么可以调用属性呢?
- 引用了字符串的属性后,JavaScript就会将字符串值通过调用 new String(s)的方式转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性引用结束,这个新创建的对象就会销毁。
- 数字和布尔值也有对应的方法:Number() , Boolean() 构造函数创建一个临时对象。
- null和undefined没有包装对象,访问它们的属性会造成一个类型错误。
var s = 'test'; //创建一个字符串
s.len = 4; //设置属性
var t = s.len; //查询这个属性
new String(str).len = 4
vat t = new String(str).len
- 执行代码,输出t值是undefined。第二行代码创建一个临时字符串对象,并给其len属性赋值为4,随即销毁这个对象。第三行通过原始字符串值创建一个新字符串包装对象,尝试读取其len属性,这个属性自然不存在。
- 字符串、数字、布尔值的属性都是只读的,不能给他们定义新属性。(给其属性赋值会被忽略这个操作)
- 可以通过构造方法显示创建包装对象。
var s = 'test', n = 1 , b = true;
var S = new String(s);
var N = new Number(n);
var B = new Boolean(b);
- JavaScript会在必要时将包装对象转换成原始值。
- 因此 S B N 常常(但不总是)表现的和值 s b n一样。
- S == s –> true
- S === s –> false
原始值和引用值
原始值
- 原始值不可更改,字符串中的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值,原始字符串的值并未改变。
- 原始值的比较是值的比较。
引用值
常将对象称为引用类型。对象值都是引用,对象比较均是引用的比较。
对象的值可以修改。
- 对象的比较:及时两个对象包含同样的属性及相同的值,它们也不是相等的。各个索引元素完全相同的数组也不相等。当且仅当它们引用同一个基对象时,它们才相等。
var o = {x:1}, p = {x:1};
// o === p -> false:两个单独的对象永不相等
var a = [], b=[];
// a === b -> false:两个单独的数组永不相等
引用同一个地址,两个变量栈数据相同,指向同一个堆数据。
var a = [];// 定义一个引用空数组的变量a
var b = a; // 变量b引用同一个数组
b[0] = 1; // 通过变量b修改引用的数组
// a[0] -> 1:变量a也会改变
// a === b -> true:a和b引用同一个数组
- 以上仅仅是赋值的引用值:对象本身没有复制。
var a = ['a','b','c'];
var b = [];
for(var i = 0; i< a.length; i++){
b[i] = a[i];
}
// 显式复制对象
如果想 单独 比较两个对象或数组:
function equalArrays(a,b){
if(a.length != b.length) return false;
for(var i = 0; i < a.length; i++)
if(a[i] !== b[i]) return false;
return true;
}
类型转换
1 原始值 -> 原始值 [显式,隐式,方法]
2 原始值 -> 对象 [调用 构造函数 转化为 包装对象]
3 对象 -> 原始值
数字 -> 字符串
- 显式类型转换
Boolean() Number() String() Object()
当不通过new运算符调用这些函数时,它们会作为类型转换函数使用。
Number('3') //3 !!要求严格数字格式!!
String(false) // 'false' 相当于 false.toString()
Boolean([]) // true
Object(3) // new Number(3)
除了null或undefined之外的任何值都具有toString()方法,执行结果通常和String()方法返回的结果一致。如果试图把null和undefined转换为对象,Object()函数会返回一个新的空对象。
- 隐式类型转换
//如果'+'运算符的一个操作数是字符串,它将会把另外一个操作数转换为字符串。
//加号 一个字符 结果必定是字符串
var a0 = 1 + ''; // '1' 也是字符
var a1 = 1 + 'a'; // '1a'
var a2 = 1 + '1'; // '11'
var a3 = 1 + '1.2'; // '11.2'
//正常情况下,一元运算符 '+' '-' '*' '/' 将其操作数转换为数字。
//加号 没有字符 按照转换表相应转换 其他类型结果NaN
var a4 = 1 + true; // 2
var a5 = 1 + false; // 1
var a6 = 1 + null; // 1
var a7 = 1 + undefined; // NaN
var a8 = 1 + Infinity; // Infinity
var a9 = 1 + -Infinity; // -Infinity
var a10 = true + true; // 2
var a11 = null + null; // 0
var a12 = Infinity + Infinity; // Infinity
var b1 = 1 - 'a'; // NaN
var b2 = 2 - '1'; // 1
var b3 = 1 - true; // 0
var b4 = 1 - Infinity; // -Infinity
var b5 = Infinity - 1; // Infinity
var b6 = 1 * 'a'; // NaN
var b7 = 1 * '3'; // 3
var b8 = 1 * true; // 1
var b9 = 1 / '1'; // 1
var b10 = 1 / '2'; // 0.5
var b11 = 1 / true; // 1
var b12 = 1 / Infinity; // 0
var b13 = 1 / -Infinity; // -0
-----------------------------------
隐示类型转换
isNaN('10'); // false
if(10 && 'a') console.log('hello');
// if(Boolean(10) && Boolean('a'))
- Number.toString(radix) [10进制转其它进制]
作用:将一个数字转换为字符串。
参数:表示进制,默认十进制。
返回:当前数字在指定进制下的字符串形式。
注意:Number对象使用。
var n = 17;
console.log(n.toString(2));
console.log('0' + n.toString(8));
console.log('0x' + n.toString(16));
- Number.toFixed(digits)
作用:小数求有效数字。
参数:小数点后要显示的数字数量。省略相当于0。
- Number.toExponential(digits)
作用:科学计数法。
参数:小数点之后出现数字的数目。
- Number.toPrecision()
字符串 -> 数字
- parseInt(‘string’ , num) [其它进制转换10进制]
作用:从字符中解析整数。
参数:可以接收第二个可选参数,参数指定数字转换的基数,合法的取值范围是2~36。
特点:直解析整数。如果字符串前缀是 ’ 0x ’ , 可以将其识别成十六进制数。
console.log(parseInt('11', 2)); // 3 (1*2+1)
console.log(parseInt('ff', 16)); // 255 (15*16+15)
console.log(parseInt('zz', 36)); // 1295 (35*36+35)
console.log(parseInt('077', 8)); // 63 (7*8+7)
console.log(parseInt('077', 10)); // 77 (7*10+7)
- parseFloat()
作用:从字符中解析数值。
特点:可以解析整数和浮点数。
- 这两个方法是全局函数,不属于任何类的方法,更加灵活。
- 它们会跳过任意数量的前导空格,尽可能解析更多数值字符。
- 如果第一个非空格字符是非法的数字直接量,返回NaN。
console.log(parseInt('3 black boys'));
console.log(parseFloat('3.14 meters'));
console.log(parseInt('-12.34'));
console.log(parseInt('0xff'));
console.log(parseInt('0xFF'));
console.log(parseInt('-0xff'));
console.log(parseFloat('.1'));
console.log(parseInt('0.1'));
console.log(parseInt('.1'));
console.log(parseFloat('$72.98'));
对象 –> 布尔值
- 所有对象都转换为true。
- 包装对象也是。
var x = new Boolean(false); //这是一个对象
console.log(x);
console.log(Boolean(x)); //Boolean(对象)
对象 -> 数字,字符串
转换规则只适用于本地对象。宿主对象根据各自的算法可以转化成字符串和数字。
所有对象都继承了两个转换方法。
object.toString()
作用:定义一个对象的字符串表示形式。可以用于判断未知对象的类型。
返回:一个表示这个对象的字符串。[ object class ]
注意:大多数对象都有自定义版本的toString(),需要在对象上显式的调用:
Object.prototype.toString.apply(o);
- object.valueOf()
变量作用域
一个变量的作用域是程序源代码中定义这个变量的区域。
- 全局变量拥有全局作用域,在代码任何地方都有定义。
- 函数内声明的变量只在函数体内有定义。
- 它们是局部变量,作用域是局部性的。
- 函数参数也是局部变量,它们只在函数体内有定义。
在函数体内,局部变量的优先级高于同名的全局变量。
如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所遮盖。
var scope = "global"; // 声明一个全局变量
function checkscope() {
var scope = "local"; // 声明一个同名的局部变量
return scope; // 返回局部变量
}
checkscope()
尽管在全局作用域编写代码时可以不写var语句,但声明局部变量时则必须使用var语句。
var scope = " global scope";
function checkscope(){
var scope = "local scope";
function nested(){
var scope = "nested scope"
return scope;
}
return nested();
}
checkscope() // nested scope
函数作用域和声明提前
函数作用域:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
函数作用域是指在函数内声明的所有变量在函数体内始终可见。
这意味着变量在声明之前甚至已经可用。这个特性称为 声明提前,即函数里声明的所有变量都被“提前”至函数体的顶部。
var scope = "global",
function f(){
console.log(scope); // 输出undefined
var scope = "local"; // 变量在这里赋初始值,
// 但变量本身在函数体内任何地方均是可见的。
console.log(scope); // 输出local
}
在函数体内局部变量遮盖了同名全局变量。尽管如此,只有在程序执行到var语句的时候,局部变量才会被真正赋值。上述过程等价于:将函数内的变量声明“提前”至函数体顶部,同时变量初始化留在原来的位置。
function f(){
var scope; //在函数顶部声明了局部变量
console.log(scope); //变量存在,但其值是“undefined”
scope = "local"; //这里将其初始化并赋值
console.log(scope); //这里它具有了我们所期望的值
}
由于JS没有块级作用域,因此一些程序员特意将变量声明放在函数体顶部,而不是将声明靠近放在使用变量之处。这种做法使得他们的源代码非常清晰地反映了真实的变量作用域。
作为属性的变量
当声明了一个JS全局变量时,实际上定义了全局对象的一个属性。当使用var声明一个变量时,创建这个属性是不可配置的。也就是说这个变量无法通过delete运算符删除。
在没有使用严格模式的并给一个未声明的变量赋值的话,JS会自动创建一个全局变量。以这种方式创建的变量是全局对象的正常的可配置属性,并可以删除:
var truevar = 1; //声明一个不可删除的全局变量
fakevar = 2; //创建全局对象的一个可删除的属性
this.fakevar2 = 3; //
delete truevar // false 变量没被删除
delete fakevar // true 变量被删除
delete this.fakevar2 // true 变量被删除
JS全局变量时全局对象的属性。局部变量当做跟函数调用相关的某个对象的属性。ES3称该对象为“调用对象”,ES5规范称“声明上下文对象”。
作用域链
每一个JS代码都有一个与之关联的作用域链(scope chain)。
这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。
当JS需要查找变量x的值的时候(这个过程叫做“变量解析”):它会从链中的第一个对象开始查找,直到查找到属性。如果作用域链上没有任何一个对象含有这个属性,最后会抛出一个异常。
在JS最顶层代码中(也就是不包含任何函数定义内的代码),作用域链由一个全局对象组成。
在不包含嵌套的函数体内,作用域上有两个对象,一个是定义函数参数和局部变量的对象,一个是全局对象。
在一个嵌套函数里,至少含有三个对象。