js中的装箱和拆箱操作

装箱操作: 把基本数据类型转换为对应的引用类型的操作
拆箱操作: 把引用类型转换为基本数据类型的操作

上面两个概念只是简洁的实质总结,可能看完后大家心里会产生疑问,到底怎么样叫装箱操作和拆箱操作呢?下面我们一起带着这个疑问往下看~~

 

装箱操作

首先我们要知道在js中有三个基本包装类型:

  • Number
  • String
  • Boolean

下面看一个例子:

  var str="hello world";
  var strRes=str.split(" ");
  console.log(strRes)        //["hello", "world"]

如上面代码所示,变量str是一个基本类型值,不是一个对象,就不存在方法,但上面代码却显示可以正常调用方法。实际上这一切都是js内部做了以下处理:

  • (1)创建String类型的一个实例;
  • (2)在实例上调用指定的方法;
  • (3)销毁这个实例;

转换为对应代码就是:

var str=new String("hello world");
var strRes=str.split(" ");
str=null;

其实,说白了就是临时创建了一个对象,然后去调用方法。下面这句话引用自《javascript高级程序设计》一书中

每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。

 

拆箱操作

拆箱操作中主要有两个方法:valueOf()方法和toString()方法。这两个方法主要用来检测你返回的是不是一个基本类型的值。一般是先用valueOf()来检测,如果返回的不是一个基本类型的值,是对象自身,则会继续用toString()来检测,如果检测结果不是一个基本类型的值,则会报错(Uncaught SyntaxError: Invalid or unexpected token)。以下是两个方法的具体描述,引用自MDN。

valueOf

  1. valueOf() 方法返回指定对象的原始值。
  2. JavaScript调用valueOf方法将对象转换为原始值。你很少需要自己调用valueOf方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。
  3. 默认情况下,valueOf方法由Object后面的每个对象继承。 每个内置的核心对象都会覆盖此方法以返回适当的值。如果对象没有原始值,则valueOf将返回对象本身。
  4. JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。因此,不同类型对象的valueOf()方法的返回值和返回值类型均可能不同

toString

  1. toString() 方法返回一个表示该对象的字符串。
  2. 每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。
  3. 默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中 type 是对象的类型。

下面通过代码来分析具体什么叫做拆箱操作:

[]+[]    //""
{}+{}    //"[object Object][object Object]"
[]+{}    //"[object Object]"
{}+[]    //0

如上面代码所示,右边注释的是其对应的结果,下面逐个分析其中的原理:

  • (1)[]+[],[]自身是一个空数组,即是一个对象,[]会先被valueOf()检测返回自身,还是[],然后使用toString()检测返回空字符"",实际最终是""+"",所以最终结果还是一个""。
  • (2){}+{},在js中{}可以表示一个代码块,也可以表示一个对象。在此处作为一个对象来运算,({}).valueOf()检测结果为自身,继续检测,({}).toString()检测结果为"[object Object]",所以{}+{}相当于"[object Object]"+"[object Object]",故结果为"[object Object][object Object]"。
  • (3)[]+{},从上面分析可以知道,这个相当于""+"[object Object]",所以结果为"[object Object]"。
  • (4){}+[],上面三个或许大多数人都能明白,但这个估计就会有人有疑问了,为什么会是0呢?首先,这里的{}被当做了代码块,由于编译原理底层一些机制,会涉及到词法分析、语法分析、语义分析、代码生成这些知识,这里+[]相当于+"",因为运算符+的原因,会将+""隐式转换为+0,所以结果最终为0。

 

相信大家到这里应该基本明白什么是装箱操作,什么是拆箱操作了吧!

 

拓展

通过上面的学习,基本原理大家应该都已经知道了,在这留一个例题,大家可以将自己的分析或想法写在评论区,看看是不是真正的理解了其中的知识,例题如下(结果直接告诉大家了,具体还是需要自己去一步一步分析,才能真正掌握理解,我相信大家都是很厉害的哦!!!):

([][[]]+[])[+!![]]+([]+{})[!+[]+!![]]    //"nb"

!! 一般用来将后面的表达式强制转换为布尔类型的数据(boolean),只能是true or false.
例:!![] //true,[]自身就是对象,会先转换成true,![]会将其转换为false,!![]再将其转换为true,实际上就是自身的布尔值.


————————————————
版权声明:本文为CSDN博主「DLGDark」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/DLGDark/article/details/100836377

 

 

 

 

 

 

上面拓展中的例题分析:

知识点一:  javascript字符串下标获取元素(涉及装箱)

js我们一般都是通过字面量的方式创建String
var str = "abc"
当操作字符串时会转换为字符串对象
var str = new String('abc');
图片描述

 

js中String类的实例,是一个类数组。

clipboard.png

所以你可以通过str[0]获取一个String对象中键名为0的值。

而charAt是String类的一个api。

clipboard.png

两者区别:
clipboard.png

下标不存在时,str[index] 会返回 undefined (未定义), str.charAt(index)会返回""(空字符串)

 

字符串是一个类似数组的对象,有length属性,可以通过数值键(0,1)取值;
str[i] 与 str.charAt(i)本质上没有区别,只是str.charAt(i)更标准而已

知识点二:运算符优先级

