JavaScript对象和基元类型的区别是什么?

时间:2022-06-21 13:23:24

Stoyan Stefanov in his excellent book 'Object-Oriented JavaScript' says:

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

Any value that doesn't belong to one of the five primitive types listed above is an object.

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

With five primitive types he means Number, String, Boolean, Undefined and Null. However in Google Chrome console it seems like number is not primitive type at all (compared to C primitive types like int). It looks like the primitive number has methods:

在五种基本类型中,他表示数字、字符串、布尔值、未定义和空值。然而,在谷歌Chrome控制台,数字似乎根本不是原始类型(与C原始类型(如int)相比)。看起来原始数有方法:

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

Thus I assumed that I can work with number as with an object, so I tried to assign a property to it:

因此我假设我可以像处理对象一样处理数字,所以我试图给它分配一个属性:

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

I don't understand that behavior. If number has a method, it should behave like object, shouldn't it? It even has a prototype:

我不理解那种行为。如果number有一个方法,它应该像对象一样,不是吗?它甚至有一个原型:

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

So what is the magic behind this? How JavaScript treats objects versus primitive types? I would rather not use the word primitive and substitute it with simple objects. As I see it those are objects which can't be extended with new properties, however they are constructed through their constructor and also have prototype which can be extended like with normal objects.

那么这背后的魔力是什么呢?JavaScript如何处理对象和原始类型?我宁愿不使用“原语”这个词,而是用简单的对象来代替它。正如我所看到的,这些都是不能用新属性扩展的对象,但是它们是通过构造函数构造的,并且具有可以像普通对象一样扩展的原型。

1 个解决方案

#1


10  

[...] It looks like the primitive number has methods

