基本原则
对象是属性的聚合。属性可以引用对象或原始数据。基本类型是值,它们没有属性。
在JavaScript中有7种基本类型:undefined、null、boolean、string,number,bigint,symbol。其他一切都是一个对象。基本类型布尔、字符串和数字可以被它们的对应对象包装。这些对象分别是布尔、String和Number构造函数的实例。
typeof true; //"boolean" typeof Boolean(true); //"boolean" typeof new Boolean(true); //"object" typeof (new Boolean(true)).valueOf(); //"boolean" typeof "abc"; //"string" typeof String("abc"); //"string" typeof new String("abc"); //"object" typeof (new String("abc")).valueOf(); //"string" typeof 123; //"number" typeof Number(123); //"number" typeof new Number(123); //"object" typeof (new Number(123)).valueOf(); //"number" 复制代码
如果原始类型没有属性,为什么"abc".length
会返回一个值?
因为JavaScript很容易在原语和对象之间强制转换。在这种情况下,为了访问属性长度,字符串值被强制转换为字符串对象。字符串对象只被使用了一小会儿,之后它就被献给了垃圾收集之神。
String.prototype.returnMe = function () { return this; }; var a = "abc"; var b = a.returnMe(); a; //"abc" typeof a; //"string" (still a primitive) b; //"abc" typeof b; //"object" 复制代码
正如许多善意的科学调查一样,只要b还在,我们就会干扰事物的自然发展,防止物体被当作垃圾收集。海森堡活得很好😉
(注意,在严格模式下,难以捉摸的生物会逃走-谢谢@DmitrySoshnikov)
下面是一个更环保的例子,它在不干扰垃圾收集的情况下验证对象类型:
Number.prototype.toString = function() { return typeof this; } (123).toString(); //"object" 复制代码
通过这种方式,原始类型可以访问由各自的对象构造函数定义的所有属性(包括方法)。
这些对象也可以被强制赋值?
是的。主要是。这种类型的对象仅仅是包装器,它们的值是它们包装的原语,它们通常会根据需要强制转换为这个值。
//object coerced to primitive var Twelve = new Number(12); var fifteen = Twelve + 3; fifteen; //15 typeof fifteen; //"number" (primitive) typeof Twelve; //"object"; (still object) //another object coerced to primitive new String("hippo") + "potamus"; //"hippopotamus" //object not coerced (because 'typeof' operator can work with objects) typeof new String("hippo") + "potamus"; //"objectpotamus" 复制代码
可悲的是,布尔对象不那么容易强制。更糟糕的是,除非布尔值为空或未定义,否则布尔对象的计算结果为true。试试这个:
if (new Boolean(false)) { alert("true???"); } 复制代码
通常你必须显式地询问布尔对象的值。以下可能有助于确定值是否为" false " ....的" true "
var a = ""; new Boolean(a).valueOf(); //false 复制代码
但实际上,这样做更容易。
var a = Boolean(""); a; //false 复制代码
甚至这样
var a = ""; !!a; //false 复制代码
强制是否允许我给原始类型赋值?
如果JavaScript检测到试图将属性赋给一个原语,它确实会强制将原语赋给一个对象。但是,与前面的示例一样,这个新对象没有引用,将立即成为垃圾收集的素材。
下面是同一个示例的伪代码表示,以说明实际发生的情况
var primitive = "september"; primitive.vowels = 3; //new object created to set property (new String("september")).vowels = 3; primitive.vowels; //another new object created to retrieve property (new String("september")).vowels; //undefined 复制代码
哦,正如你所看到的,它不仅没用,而且相当浪费。
包装对象
事实证明,赋值属性的能力是对象相对于原始对象的唯一优势,但在实践中,即使这是一个值得怀疑的品质。字符串、布尔值和数字具有特定的、定义良好的目的,将它们重新定义为国家持有者很可能会使人们感到困惑。此外,由于原语是不可变的,你不能通过调整对象包装器的属性来修改它们:
var me = new String("Angus"); me.length = 2; //(error in strict mode) me.length; //5 (not 2 - thanks @joseanpg) me.valueOf(); "Angus" 复制代码
补充
var me = new String("Angus"); me.length = 2; //(error in strict mode) const d = Object.getOwnPropertyDescriptor(me, 'length'); // Object { value: 5, writable: false, enumerable: false, configurable: false } console.log(d); 复制代码
因为length
属性的writable
是false
所以长度不会被赋值而改变。
尽管如此,我确实认为,良好地理解原语以及当您与它们交互时发生了什么,是深入了解该语言的重要一步。我希望这对你有所帮助。