下面的表将所有运算符按照优先级的不同从高(20)到低(1)排列。

优先级运算类型关联性运算符
20圆括号n/a(不相关)( … )
19成员访问从左到右… . …
需计算的成员访问从左到右… [ … ]
new (带参数列表)n/anew … ( … )
函数调用从左到右… ( … )
可选链(Optional chaining)从左到右?.
18new (无参数列表)从右到左new …
17后置递增(运算符在后)n/a
 
… ++
后置递减(运算符在后)… --
16逻辑非从右到左! …
按位非~ …
一元加法+ …
一元减法- …
前置递增++ …
前置递减-- …
typeoftypeof …
voidvoid …
deletedelete …
awaitawait …
15从右到左… ** …
14乘法从左到右
 
… * …
除法… / …
取模… % …
13加法从左到右
 
… + …
减法… - …
12按位左移从左到右… << …
按位右移… >> …
无符号右移… >>> …
11小于从左到右… < …
小于等于… <= …
大于… > …
大于等于… >= …
in… in …
instanceof… instanceof …
10等号从左到右
 
… == …
非等号… != …
全等号… === …
非全等号… !== …
9按位与从左到右… & …
8按位异或从左到右… ^ …
7按位或从左到右… | …
6逻辑与从左到右… && …
5逻辑或从左到右… || …
4条件运算符从右到左… ? … : …
3赋值从右到左… = …
… += …
… -= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
2yield从右到左yield …
yield*yield* …
1展开运算符n/a... …
0逗号从左到右… , …

知识点三:+号的运算逻辑 (涉及拆箱)

进行 + 号运算时会将 + 号两侧的操作对象转化为原始对象(此过程实际上由javaScript引擎内部的抽象操作 ToPrimitive(input [, PreferredType]) 函数执行,当某个对象出现在了需要原始类型才能进行操作的上下文时,JavaScript 会自动调用 ToPrimitive 函数将对象转化为原始类型)。常见的 + 号运算逻辑有以下两种:

(1)字符串连接符+  :会把其他数据类型调用String()方法转成字符串然后拼接;

(2)算数运算符+ :会把其他数据类型调用Number()方法转成数字然后做加法计算;

 

+ 号被当做 字符串连接符 时的情况:

  • + 号两侧的操作数至少一个为字符串;
  • 其中一个为非数值类型或非字符串类型,且调用toPrimitive(input,Number)方法得到String类型结果

 

+ 号被当做 算术运算符 时的情况也分为两种:

  • + 号两侧的操作数均为数字
  • 其中一个为非数值类型或非字符串类型,且调用toPrimitive(input,Number)方法得到Number类型结果;

 

 

知识点四:逻辑非隐式转换

逻辑非:将其他数据类型使用Boolean()转成布尔类型  ( !val 的运算逻辑是:先将 val 转换为布尔型,再取反)

以下八种情况转换为布尔型会得到false:

  • 0
  • -0
  • NaN
  • undefined
  • null
  • ""(空字符串)
  • false
  • document.all()

除以上八种情况之外所有数据都会得到true。

因此对表达式:

([][[]]+[])[+!![]]+([]+{})[!+[]+!![]]

分析如下:

(1)圆括号优先级最高,因此先计算圆括号中的表达式,先看第一个圆括号中的表达式:

([][[]]+[]) == "undefined"
// 根据运算符优先级先运算 + 号两侧操作数,
// 先看左侧的操作数,从左至右,[]即为空数组,其后跟随[[]],则外层[]表示需计算的成员访问,因为[]为空数组,因此访问其成员得到的结果为 undefined,
// + 号右侧的[]通过toPrimitive(input,Number)方法得到的计算结果为 [].valueOf().toString() == '', 
//此时的计算表达式变为:undefined+'',根据 + 号运算规则,此时为字符串拼接操作,
//因此 ([][[]]+[])  结果为:"undefined"

再看第二个圆括号中的表达式:

([]+{}) == "[object Object]"
//根据 + 号的运算规则,+ 号两侧的操作数通过toPrimitive(input,Number)方法得到的结果分别为 [].valueOf().toString() == "", ({}).valueOf().toString() == "[object Object]"
//因此 ([]+{}) 结果为 "[object Object]"

此时表达式已转化为:

"undefined"[+!![]]+"[object Object]"[!+[]+!![]]

(2)根据运算符优先级可知,上面的表达式即为:

"undefined"[+!![]]  +  "[object Object]"[!+[]+!![]]

则接下来继续计算中间 + 号两侧的操作数,首先看左边的操作数:

[+!![]] == [1];
"undefined"[+!![]] == "undefined"[1] == 'n';
//表达式中外层的[]表示需计算的成员访问,而[]内部的表达式 +!![] 则首先进行两次逻辑非的运算,得到 +true,根据 + 号运算规则其结果为 +true == 1,
//因此 "undefined"[+!![]] 即为 "undefined"[1],根据知识点一可知其结果为 'n',
//即 "undefined"[+!![]] 结果为 'n'

根据运算符优先级和左边操作数的计算规则,可得出右边的操作数结果为:

!+[]+!![] == 2;
"[object Object]"[!+[]+!![]] == "[object Object]"[2] == "b"

所以,最终的表达式结果为:

"n" + "b" == "nb"

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值