160 lines
6.3 KiB
Markdown
160 lines
6.3 KiB
Markdown
# 包装类型
|
||
|
||
|
||
|
||
## 经典真题
|
||
|
||
|
||
|
||
- 是否了解 *JavaScript* 中的包装类型?
|
||
|
||
|
||
|
||
## 包装类型
|
||
|
||
|
||
|
||
在 *ES* 中,数据的分类分为**基本数据类型**和**引用类型**。
|
||
|
||
|
||
|
||
按照最新 *ES* 标准定义,基本数据类型(*primitive value*)包括 *undefined、null、boolean、number、symbol、string*。
|
||
|
||
引用类型包括 *Object、Array、Date、RegExp* 等。
|
||
|
||
基本数据类型和引用类型这两个类型其中一个很明显的区别是,引用类型有自己内置的方法,也可以自定义其他方法用来操作数据,而基本数据类型不能像引用类型那样有自己的内置方法对数据进行更多的操作。
|
||
|
||
|
||
|
||
但基本数据类型真的就不能使用方法吗?对于部分基本类型来说确实是这样的。
|
||
|
||
但是有 *3* 个是 *ES* 提供了对应的特殊引用类型(包装类型)*Boolean、Number、String*。
|
||
|
||
基本包装类型,和其他引用类型一样,拥有内置的方法可以对数据进行额外操作。如下:
|
||
|
||
```js
|
||
var str = 'hello'; // string 基本类型
|
||
var s2 = str.charAt(0);
|
||
console.log(s2); // h
|
||
```
|
||
|
||
上面的 *string* 是一个基本类型,但是它却能调用 *charAt( )* 的方法。
|
||
|
||
其主要是因为在执行第二行代码时,后台会自动进行下面的步骤:
|
||
|
||
1. 自动创建 *String* 类型的一个实例(和基本类型的值不同,这个实例就是一个基本包装类型的对象)
|
||
2. 调用实例(对象)上指定的方法
|
||
3. 销毁这个实例
|
||
|
||
用代码的方式解释就是如下:
|
||
|
||
```js
|
||
//我们平常写程序的过程:
|
||
var str = 'hello'; // string 基本类型
|
||
var s2 = str.charAt(0); // 在执行到这一句的时候 后台会自动完成以下动作 :
|
||
(
|
||
var _str = new String('hello'); // 1 找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象
|
||
var s2 = _str.charAt(0); // 2 然后这个对象就可以调用包装对象下的方法,并且返回结给 s2.
|
||
_str = null; // 3 之后这个临时创建的对象就被销毁了, str =null;
|
||
)
|
||
console.log(s2); // h
|
||
console.log(str); // hello
|
||
```
|
||
|
||
|
||
|
||
基本类型的值虽然没有方法可以调用,但是后台临时创建的包装对象上有内置方法可以让我们调用方法,因此这样我们就可以对字符串、数值、布尔值这三种基本数据类型的数据进行更多操作。
|
||
|
||
而什么时候后台会自动创建一个对应的基本包装类型的对象,取决于当前执行的代码是否是为了获取他的值。
|
||
|
||
每当读取一个基本类型的值,也就是当我们需要从内存中获取到他的值时(这个访问过程称为读取模式),这时后台就会自动创建一个基本包装类型的对象。例如:
|
||
|
||
```javascript
|
||
var test = 'hhh'
|
||
console.log(test) // 读取模式,后台自动创建基本包装类型对象
|
||
var test2 = test // 赋值给变量 test2,也需要读取 test 的值,同上
|
||
```
|
||
|
||
基本包装类型的对象和引用类型的对象最大的一个区别是,对象的生存期不同,导致的一个结果就是,基本包装类型无法自定义自己的方法。
|
||
|
||
对于引用类型的数据,在执行流离开当前作用域之前都会保存在内存中,而对于自动创建的基本包装类型的对象,只存在于一行代码的执行瞬间,执行完毕就会立即被销毁。
|
||
如下:
|
||
|
||
```javascript
|
||
var str = 'test'
|
||
str.test = 'hhh'
|
||
console.log(str.test) //undefined
|
||
```
|
||
|
||
上面第二行代码给自动创建的 *String* 实例对象添加了 *test* 属性,虽然此刻代码执行时他是生效的,但是在这行代码执行完毕后该 *String* 实例就会立刻被销毁,*String* 实例的 *test* 属性也就不存在了。
|
||
|
||
当执行第三行代码时,由于是读取模式,又重新创建了新的 *String* 实例,而这个新创建的 *String* 实例没有 *test* 属性,结果也就是 *undefined*。
|
||
|
||
|
||
|
||
用代码的方式解释就是如下:
|
||
|
||
```js
|
||
var str = 'hello';
|
||
str.number = 10; //假设我们想给字符串添加一个属性 number ,后台会有如下步骤
|
||
(
|
||
var _str = new String('hello'); // 1 找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象
|
||
_str.number = 10; // 2 通过这个对象调用包装对象下的方法 但结果并没有被任何东西保存
|
||
_str =null; // 3 这个对象又被销毁
|
||
)
|
||
console.log(str.number); // undefined 当执行到这一句的时候,因为基本类型本来没有属性,后台又会重新重复上面的步骤
|
||
(
|
||
var str = new String('hello');// 1 找到基本包装对象,然后又新开辟一个内存,创建一个值为 hello 对象
|
||
str.number = undefined;// 2 因为包装对象下面没有 number 这个属性,所以又会重新添加,因为没有值,所以值是未定义;然后弹出结果
|
||
str =null; // 3 这个对象又被销毁
|
||
)
|
||
```
|
||
|
||
|
||
|
||
那么我们怎么才能给基本类型添加方法或者属性呢?
|
||
|
||
答案是在基本包装对象的原型下面添加,每个对象都有原型。
|
||
|
||
```js
|
||
//给字符串添加方法 要写到对应的包装对象的原型下才行
|
||
var str = 'hello';
|
||
String.prototype.last= fuction(){
|
||
return this.charAt(this.length);
|
||
};
|
||
str.last(); // 5 执行到这一句,后台依然会偷偷的干这些事
|
||
(
|
||
var _str = new String('hello');// 找到基本包装对象,new一个和字符串值相同的对象,
|
||
_str.last(); // 通过这个对象找到了包装对象下的方法并调用
|
||
_str =null; // 这个对象被销毁
|
||
)
|
||
```
|
||
|
||
|
||
|
||
## 真题解答
|
||
|
||
|
||
|
||
- 是否了解 *JavaScript* 中的包装类型?
|
||
|
||
> 参考答案:
|
||
>
|
||
> 包装对象,就是当基本类型以对象的方式去使用时,*JavaScript* 会转换成对应的包装类型,相当于 *new* 一个对象,内容和基本类型的内容一样,然后当操作完成再去访问的时候,这个临时对象会被销毁,然后再访问时候就是 *undefined*。
|
||
>
|
||
> *number、string、boolean* 都有对应的包装类型。
|
||
>
|
||
> 因为有了基本包装类型,所以 *JavaScript* 中的基本类型值可以被当作对象来访问。
|
||
>
|
||
> 基本类型特征:
|
||
>
|
||
> 1. 每个包装类型都映射到同名的基本类型
|
||
> 2. 在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据操作
|
||
> 3. 操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象
|
||
|
||
|
||
|
||
-*EOF*-
|
||
|
||
|