默海笑

V1

2022/07/12阅读:11主题:默认主题

【译】JavaScript原始数据的秘密生命 【The Secret Life of JavaScript Primitives】

原文链接: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属性的writablefalse 所以长度不会被赋值而改变。

尽管如此,我确实认为,良好地理解原语以及当您与它们交互时发生了什么,是深入了解该语言的重要一步。我希望这对你有所帮助。

分类:

前端

标签:

JavaScript

作者介绍

默海笑
V1