【问题标题】:What is the difference between JavaScript object and primitive types?JavaScript 对象和原始类型有什么区别?
【发布时间】:2014-03-22 21:22:00
【问题描述】:

Stoyan Stefanov 在他的优秀著作《面向对象的 JavaScript》中说:

任何不属于上面列出的五种基本类型之一的值都是对象。

对于五种原始类型,他的意思是NumberStringBooleanUndefinedNull。然而,在 Google Chrome 控制台中,似乎 number 根本不是原始类型(与 C 类似 int 的原始类型相比)。看起来 primitive 数字有方法:

var a = 2.2;
console.log(a.toFixed()); // logs "2"

因此我假设我可以像使用对象一样使用数字,所以我尝试为它分配一个属性:

var a = 2;
a.foo = 'bar';
console.log(a.foo); // logs undefined

我不明白这种行为。如果 number 有一个方法,它应该表现得像一个对象,不是吗?它甚至还有一个原型:

Number.prototype.foo = 'bar';
var a = 2;
console.log(a.foo); // logs 'bar'

那么这背后的魔法是什么? JavaScript 如何处理对象与 primitive 类型?我宁愿不用primitive这个词,而是用simple objects代替它。在我看来,这些对象是不能用新属性扩展的,但是它们是通过它们的构造函数构造的,并且还有可以像普通对象一样扩展的原型。

【问题讨论】:

  • 它只是未定义的,因为您没有在其上创建自己的自定义 'foo' 方法。当你这样做时(number.prototype.),没关系。这不是“魔术”只是阶级行为。 (适用于所有“类型”)所以我不确定如何回答你的问题“这背后的魔力是什么?” :L)
  • 如果有什么不清楚的地方,只需发表评论或编辑问题,我会尝试相应地更新答案:)

标签: javascript


【解决方案1】:

[...]它看起来原始数字有方法

原语实际上并没有自己的属性。它被强制到一个对象,以便能够访问“它的”属性。强制对象在被调用方法之外不可访问*(在严格模式下,即使在方法内部 not)*。因此,引用的变量始终是原语。

考虑这个简单的例子:

Number.prototype.myTypeInAMethod = function () {
   console.log (typeof this.valueOf ()) //"number" => The primitive is wrapped in an object. 
   return typeof this;
}

var num = 123;
typeof num; //number
num.myTypeInAMethod () //object

旁注:在 ES5 的严格模式下,this 是一个原始类型,类型是数字

由于变量num 是一个原语,您可以为其赋值。

num.foo = "bar";
num.foo //undefined

如果你改为通过它的对象构造函数创建一个数字(或字符串),它的类型确实是一个对象。 通过添加属性快速检查表明它实际上可以被分配。

var objNum = new Number(123);
typeof objNum ; //"object"
objNum.foo = "bar";
objNum.foo //"bar"

那么这背后的魔力是什么? JavaScript 如何处理对象和原始类型?

ES5 §8.7.1GetValue

中描述了这个过程

对于一个对象:

  • 如果Type(V) 不是引用,则返回V
  • 设 base 为调用 GetBase(V) 的结果。
  • 如果IsUnresolvableReference(V),则抛出ReferenceError异常。
  • 如果IsPropertyReference(V),那么
    • 如果HasPrimitiveBase(V)false,则令getbase的[[Get]]内部方法,否则令get 是下面定义的特殊 [[Get]] 内部方法。
    • 返回调用 get 内部方法的结果,使用 base 作为其 this 值,并为参数传递 GetReferencedName(V)
  • 否则,base 必须是环境记录。
    • 返回调用baseGetBindingValue(见10.2.1)具体方法的结果,传递GetReferencedName(V)IsStrictReference(V)作为参数。

对于原语:

当 V 是带有 原始基础值。使用 base 作为 this 值并使用属性 P 作为参数来调用它。 采取以下步骤:

  • OToObject(base)
  • desc 为调用属性名为 PO 的 [[GetProperty]] 内部方法的结果。
  • 如果 descundefined,则返回 undefined
  • 如果IsDataDescriptor(desc)true,则返回desc。[[Value]]。
  • 否则,IsAccessorDescriptor(desc) 必须为 true 所以,让 getterdesc。[[Get]]。
  • 如果 getterundefined,则返回 undefined
  • 返回调用 getter 的 [[Call]] 内部方法的结果,提供 base 作为 this 值并且不提供任何参数。

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

[1]IsPropertyReference(V)。如果基值是对象或HasPrimitiveBase(V)true,则返回true;否则返回 false

【讨论】:

  • 我们正在检查我们给它的“方法”上的“typeof”——作为一个“对象”是正确的——但“str”“类型”仍然是一个字符串。 'type' 没有被强制/改变。
  • @RobSedgwick 谢谢 :) 我试着稍微改变一下措辞,以更清楚地表明原语仍然是原语。如果您有更好的表述,欢迎您改写答案,因为英语不是我的母语。
  • 我认为你的回答很棒。我添加了该评论,因为看起来 OP 在 'number' value (它是 'number' 的类型)和具有方法的 'number' 类( object )之间感到困惑。正如您的回答所示,我们可以使用字符串值和字符串类 'new String("string")'。
  • @RobSedgwick 是的,这很好。我更新了答案以更直接地回答问题的各个部分,并以数字原语为例。感谢您的反馈:)
  • @C5H8NNaO4 感谢您的出色回答!我肯定将其标记为已接受。不过我个人认为第二部分(从 ES5 规范复制而来)可以更详细地解释给经验不足的读者,但我将把它留给你:-)。
猜你喜欢
  • 2016-09-26
  • 2016-01-02
  • 1970-01-01
  • 2012-02-06
  • 1970-01-01
  • 2017-07-16
  • 2011-11-25
  • 1970-01-01
  • 2014-02-19
相关资源
最近更新 更多