开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
数据类型
在 JavaScript 中,数据类型分为两大类:基本数据类型 和 引用数据类型。
基本数据类型又称为`原始值`,**引用数据类型**又称为 `引用值`
基本数据类型:string、number、boolean、undefined、null 以及 ES6 新添加的类型 symbol
引用数据类型:object
基本类型和引用类型的区别
两者的主要区别在于:在内存中的存储方式不同
原始值:存储于内存的 栈区 里面,是不可细化的最底层形式的数据。
引用值:存储于 堆区 里,值可以向下拆分。
- 访问方式、比较方式
原始值 是 按值访问 、按值比较 的
let str = 'Hello';
// 这里先在内存的栈区开辟一个空间存储变量str2,然后将str2对应的值赋为Hello
const str2 = str;
let str3 = 'Hello';
str = null; // 改变str对应的具体值,不会影响str2的值,因为两者在内存中是指向不同的地址
console.log(str, str2); // null, Hello
console.log(str === str3); // true
引用值 是 按引用访问、引用比较 的
`引用值` 存储的具体值是一个指向`堆内存`的地址,访问和比较时都是读取这个地址所指向的值。
let o = {
m: 1,
n: 2
};
// 这里赋值的不是o的值,而是o的引用地址,赋值后,o2和o指向同一个内存地址
let o2 = o;
// 这里定义的o3,值虽然与o相同,但是o3是存储在内存新开辟的空间中,与o指向的引用地址也就不是同一个
let o3 = {
m: 1,
n: 2
}
// 这里相当于将 o 和 o2 指向的地址中的 m 值修改了
o2.m = 10;
console.log(o, o2); // { m: 10, n: 2 } { m: 10, n: 2 }
console.log(o === o3); // false
- 动态属性
引用值 支持添加、修改、删除自定义属性和方法
原始值 只可以访问一些内置属性或方法
let o = {
m: 1
};
o.n = 10;
console.log(c); // { m: 1, n: 10 }
let s = 'Hello';
s.m = 'World!';
console.log(s, s.m); // Hello undefined
包装类型
- 为什么像
string、number、boolean这样的基本数据类型不能调用自定义方法,但是可以访问内置的方法?
let str = 'Hello';
// 可以访问内置方法charAt
let str2 = str.charAt(0);
console.log(str2); // ==> H
// 自定义方法 say
str2.say = function(name) {
console.log('Hello ' + name);
}
// 不可以访问自定义方法 say
str2.say('str2'); // str2.say is not a function
这是因为ES提供了对应的包装类型:Boolean、Number、String,当这三种类型的值访问内置方法时,会对数据进行如下的额外操作(以上述代码为例):
- 自动创建
String类型的一个实例(这个实例就是一个基本包装类型的对象) - 调用这个实例上指定的方法
- 销毁这个实例
对应代码如下:
let str = 'Hello';
let str2 = str.charAt(0);
// ===== 代码执行到上面↑这行代码时会做如下操作
{
let _str = new String('Hello'); // 创建一个String类型的实例
let str2 = _str.charAt(0); // 调用实例下的charAt方法,并赋值给str2
_str = null; // 销毁实例
}
console.log(str2); // ==> H
- 为什么自定义方法无法访问?
同理,当给基本类型添加自定义方法时,内部执行的过程是这样的:
let str = 'Hello';
let str2 = str.charAt(0);
// 自定义方法 say
str2.say = function(name) {
console.log('Hello ' + name);
}
str2.say('str2');
// ==== 内部执行过程
{
let _str = new String('Hello');
let str2 = _str.charAt(0);
let _str2 = new String(str2); // 根据str2的值创建对应的实例
_str2.say = function(name) { // 在该实例上定义say方法
console.log('Hello ' + name);
}
_str2 = null; // 销毁实例
_str = null;
}
通过上面代码可以发现,say方法是定义在 _str2 这个临时实例上,并不是直接添加到 str2 上,所以后续使用 str 去调用 say 方法时就会报错。
- 如何才能给基本类型添加自定义属性或者方法?
可以在基本包装类型的原型上添加:
let str = 'Hello';
let str2 = str.charAt(0);
// 自定义方法 say
String.prototype.say = function(name) {
console.log('Hello ' + name);
}
str2.say('str2'); // Hello Str2
但是这会带来其它的问题,比如:所有String类型的值都存在say方法,污染了原型