js 数据类型


js数据类型

在ECMAScript规范中,共定义了7种数据类型,分为基本类型和引用类型两大类

1.基本类型(5种)

Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存(stack)中的简单数据段,可以直接访问。

2.引用类型

何为引用类型呢?这和它的存储有关,变量保存的其实是一个指向特定位置的地址指针。当我们需要获取该引用类型变量值的时候,首先获取到地址指针,然后再从堆内存(heap)中获取到所需要的数据。在js中引用类型基本就是 Object 、Function 、Array、RegExp、Date

引用类型也称为复杂类型,由于其值的大小会改变,所以不能将其存放在栈中,否则会降低变量查询速度,因此,其值存储在堆(heap)中

js数据类型的判断

1.typeof

1
2
3
4
5
6
7
8
9
10
typeof 'test'; // string 有效
typeof 1; // number 有效
typeof Symbol(); // symbol 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Function(); // function 有效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效

总结上述结论:

  • 对于基本类型,除null以外,均可以返回正确的结果。对于null返回object类型。
  • 对于引用类型,除function以外,一律返回object类型。对于function返回function类型。

2. instanceof

instanceof是用来判断A是否为B的实例,表达式为: A instanceof B,如果A是B的实例,则返回 true,否则返回 false。

1
2
3
4
[] instanceof Array; //true
[] instanceof Object; //true
{} instanceof Object;//true
new Date() instanceof Date;//true

可以看到instanceof能够判断出[]Array的实例,但它认为[]也是Object的实例,
原因是 instanceof 能够判断出[].__proto__ 指向 Array.prototype,而 Array.prototype.__proto__ 又指向了Object.prototype,最终Object.prototype.__proto__ 指向了null,标志着原型链的结束。因此,[]ArrayObject 就在内部形成了一条原型链。

因此 instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。

所以判断一个对象是数组类,可以使用

1
2
3
functin isArray(obj){
return obj.__proto__ === Array.prototype;
}

3. 有个万能的判断数据类型的方法就是使用 Object.prototype.toString.call()

1
2
3
4
5
6
Object.prototype.toString.call(null)
//"[object Null]"
Object.prototype.toString.call(undefined)
//"[object Undefined]"
Object.prototype.toString.call({})
//"[object Object]"

js中的深浅拷贝

1.浅拷贝

1.1. 简单的引用复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 浅拷贝实现
function shadowCopy(target, source) {
if( !source || typeof source !== 'object') {
return;
}

if( !target || typeof target !== 'object') {
return;
}

for(var key in source) {
if(source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}

const test = {a: 1, b: 2, c:[1,2,3]}
const testCopy = shallowClone(test, {});
console.log(test.c === testCopy.c); // true

1.2. Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

1
2
3
const test = {a: 1, b: 2, c:[1,2,3]}
const testCopy = Object.assign({}, test);
console.log(test.c === testCopy.c); // true

1.3. Array的slice和concat方法
类似于第一种情况,具体的拷贝规则如下:

  • 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。

  • 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。

2.深拷贝

2.1. JSON对象的parse和stringify
JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝。

这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。

2.2. 借助lodash.js 中的方法实现深拷贝 _.cloneDeep(value)

2.3. 实现深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;

// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}

// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}

// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}

throw new Error("Unable to copy obj! Its type isn't supported.");
}


Author: 杜宏飞
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source 杜宏飞 !
  TOC