Round 1 数据类型相关
数据类型
JavaScript 的数据类型共有六种(ES6 又新增了第七种 Symbol 类型的值):
数值(number)、字符串(string)、布尔值(boolean)、undefined
、null
、对象(object)。
对象又可以分为三个子类:狭义的对象(object)、数组(array)、函数(function)。
其中数值、字符串、布尔值、undefined
和 null
,合称为基本数据类型;而对象称为引用数据类型。
什么是基本数据类型?什么是引用数据类型?
在理解“什么是什么是基本数据类型,什么是引用数据类型”之前,我们先要知道什么是堆内存和栈内存。
栈内存
栈内存主要用于简单存储,一般存储一些大小已知或者有上限的变量。栈内存一般为有序存储,容量小,系统分配效率高。
堆内存
堆内存主要用于存储一些大小未知变量。堆内存存储时不仅要在堆内存中分配存储区域,还要把引用地址存到栈内存中,效率相对较低。
基本数据类型
基本数据是存在栈内存中的简单数据段,可以直接按值访问。
举个栗子:
var a = 1;
var b = a;
b = 2;
console.log(a); //1
- 声明一个变量
a = 1;
此时a
被存的栈内存中; - 声明一个变量
b = a;
此时b
获得了a
值的拷贝,这个时候虽然两个变量的值相等,但是实际上两个变量是保存了两个不同的基本数据类型; - 改变变量
b = 2;
此时b
变量发生改变但是不会影响变量a
;
引用数据类型
引用数据是存在堆内存中的对象,需要通过指向储存对象的内存地址的指针进行访问。
举个栗子:
var obj1 = new Object();
var obj2 = obj1;
obj2.attr= "添加属性";
console.log(obj1.attr); // 添加属性
- 声明一个对象
obj1 = new Object();
此时obj1
被存的栈内存中,而obj1
所对应的值是存在堆内存中的对象的一个引用地址(指针); - 声明一个对象
obj2 = obj1;
此时obj2
获得了obj1
值的拷贝,此时obj2
就获得了存在堆内存中的对象的引用地址(指针),这样他们共同指向了同一个堆内存中的对象; - 给对象添加属性
obj2.attr= "添加属性";
此时obj2
实际上是为堆内存中的对象添加属性,所以obj1
同样受到影响;
数据类型确定方法
使用 typeof
确定数据类型(typeof xxx
)
数值 —— number
字符串 —— string
布尔值 —— boolean
函数 —— function
undefined
—— undefined
[]
, {}
, [...]
, {...}
, null
—— object
使用 instanceof
确定数据类型(object instanceof constructor
)
使用 Object.prototype.toString
确定数据类型
null 和 undefined
null:空值
undefined:未定义
undefined == null
—— true
Number(null)
—— 0
Number(undefined)
—— NaN
数值
JavaScript 内部,所有数字都是以64位浮点数形式储存(1 === 1.0
—— true
)。
0.1 + 0.2
—— 0.30000000000000004
0.1 + 0.2 === 0.3
—— false
国际标准 IEEE 754,JavaScript 浮点数的 64
个二进制位
- 符号位(第1位):决定正负(0 —— 正数,1 —— 负数)
- 指数部分(第2位到第12位):决定大小 ——
64
位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047
(2的11次方减1)。也就是说,64
位浮点数的指数部分的值最大为2047
,分出一半表示负数,则 JavaScript 能够表示的数值范围为2
1024
到2
-1023
,超出这个范围的数无法表示(正向溢出:返回Infinity
;负向溢出:返回0
)。
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
- 小数部分(第13位到第64位):决定精度 —— 精度最多只能到53个二进制位,所以,绝对值小于2的53次方的整数,即
-2
53
到2
53
可以精确表示。
数值表示
- 自动将数值转为科学计数法的几种情况:
- 小数点前的数字多于21位;
- 小数点后的零多于5个
- 进制表示:
- 十进制:没有前缀;
- 二进制:前缀
0b
或0B
; - 八进制:前缀
0o
或0O
(或者有前导0、且只用到0-7的数值); - 十六进制:前缀
0x
或0X
。
特殊数值
-
+0 / -0
:除了(1 / +0) === (1 / -0) // false
,正零和负零是等价的; -
NaN
:NaN
不等于任何值,包括它本身; -
Infinity
:Infinity
与NaN
比较,总是返回false
;
数值相关操作
-
parseInt()
: 将字符串转为整数
-
parseFloat()
: 将一个字符串转为浮点数
-
isNaN()
: 用来判断一个值是否为NaN
-
isFinite()
: 判断某个值是否为正常的数值
字符串
- 长字符串分成多行,可在每行尾部使用反斜杠;
- 反斜杠(
\
)表示字符(应该字符的 Unicode 码点)的三种用法:-
\
后面紧跟三个八进制数 -
\x
后面紧跟两个十六进制数 -
\u
后面紧跟四个十六进制数
-
-
btoa()
:任意值转为 Base64 编码
atob()
:Base64 编码转为原来的值
对象
对象相关操作
-
Object.keys
:查看一个对象本身的所有属性 -
delete
:删除对象的属性,删除成功后返回true
,存在且不得删除返回false
-
in
('属性名' in 对象
):检查对象是否包含某个属性,包含就返回true
,否则返回false
-
hasOwnProperty
(对象.hasOwnProperty('属性名')
):判断是否为对象自身的属性 -
for...in
:遍历一个对象的全部属性
-
with
:方便操作同一个对象的多个属性
var obj = {
a: 1,
};
with (obj) {
a = 2;
b = 3;
}
obj.a // 2;
obj.b // undefined
b // 3;
函数
作用域
举个栗子:
var a = 1;
var x = function () {
console.log(a);
};
function f() {
var a = 2;
x();
}
f() // 1
函数 x
在声明的时候是处于最外层作用域,即使在函数 f
内部被调用,也不会取 f
内部的变量 a
的值,所以会返回 1
而不是 2
。
参数
- 有同名的参数,则取最后出现的那个值;
-
arguments
:函数运行时的所有参数,只有在函数体内部,才可以使用
-
arguments
转为真正的数组的两种方法:
var args = Array.prototype.slice.call(arguments);
-
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
-
callee
:返回它所对应的原函数(可以通过arguments.callee
调用函数自身)
闭包
闭包的两个最大用处:
- 可以读取函数内部的变量,这些变量始终保持在内存中,即闭包的诞生环境一直存在;
举个栗子:记住上一次调用时的运算结果
function fun(n) {
return function () {
return n++;
};
}
var f = fun(1);
f() // 1
f() // 2
f() // 3
- 封装对象的私有属性和私有方法
eval
只要不是直接调用,都属于别名调用,别名调用的作用域是全局作用域。
数组
-
length
最大值是 4294967295; - 数组本质上是对象,所以可以为数组添加属性(但是不影响
length
属性);
函数相关操作
name
:返回函数的名字
var myFunc = function () {
console.log(11111);
}
function test(f) {
return f.name;
}
eval(test(myFunc))(); //11111
new Function('return ' + test(myFunc))()(); //11111
length
:返回函数预期传入的参数个数,即函数定义之中的参数个数
toString
:返回函数的源码字符串(原生的函数返回原生代码提示 function (){[native code]}
)
类型转换
强制转换
-
Number()
—— 数字
a. 原始类型值
Number(123)
—— 123
Number('123')
—— 123
Number('123abc')
—— NaN
Number('')
—— 0
Number(true)
—— 1
Number(false)
—— 0
Number(undefined)
—— NaN
Number(null)
—— 0
b. 对象
Number({a: 1})
—— NaN
Number([1, 2, 3])
—— NaN
Number([1])
—— 1
-
String()
—— 字符串
转换规则同Number()
只是调换了valueOf()
和toString()
的顺序。
a. 原始类型值
String(123)
——"123"
String('abc')
——"abc"
String(true)
——"true"
String(undefined)
——"undefined"
String(null)
——"null"
b. 对象
String({a: 1})
——"[object Object]"
String([1, 2, 3])
——"1,2,3"
-
Boolean()
—— 布尔值
undefined
、null
、false
、0
、NaN
、" "、' ' 为false
,其他都为true
。
空数组([]
)和空对象({}
),都是true
隐式转换:
- 不同类型的数据互相运算(
+-*/
)-
+
:有可能转为字符串
注:
'1' + {}
——"1[object Object]"
'1' + function (){}
——"1function (){}"
'1' + undefined
——"1undefined"
'1' + null
——"1null"
-
-
或*
或/
:转成数值
-
- 对非布尔值类型的数据求布尔值
-
!()
:同Boolean()
-
- 对非数值类型的值使用一元运算符(
+/-
)- 转成数值
注:
+{}
——NaN
+{a: 1}
——NaN
+[]
——0
+[1, 2]
——NaN
- 转成数值
- 比较判断(
===
、!==
、==
、!=
、>
、>=
、<
、<=
)- 转成布尔值