JS之数据类型V.S.对象,引用传递和值传递,如何克隆对象
这是作者第一篇文章,之后会持续完善与JS相关的内容文集。
本篇文章主要解释了:
- primitive data types
- typeof返回值
- falsy values & truthy values
- object
- pass by value/reference
- how to clone an object
- shallow copy VS. deep copy
- 递归函数
1. 6种基本数据类型(6 primitive data types)
- 数字Numbers
- 字符串Strings
- 布尔值Booleans(true or false)
- 空值Null
- 默认值undefined
- 符号symbol(ES6特色)
注意,JS中总数据类型加上数组array和对象object一共有8种,除基本数据类型以外的类型都可看作对象object。
2. typeof返回值
数据类型和typeof属于不同概念:
typeof可能的返回值
3.1 falsy值:
- undefined
- null
- false
- +0, -0, NaN
- " "
3.2 truthy值:
- {}
- []
- 其他不属于以上的值
4. 对象Object
Javascript的设计是一个简单的基于对象的范式。一个对象就是一系列属性的集合,一个属性包含一个名和一个值。一个属性的值可以是函数(此时被称为方法)。
简单来讲就是数据和行为,包含名词和动词的一个整体。
注意
1.基本数据类型是不能改变的(immutable), 所有的修改其实是在声明新的变量;
2.可以对对象进行修改,因为对象是以引用(by reference)的方式被储存在内存中的。
5. 传值VS.传址
const o = {
a: 'a',
b: 'b'
};
const o2 = o;
o2.a = 'new value';
console.log(o.a); //new value
对象以参照的方式进行存储意味着从始至终对象在内存存储位置都不会变,把对象赋值给新对象,所谓的新对象也会指向原先对象的位置,对新对象进行修改,其变化会直接写入原对象(因为始终都是同一个东西)。
6. 如何克隆一个对象
那么如果我说我只是想要创建一个与原对象同等值(不同内存地址)的对象,怎么做才能使两个对象都是独立个体? 有两种方法:
方法1:手动再输入一遍所有的key-value pairs(较辛苦)
const o = {
a: 'a',
b: 'b'
};
const o2 = {
a: 'a',
b: 'b'
};
o2.a = 'new value';
console.log(o.a); //a
方法二:用Object.assign()重写:
const o = {
a: 'a',
b: 'b'
};
const o2 = Object.assign({}, o);
o2.a = 'new value';
console.log(o.a); //a
此处的Object.assign()功能很简单,就是把原先对象的所有key-value pairs都复制进现在所创建的新对象,于是两个对象虽然值都一样,但是在内存中相互分立,指向不同位置。
7. 浅拷贝 VS. 深拷贝
注意!!!
- 浅拷贝(shallow copy)是对引用(reference)的拷贝, 实际上存储的内存地址没有发生变化,如果其中有
key
是对象,那么复制后的对象还是指向原先的内存地址,所以只是拷贝了一份引用。 - 深拷贝和浅拷贝不同,就是彻底copy一个对象,而不是copy对象的引用,即重新创建了相同的值,但是完全不同的内存地址。
-
Object.assign()
对于没有嵌套类型为数组或者对象的数组和对象来说,其实可以理解为深拷贝(deep copy),对于有嵌套类型为数组或者对象的数组和对象,它就是浅拷贝。
const o = {
a: 'a',
b: 'b',
obj:{
key: 'key',
},
};
const o2 = Object.assign({}, o);
o2.obj.key = 'new value';
console.log(o.obj.key); //new value
深拷贝具体操作要用到library:
//deep copy
function deepCopy(obj) {
//check if vals are objs
//if so, copy that obj(deep copy)
//else return the value
const keys = Object.keys(obj);
const newObject = {};
for(let i = 0; i < keys.length; i++ ) {
const key = keys[i];
if(typeof(obj[key]) === 'object') {
newObject[key] = deepCopy(obj[key]); //此处的deepCopy()为递归函数
} else {
newObject[key] = obj[key];
}
} return newObject;
}
const o3 = deepCopy(o);
o.obj.key = 'new key !';
console.log(o3.obj.key); //key
实际上深拷贝和浅拷贝的概念只针对于数组和对象存在,对于一般的基本数据类型的拷贝,都是深拷贝,这样给b
赋值后,a
,b
和a
其实就是完全不同的两个变量,内存地址完全不一样,即修改任意变量的值,互相都不影响,互相独立。
let a = 1;//a是一个数字变量
let b =a;//赋值(拷贝)
8. 递归函数
递归函数通过它调用其本身。 上例中的deepCopy()就为递归函数,在还未定义完整个函数就可以在内部直接调用。阶乘的算法是最经典的一个递归函数:
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}