[…看起来原始数字有方法

The primitive, does not actually has its own properties. It gets coerced to an object in order to be able to access "its" properties. The coerced object is not accessable outside the called Method *(In strict mode even not inside the method)*. As such, the referenced variable is always a primitive.

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

Consider this simple example:

考虑一下这个简单的例子:

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

side note: In ES5s strict mode,this would be a primitive and the type would be number

注:在ES5s严格模式下,这是一个原语,类型为number

Since the variable num is a primitive, you can not assign values to it.

由于变量num是一个原语,所以不能为它赋值。

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

If you instead create a number (or string) via its object constructor, its type indeed is an object. A quick check by adding a property shows it can actually be assigned.

如果您相反地通过它的对象构造函数创建一个数字(或字符串),那么它的类型实际上就是一个对象。通过添加属性进行快速检查,可以实际分配属性。

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

So what is the magic behind this? How JavaScript treats objects versus primitive types?

那么这背后的魔力是什么呢?JavaScript如何对待对象与原始类型?

This process is described in ES5 §8.7.1 GetValue

这个过程中描述ES5§8.7.1 GetValue

For an object:

一个对象:

  • If Type(V) is not Reference, return V.
  • 如果类型(V)不是引用,返回V。
  • Let base be the result of calling GetBase(V).
  • 让base是调用GetBase(V)的结果。
  • If IsUnresolvableReference(V), throw a ReferenceError exception.
  • 如果IsUnresolvableReference(V),抛出一个ReferenceError异常。
  • If IsPropertyReference(V), then
    • If HasPrimitiveBase(V) is false, then let get be the [[Get]] internal method of base, otherwise let get be the special [[Get]] internal method defined below.
    • 如果HasPrimitiveBase(V)为false,则设get为base的[[get]]内部方法,否则设get为下面定义的特殊[[[[get]]内部方法。
    • Return the result of calling the get internal method using base as its this value, and passing GetReferencedName(V) for the argument.
    • 返回使用base作为该值调用get内部方法的结果,并为参数传递GetReferencedName(V)。
  • 如果IsPropertyReference(V),则如果HasPrimitiveBase(V)为false,则设get为base的[[[get]]内部方法,否则设get为下面定义的特殊[[[get]]内部方法。返回使用base作为该值调用get内部方法的结果,并为参数传递GetReferencedName(V)。
  • Else, base must be an environment record.
    • Return the result of calling the GetBindingValue (see 10.2.1) concrete method of base passing GetReferencedName(V) and IsStrictReference(V) as arguments.
    • 返回调用GetBindingValue(参见10.2.1)的结果,该方法通过GetReferencedName(V)和IsStrictReference(V)作为参数。
  • 否则,base必须是一个环境记录。返回调用GetBindingValue(参见10.2.1)的结果,该方法通过GetReferencedName(V)和IsStrictReference(V)作为参数。

For a primitive:

一种原始的:

The following [[Get]] internal method is used by GetValue when V is a property reference[1] with a primitive base value. It is called using base as its this value and with property P as its argument. The following steps are taken:

当V是具有基本值的属性引用[1]时,GetValue使用下面的[[[Get]]]内部方法。用base作为它的这个值,用属性P作为它的参数。现采取下列步骤:

  • Let O be ToObject(base).
  • 让O ToObject(基地)。
  • Let desc be the result of calling the [[GetProperty]] internal method of O with property name P.
  • 让desc是调用O的内部方法[[[GetProperty]]的结果,该方法的属性名为P。
  • If desc is undefined, return undefined.
  • 如果desc未定义,则返回未定义。
  • If IsDataDescriptor(desc) is true, return desc.[[Value]].
  • 如果isdatadescript (desc)为真,返回desc.[值]]。
  • Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]].
  • 否则,IsAccessorDescriptor(desc)必须为真,因此,让getter为desc.[[Get]]。
  • If getter is undefined, return undefined.
  • 如果getter是未定义的,则返回未定义的。
  • Return the result calling the [[Call]] internal method of getter providing base as the this value and providing no arguments.
  • 返回调用[[[Call]]] getter的内部方法的结果,该方法将基作为该值,不提供任何参数。

NOTE The object that may be created in step 1 is not accessible outside of the above method. An implementation might choose to avoid the actual creation of the object. The only situation where such an actual property access that uses this internal method can have visible effect is when it invokes an accessor function.

注意,可以在步骤1中创建的对象在上述方法之外是不可访问的。实现可以选择避免对象的实际创建。只有使用此内部方法的实际属性访问才能具有可见效果时,才会调用访问器函数。

[1] IsPropertyReference(V). Returns true if either the base value is an object or HasPrimitiveBase(V) is true; otherwise returns false.

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

#1


10  

[...] It looks like the primitive number has methods

[…看起来原始数字有方法

The primitive, does not actually has its own properties. It gets coerced to an object in order to be able to access "its" properties. The coerced object is not accessable outside the called Method *(In strict mode even not inside the method)*. As such, the referenced variable is always a primitive.

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

Consider this simple example:

考虑一下这个简单的例子:

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

side note: In ES5s strict mode,this would be a primitive and the type would be number

注:在ES5s严格模式下,这是一个原语,类型为number

Since the variable num is a primitive, you can not assign values to it.

由于变量num是一个原语,所以不能为它赋值。

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

If you instead create a number (or string) via its object constructor, its type indeed is an object. A quick check by adding a property shows it can actually be assigned.

如果您相反地通过它的对象构造函数创建一个数字(或字符串),那么它的类型实际上就是一个对象。通过添加属性进行快速检查,可以实际分配属性。

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

So what is the magic behind this? How JavaScript treats objects versus primitive types?

那么这背后的魔力是什么呢?JavaScript如何对待对象与原始类型?

This process is described in ES5 §8.7.1 GetValue

这个过程中描述ES5§8.7.1 GetValue

For an object:

一个对象:

  • If Type(V) is not Reference, return V.
  • 如果类型(V)不是引用,返回V。
  • Let base be the result of calling GetBase(V).
  • 让base是调用GetBase(V)的结果。
  • If IsUnresolvableReference(V), throw a ReferenceError exception.
  • 如果IsUnresolvableReference(V),抛出一个ReferenceError异常。
  • If IsPropertyReference(V), then
    • If HasPrimitiveBase(V) is false, then let get be the [[Get]] internal method of base, otherwise let get be the special [[Get]] internal method defined below.
    • 如果HasPrimitiveBase(V)为false,则设get为base的[[get]]内部方法,否则设get为下面定义的特殊[[[[get]]内部方法。
    • Return the result of calling the get internal method using base as its this value, and passing GetReferencedName(V) for the argument.
    • 返回使用base作为该值调用get内部方法的结果,并为参数传递GetReferencedName(V)。
  • 如果IsPropertyReference(V),则如果HasPrimitiveBase(V)为false,则设get为base的[[[get]]内部方法,否则设get为下面定义的特殊[[[get]]内部方法。返回使用base作为该值调用get内部方法的结果,并为参数传递GetReferencedName(V)。
  • Else, base must be an environment record.
    • Return the result of calling the GetBindingValue (see 10.2.1) concrete method of base passing GetReferencedName(V) and IsStrictReference(V) as arguments.
    • 返回调用GetBindingValue(参见10.2.1)的结果,该方法通过GetReferencedName(V)和IsStrictReference(V)作为参数。
  • 否则,base必须是一个环境记录。返回调用GetBindingValue(参见10.2.1)的结果,该方法通过GetReferencedName(V)和IsStrictReference(V)作为参数。

For a primitive:

一种原始的:

The following [[Get]] internal method is used by GetValue when V is a property reference[1] with a primitive base value. It is called using base as its this value and with property P as its argument. The following steps are taken:

当V是具有基本值的属性引用[1]时,GetValue使用下面的[[[Get]]]内部方法。用base作为它的这个值,用属性P作为它的参数。现采取下列步骤:

  • Let O be ToObject(base).
  • 让O ToObject(基地)。
  • Let desc be the result of calling the [[GetProperty]] internal method of O with property name P.
  • 让desc是调用O的内部方法[[[GetProperty]]的结果,该方法的属性名为P。
  • If desc is undefined, return undefined.
  • 如果desc未定义,则返回未定义。
  • If IsDataDescriptor(desc) is true, return desc.[[Value]].
  • 如果isdatadescript (desc)为真,返回desc.[值]]。
  • Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]].
  • 否则,IsAccessorDescriptor(desc)必须为真,因此,让getter为desc.[[Get]]。
  • If getter is undefined, return undefined.
  • 如果getter是未定义的,则返回未定义的。
  • Return the result calling the [[Call]] internal method of getter providing base as the this value and providing no arguments.
  • 返回调用[[[Call]]] getter的内部方法的结果,该方法将基作为该值,不提供任何参数。

NOTE The object that may be created in step 1 is not accessible outside of the above method. An implementation might choose to avoid the actual creation of the object. The only situation where such an actual property access that uses this internal method can have visible effect is when it invokes an accessor function.

注意,可以在步骤1中创建的对象在上述方法之外是不可访问的。实现可以选择避免对象的实际创建。只有使用此内部方法的实际属性访问才能具有可见效果时,才会调用访问器函数。

[1] IsPropertyReference(V). Returns true if either the base value is an object or HasPrimitiveBase(V) is true; otherwise returns false.

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