最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 【JS】string和String差异详解,基本类型和包装类的差异对比详解

    正文概述 掘金(anysunflower同学)   2020-12-22   491

    JS数据基本类型和引用类型


    基本类型:undefined、null、string、number、boolean、symbol(ES6)

    普通基本类型:undefined、null、symbol(ES6)

    特殊基本包装类型:String、Number、Boolean

    引用类型:Object、Array、RegExp、Date、Function

    区别:引用类型值可添加属性和方法,而基本类型值则不可以。

    基本类型

    • 基本类型的变量是存放在栈内存(Stack)里的
    • 基本数据类型的值是按值访问
    • 基本类型的值是不可变的
    • 基本类型的比较是它们的值的比较

    引用类型

    • 引用类型的值是保存在堆内存(Heap)中的对象(Object)
    • 引用类型的值是按引用访问的
    • 引用类型的值是可变的
    • 引用类型的直接比较是引用地址的比较

    (NaN是数字类型) JS数据类型

    包装类


    中文里面JS的String、Number、Boolean翻译成包装类,那么它们和普通的string、number、boolean具体有什么差别呢? 以string为例子我们来看下基本数据类型和其包装类的差别和关系是什么?

    string vs String

    我们知道基本数据类型的值是直接保存在栈内存当中的,而且在内存中是连续保存的,按值访问。像null这种基本数据类型不能进行.property的操作,因为它就是个值。 按理说.property的方式基础数据类型应该做不到的,但是在日常使用中,对于也是基本数据类型的string,直接.length就可以获取到它的长度。

    var a='test';
    console.log(a.length);//4
    
    var b=null;
    console.log(b.length);//Uncaught TypeError: Cannot read property 'length' of null
    

    为什么string就可以,null就不可以呢?

    前面的类型总结当中我们可以看到js当中除了string还有String,这两者并不是一个东西。

    String(val)是什么?在做什么?

    以String为例子,我们可以看到在ECMA262的文档中与String相关的有这几种定义:

    • String value:内存中有限长度的有序数值的原始值
    • String type:所有String value的组合
    • String Object:显然这是个Object,是js内置String constructor的实例。当使用new String(str)形式调用的时候就会创建该对象。

    我们可以看到String相关的几种定义里面,涉及到了Object,那么.length这些属性是不是通过Object来实现的呢?

    var a = String('a');
    var a2 = String('a');
    console.log(a === a2);
    //true
    
    var b = new String('b');
    var b2 = new String('b');
    console.log( b === b2);
    // false
    console.log( b == b2);
    // false
    

    【JS】string和String差异详解,基本类型和包装类的差异对比详解

    果然,我们看到new String('b')最后是返回了一个特殊的String对象(本质还是Object),具有length属性,所以,当然直接b.length就可以访问到它的长度。

    而且,我们可以看到实例b的__proto__指向的是String,展开我们可以看到一个特殊的String.prototype对象,我们平常常用的一些对string的操作方法都定义在这个对象上了。

    【JS】string和String差异详解,基本类型和包装类的差异对比详解

    从现在来看,对于new String(val)怎么能访问到.length这些属性,还是比较好理解的,因为它返回的就是一个特殊的String对象的实例,所以当然可以访问到String原型上定义的各种方法啦。也就是说,String的length访问其实归根到底,还是借助特殊的Object来实现的。

    new String()和String()的区别

    我们知道new关键字的过程涉及到新对象的创建,所以,new String(str)的结果返回的一个新的String实例,所以,b和b2保存的是两个对象的引用,他们的引用地址不一样,直接比较的话,逻辑引用类型的比较是一样的,结果就是不相等。

    在ecma262当中,对于String实例的属性是这样描述的:

    String instances have a "length" property, and a set of enumerable properties with integer-indexed names. 字符串实例属于String exotic objects。实例继承了String prototype object的属性,也有[[StringData]]内置属性。同时String instances也有length 属性。

    那为什么String(str)的值还是能按值比较呢?这个看起来就是个构造函数方法哇?像Object即使不结合new去使用,最后返回的引用实例类型也不一样。

    var c = Object('a'); 
    var d = Object('a');
    console.log(c === d);
    //false
    

    然后我们看一下ecma262/#sec-string-constructor当中对于String constructor(也就是String(...))也中有两句是这么描述的:

    • creates and initializes a new String object when called as a constructor.
    • 作为构造函数被调用的时候(就是被new的时候),会创建和初始化一个新的String对象
    • performs a type conversion when called as a function rather than as a constructor.
    • 被当做函数调用的时候(就是直接String(...)),会进行类型转换

    也就是说当不用new的时候,String(...) === toString(...) 用代码来验证一下:

    var a = String(1);
    var b = '1';
    console.log( a === b);
    // true
    
    var  c = String({a:1});
    var d = "[Object Object]"
    console.log( c === d);
    // true
    console.log(d.length);
    //15
    

    从上面的结果来看,String(val)其实跟toString的效果没什么差别,最后也是返回一个普普通通的string字符串,但是我们看到它还是可以调用.length,这是为啥? 我们来看这句代码输出的结果

    console.log('1'.__proto__);
    

    【JS】string和String差异详解,基本类型和包装类的差异对比详解 我们可以看到,'1'字符串是可以直接取到输出原型对象的,按理说'1'就是个简单的String value,它不是通过new String(val)创建的,为什么可以输出__proto__呢? 而且,我们对其添加属性的话并不会成功,如下代码可以正常执行,但是添加的属性最后访问到的数值是undefined

    var a = '2';
    a.haha = '123';
    console.log(a);//2
    console.log(a.haha);//undefined
    

    不是说好的,String value是按值访问的吗,为什么String value还可以访问属性呢? 关键就是在这个.操作上。看到了stackoverflow上的回答,终于知道点操作的学名原来叫Property Accessors stackoverflow/difference-between-the-javascript-string-type-and-string-object

    Property Accessors(.XXX)的时候发什么了什么


    在我们看ecma262中MemberExpression.IdentifierName(即类似a.b)这种方式获取属性的时候会发生什么? a.property时关键的方法就是evaluate-property-access-with-identifier-key 在这个方法当中,会对调用ToObject(a)方法。 所以关键就在于,ToObject方法到底对不同数据类型的参数做了什么操作? 【JS】string和String差异详解,基本类型和包装类的差异对比详解

    在这个表格中,我们看到了String类型那一行三个显眼的单词new String object,结合我们最开始了解到的new String()做了啥了,我们就清楚了,原来对于普通String value来说,当对它调用.操作的时候,js会默默的用ToObject操作,调用new String(val)创建一个新的String Object。这个过程当中,会通过reference操作,查找新的String Object上是不是具有所要访问的属性值。

    所以这个length其实不是'1'这个String value的属性,而是String Obj实例的属性。

    所以从代码上来说,我们可以简单理解为:

    '1'.length的时候不是因为'1'具有`length`属性,而是创建了个String对象实例,访问到了这个实例上的length属性。
    var a = new String('1');// a是个String object是个对象指向了String.prototype对象
    a.length;
    

    有一个细节要注意,因为new String(val)返回值其实就是一个objectToObject(obj)返回的是这个obj本身,所以ee.heihei = '123',相当于是直接对ee添加了个属性,因此后续还是能够访问到它。

    var ee = new String('a'); 
    console.log(ee.heihei);
    //undefined
    ee.heihei = '123';
    console.log(ee);
    

    【JS】string和String差异详解,基本类型和包装类的差异对比详解

    对于'1'.haha访问不到,是undefined,很多文章解释是,包装类创建完后会把对象销毁,在ECMA规范中我貌似没有直接找到与销毁相关的描述,所以对于为什么访问不到我大概的理解是:

    但是对于'1'.haha='123'来说,每次.操作的时候都是创建了新的String obj

    '1'.haha = '123';
    创建了String obj
    console.log('1'.haha);
    又创建了另一个String obj,每一个全新的String obj都没有haha属性
    

    所以即使你做了赋值操作,也不能在后续访问到其上面的haha属性的数值。

    ToObject的处理逻辑,null/undefined 调用.操作就会报错的真正原因就很清楚啦。


    起源地下载网 » 【JS】string和String差异详解,基本类型和包装类的差异对比详解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元