原语和对象

Primitives and Objects

本文关键字:对象 原语      更新时间:2023-09-26

David Flanagan的《权威JavaScript》对对象和原语进行了区分。

他将原语定义为Number、String、Boolean、Null和Undefined,与标准一样。

然而,将原语定义为对象的子集是否更准确,即称它们为原始对象(primitive Objects) ?

因为它们有自己的方法,而且是复杂的实体。

实际问题

当定义String, Boolean和Number时,Primitive Object会比Object更准确吗?

对象和原语是不同的:

typeof 42 === "number"             
typeof new Number(42) === "object"
new Number(42) !== 42

然而,必要时,原语会被临时对象自动包装,这些临时对象可以自动转换回原语:

(42).toString() === "42"
new Number(42) == 42
new Number(42) + 1 === 43

特别是在Java和c#编程语言的上下文中,这种行为称为自动装箱。包装器对象有一些令人困惑的特征,例如:

Boolean(new Boolean(false)) === true

最好避免故意将它们存储在变量中,而是尽可能使用原语。

这不是语义的问题,看:

var threePrimitive = 3;
var threeObject = new Number(3);
threePrimitive.toFixed(2); // 3.00
threeObject.toFixed(2); // 3.00
threePrimitive.foo = true
threeObject.foo = true;
threePrimitive.foo; // undefined
threeObject.foo; // true

当你尝试在原语上调用方法时,原语被包装在对象中,但在初始使用后,该对象将被丢弃。


至于规范中是如何表述的,我不是100%确定,但以下是我的想法(基于Bergi在他的一个答案中留下的提示)。第11.2.1节指出,访问器属性的计算方法如下:

  1. 让baserreference作为MemberExpression的求值结果
  2. 设basvalue为GetValue ( baserreference )。

(…)

然后在8.7.1中我们看到如下:

V是a时,GetValue使用下面的[[Get]]内部方法具有基本值的属性引用。这叫做使用以属性P为参数,以base为this值。的执行以下步骤:

  1. 设0为ToObject(base)。
  2. 让desc为调用的结果[[GetProperty]]带有属性名p的O的内部方法
  3. 如果desc是未定义,返回未定义。
  4. 如果IsDataDescriptor(desc)为true,返回desc。[[价值]]。
  5. 否则,IsAccessorDescriptor(desc)必须为true,因此,
  6. 让getter成为desc.[[Get]]。如果getter未定义,则返回undefined。
  7. 返回调用getter内部方法[[Call]]的结果提供base作为this值,并且不提供参数。

注意对象之外无法访问可能在步骤1中创建的以上方法。实现可能会选择避免实际的对象的创建。唯一的情况是这样一个实际使用这个内部方法的属性访问可以产生可见的效果是调用访问器函数时。

在定义时,原始对象是否比对象更准确字符串,布尔值和数字?

请注意,我并不是说这里的数字不是对象,我是指出它看起来有歧义。这是让JavaScript新手感到困惑的事情。

这种区别主要是学术上的,但在一种情况下它似乎是模棱两可的:字面量表示原始对象,除非字面量似乎表示数字。不能将方法直接应用于文字整数*符号:

1.toString();
SyntaxError: identifier starts immediately after numeric literal

…,但你可以应用number的方法:

Number(1).toString();
'1'

…和包含数字的名称是number:

x = 4;
x.toString();
'4'

我认为这实际上是一个解析问题,但我真的不知道为什么解析器不能告诉1是一个数字,因为它可以告诉"abc"是一个字符串。我想这与.符号的语义歧义有关。(是小数点还是方法操作符?)

*JavaScript实际上没有整数。我指的是一个完全由[0-9]+组成的符号