原文链接:The Secret Life of JavaScript Primitives
您可能不知道,但在JavaScript中,每当您与字符串、数字或布尔原语交互时,您就进入了一个隐藏的对象阴影和强制的世界。
基本原则
对象是属性的聚合。属性可以引用对象或原始数据。基本类型是值,它们没有属性。
在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
所以长度不会被赋值而改变。
尽管如此,我确实认为,良好地理解原语以及当您与它们交互时发生了什么,是深入了解该语言的重要一步。我希望这对你有所帮助。
本文由mdnice多平台发布
网友评论