本文转载自微信公众号《菜鸟小战》,作者陈皮皮,弱势书生。转载本文请联系菜鸟小站公众号。前言随着JavaScript越来越流行,越来越多的开发者开始接触和使用JavaScript。同时,我也发现很多开发者对JavaScript最基本的原始值和包装对象并没有清晰的认识。那么在这篇文章中,渣皮就为大家详细介绍一下。话不多说,走吧!文本原始类型(Primitivetypes)原始类型也称为“基本类型”。目前JavaScript中有以下基本类型:string(字符串)number(数字)boolean(布尔)null(空)undefined(未定义)bigint(大整数,ES6)symbol(flag?ES6)如下:typeof'chenpipi';//"string"typeof12345;//"number"typeoftrue;//"boolean"typeofnull;//"object"typeofundefined;//"undefined"typeof12345n;//"bigint"typeofSymbol();//"symbol"特别注意typeofnull返回的是“object”,但这并不意味着null就是一个对象。这其实是JavaScript的一个bug,从JavaScript诞生之日起就一直如此。在JavaScript的原始实现中,JavaScript中的值由指示类型和实际数据值的标记表示。对象的类型标号为0。由于null表示空指针(在大多数平台上为0x00),null的类型标号为0,因此typeofnull返回“object”。“typeofnull”的历史:https://2ality.com/2013/10/typeof-null.html原始值(Primitivevalues)原始值也就是原始类型的值(数据)。原始值是不是对象且没有方法的数据。原始值是不是对象且没有方法的数据。也就是说,string、number、boolean等原始类型的值是没有任何属性和方法的。这时候,嗅觉灵敏的小伙伴们有没有察觉到不对劲呢?是孜然!我加了孜然!(手动狗头并划掉)这里有一个很有意思的点,不过在讨论这个问题之前,我们先来认识一下wrapperobjects。包装对象(Wrapperobjects)原始类型除了null和undefined都有对应的包装对象:String(字符串)Number(数字)Boolean(布尔)BigInt(大整数,ES6)Symbol(标志?ES6)对象(Object)对象是引用类型。首先,wrapper对象本身是一个对象,也是一个函数。StringinstanceofObject;//trueStringinstanceofFunction;//真正的构造函数(Constructor)实例(Instance)其中String、Number和Boolean都支持使用new运算符创建对应的包装对象实例。比如String的声明(摘录):interfaceStringConstructor{new(value?:any):String;(value?:any):string;readonlyprototype:String;}declarevarString:StringConstructor;使用new运算符得到的数据是一个对象(Object)//stringtypeof'pp';//"string"typeofnewString('pp');//"object"newString()instanceofObject;//true//numbertypeof123;//“数字”typeofnewNumber(123);//"object"newNumber()instanceofObject;//true//Booleantypeoftrue;//"boolean"typeofnewBool??ean(true);//"object"newBool??ean()instanceofObject;//true我们可以调用valueOf()函数得到它的原始值://stringlets=newString('pp');s.valueOf();//"pp"typeofs.valueOf();//"string"//numberletn=newNumber(123);n。valueOf();//123typeofn.valueOf();//"number"//Booleanletb=newBool??ean(true);b.valueOf();//truetypeofb.valueOf();//"boolean""异构"(注意)而BigInt和Symbol是不支持new运算符的“不完整类”。例如BigInt的声明(摘录):interfaceBigIntConstructor{(value?:any):bigint;readonlyprototype:BigInt;}declarevarBigInt:BigIntConstructor;可以看到BigInt的声明中并没有新增运算符相关的函数。普通函数(Function)包装器对象也可以作为普通函数使用。其中,String()、Number()和Boolean()函数可用于对任何类型的数据进行显式类型转换。另外,Object()函数也可以用于显式类型转换,本文不做展开。字符串示例代码:typeofString();//"string"String();//""String('pp');//"pp"String(123);//"123"String(true);//"true"String(false);//"false"String(null);//"null"String(undefined);//"undefined"String([]);//""String({});//[objectObject]"技巧1当我们使用String()函数转换一个对象时,JavaScript会先访问对象上的toString()函数。如果没有实现,它会向上搜索原型链。举个栗子:执行String({toString(){return'pp';}})返回的结果是“pp”,而不是“[objectObject]”。所以不能用String()函数来判断一个值是否是对象(会推翻)。Tip2常用的判断对象的方式是Object.prototype.toString({})==='[objectObject]'。例如:执行Object.prototype.toString({toString(){return'pp';}})返回“[objectObject]”。数字示例代码:typeofNumber();//"number"Number();//0Number('');//0Number('pp');//NaNNumber(123);//123Number(true);//1Number(false);//0Number(null);//0Number(undefined);//NaNNumber([]);//0Number({});//NaN提示对于Number()函数,有可能最实用的转换是将true和false转换为1和0。布尔示例代码:typeofBoolean();//"boolean"Boolean();//falseBoolean('');//falseBoolean('pp');//trueBoolean(0);//falseBoolean(1);//trueBoolean(null);//falseBoolean(undefined);//falseBoolean([]);//trueBoolean({});//true提示在某些情况下,我们会用数据中的0和1来表示True或false状态,此时可以使用Boolean()来判断状态。BigIntBigInt()函数用于将整数转换为大整数。该函数接受一个整数作为参数。如果传入的参数是浮点数或任何非数值数据,都会报错。示例代码:BigInt(123);//123nBigInt(123n);//123ntypeof123n;//"bigint"typeofBigInt(123);//"bigint"BigInt&Number需要注意的是BigInt和Number并不严格相等(大致相等)。示例代码:123n===123;//false123n==123;//trueSymbolSymbol()函数用于创建符号类型值。这个函数接受一个字符串作为描述符(参数),如果传入其他类型的值,会被转为字符串(undefined除外)。请注意,每个符号值都是唯一的,即使它们的描述符相同。而交易品种类型数据只能通过Symbol()函数创建。示例代码://以下返回值是Devtools模拟出来的,不是实际值Symbol('pp');//Symbol(pp)Symbol(123);//Symbol(123)Symbol(null);//Symbol(null)Symbol({});//Symbol([objectObject])//类型typeofSymbol('pp');//"symbol"Symbol('pp')===Symbol('pp');//false//DescriptorSymbol('pp').description;//"pp"Symbol(123).description;//"123"Symbol({}).description;//[objectObject]"Symbol().description;//undefinedSymbol(undefined).description;//undefined原始值不是对象(PrimitivenotObject)有趣的是,没有属性和方法(Noproperties,nofunctions)正如本文前面提到的:“原始值是一个没有任何方法的非对象数据。”我们都知道对象(Object)可以有属性和方法。但是字符串不是对象,所以你不能给字符串添加属性。做个小实验:leta='chenpipi';console.log(a.length);//8//尝试增加一个新的属性a.name='吴彦祖';console.log(a.name);//undefined//尝试修改已有属性typeofa.slice;//"function"a.slice=null;typeofa.slice;//"function"渣男小剧场这时候有个天才小伙伴使用了反驳技能。渣男,别在这里忽悠我。我通常写错误。当我不写代码时,我可以调用字符串、数字和布尔值的方法!例如下面的代码可以正常执行并得到预期的结果://"PiPi"//numberletn=123;n.toString();//"123"(123.45).toFixed(2);//"123.5"//布尔值letb=true;b.toString();//"true"false.toString();//"false"没用你有没有发现不能在字面值后面直接调用函数的号码?比如执行123.toString(),就会报SyntaxError(语法错误)。这是因为数字(浮点数)本身使用了小数点.,调用函数也需要使用小数点。这时候就有歧义了(字符串和布尔值没有这种麻烦)。对于这种情况,我们可以用括号()把数字包起来,比如(123).toString();或者用两个连续的小数点..来调用函数,比如123..toString()。奇怪,那么既然字符串不是对象,为什么字符串会有属性和方法呢?想了想,数字就是数字,数字上怎么会有方法呢?这确实不合逻辑,但却很符合现实的矛盾。这是怎么回事???替身使者的答案(这个我不会翻译)揭晓~秘密操作以字符串为例。当我们在代码中读取字符串的属性或方法时,JavaScript会默默地执行以下操作:将字符串通过newString()传递,创建一个临时的包装对象实例;通过创建的对象执行我们的代码逻辑(读取属性或执行函数);临时对象不再使用,可以销毁。比如下面的栗子:leta='chenpipi';console.log(a);//"chenpipi"//------------------------------letb1=a.length;console.log(b1);//8//以上代码等价于:letb2=(newString(a)).length;console.log(b2);//8//----------------------------letc1=a.toUpperCase();console.log(c1);//"CHENPIPI"//上面的代码等同于:letc2=(newString(a)).toUpperCase();console.log(c2);//"CHENPIPI"的数字(number)和布尔值(boolean)是相同,但是数字临时对象是用newNumber()创建的,而布尔值是用newBoolean()创建的。除了上面的例子,最有力的证明就是它们的构造函数:'chenpipi'.constructor===String;//true(12345).constructor===Number;//truetrue.constructor===Boolean;//true这一切都是JavaScript偷偷做的,过程中产生的临时对象都是一次性的(用完就扔掉)。原来芜湖如此,如此有道理!这也可以解释为什么我们可以访问字符串上的属性和方法,但不能添加或修改属性。那是因为我们实际操作的对象其实是JavaScript创建的临时对象,而不是字符串本身!所以我们的添加或修改操作实际上是生效的,但只是在临时对象上生效!像这样://代码中:leta='chenpipi';a.name='吴彦祖';console.log(a.name);//undefined//等价于:leta='chenpipi';(newString(a)).name='吴彦祖';console.log(a.name);//undefined//等同于:leta='chenpipi';lettemp=newString(a);temp.name='吴彦祖';console.log(a.name);//undefinedSummary(摘要)以上就是本文的全部内容。最后总结一下:大多数原始类型都有对应的包装对象;有些包装器对象可以是新的,有些则不能;包装器对象通常用于显式类型转换;对象有属性和方法;原始值没有属性和方法;原始值不能有属性和方法;但是我们可以像操作对象一样操作原始值;这是因为JavaScript在执行代码时偷偷搞了些小把戏;JavaScript会用临时包装对象替换原始值来执行一个动作。我们在写代码的时候通常不会注意到这一点,实际上它并不影响我们写代码。那么,这篇文章不就是在浪费时间吗?是的,还不止这些~知己知彼,百战不殆。学习了以上无用的小知识,也算是对JavaScript有了更深的了解,至少可以用来吹牛了(手动狗头~)。相关信息《JavaScript 高级程序设计(第4版)》《JavaScript 权威指南(第6版)》Primitive-MDN:https://developer.mozilla.org/en-US/docs/Glossary/Primitive“typeofnull”的历史:https://2ality.com/2013/10/typeof-null.html
