【问题标题】:Why are there two kinds of JavaScript strings?为什么有两种 JavaScript 字符串?
【发布时间】:2011-04-01 13:54:36
【问题描述】:

这个只是狠狠地刺了我一刀。我不知道是不是所有浏览器都是这样(我没有任何其他能胜任的浏览器可以测试),但至少 Firefox 有两种字符串对象。

打开 Firebugs 控制台并尝试以下操作:

>>> "a"
"a"
>>> new String("a")
String { 0="a"}

您可以直观地观察到,Firefox 对待 new String("a")"a" 的方式不同。但是,否则,两种字符串的行为似乎相同。例如,有证据表明两者都使用相同的原型对象:

>>> String.prototype.log = function() { console.log("Logged string: " + this); }
function()
>>> "hello world".log()
Logged string: hello world
>>> new String("hello world").log()
Logged string: hello world

显然,两者是相同的。也就是说,直到您询问类型。

>>> typeof("a")
"string"
>>> typeof(new String("a"))
"object"

我们还可以注意到,当this 是一个字符串时,它总是对象形式:

>>> var identity = function() { return this }
>>> identity.call("a")
String { 0="a"}
>>> identity.call(new String("a"))
String { 0="a"}

再进一步,我们可以看到非对象字符串表示不支持任何附加属性,但对象字符串支持:

>>> var a = "a"
>>> var b = new String("b")
>>> a.bar = 4
4
>>> b.bar = 4
4
>>> a.bar
undefined
>>> b.bar
4

另外,有趣的事实!您可以使用toString() 函数将字符串对象转换为非对象字符串:

>>> new String("foo").toString()
"foo"

从未想过致电String.toString() 会有用!无论如何。

所以所有这些实验都引出了一个问题:为什么 JavaScript 中有两种字符串?


评论表明,每个原始 JavaScript 类型(包括数字和布尔值)也是如此。

【问题讨论】:

  • 数字也有两种……
  • 还有两种布尔值true === new Boolean(true); // false
  • 假设 JavaScript 是一种二进制类型的语言...
  • 换句话说,每个原语都有一个包装类型(我觉得完全没用)

标签: javascript string


【解决方案1】:

Javascript 中有两种类型的字符串——文字字符串和字符串对象。他们的行为确实有点不同。两者的主要区别在于您可以向 String 对象添加其他方法和属性。例如:

var strObj = new String("object mode");
strObj.string_mode = "object"
strObj.get_string_mode = function() { return this.string_mode; }

// this converts it from an Object to a primitive string:
str = strObj.toString();

字符串文字只是临时转换为 String 对象以执行任何核心方法。

同样的概念也适用于其他数据类型。 Here's more on primitive data types and objects.

编辑

如 cmets 中所述,字符串文字不是原始字符串,而是“类型为内置原始 [string] 值的文字常量”,引用 this source

【讨论】:

  • 它是字符串 primitives 而不是文字。请参阅我对 Raynos 的回答的评论。
  • 术语 literalprimitive 似乎使用松散,无论这是否符合规定。 Wikipedia 以相同的想法使用这两个术语:“JavaScript 提供了一种布尔数据类型,具有真假字面量。typeof 运算符为这些原语返回字符串 'boolean'。”
  • @Kelly:虽然它们是不同的东西。在字符串的情况下,文字产生一个原始字符串,但字符串原始也可以通过在任何原生 JavaScript 对象上调用 toString() 来产生。
  • @TimDown 它是字符串 values 而不是原语。请参阅 ES5 规范。
  • @Raynos:primitive 是一个非常可接受的术语,并且在整个 ECMAScript 5 规范中应用于字符串、数字和布尔值的集合。还有ToPrimitive 内部方法和[[PrimitiveValue]] 属性,前者返回一个原始值,可以是任何原始类型。因此,字符串值或字符串原语都应该是可接受的术语。
【解决方案2】:

您将字符串值与字符串对象进行比较。

"a" 是一个字符串值。

"a" === "a"; // true

new String("a") 是一个字符串对象。

new String("a") === new String("a"); // false

有两个字符串。 "a" 只是获取字符串值“a”,而 new String("a") 创建一个新的字符串对象,该对象内部具有字符串值“a”

【讨论】:

  • 这是否意味着new String("foo" + "bar").toString() 创建了一个全局的、静态的"foobar" 字符串?
  • 创建可能是错误的词。但它返回一个字符串文字。
  • 有点不准确:区别在于字符串原语和字符串对象。字符串文字确实会产生字符串原语,但不是唯一的方法。任何 JavaScript 对象上的 toString() 也会产生一个字符串原语。
  • 同意,我会修正术语。 “文字”定义了在不使用构造函数的情况下创建某些东西所必需的语法。文字可以创建一个基元或一个对象(请参阅对象/数组文字),正如@Tim Down 所说,可以从对象创建基元。
  • @TimDown @AndyE 更改了措辞以匹配 ES5 规范。
【解决方案3】:

要记住的另一件重要事情如下:

typeof "my-string" // "string"
typeof String('my-string') // 'string'
typeof new String("my-string") // "object". 

因此,在测试参数或变量是否为字符串时,您至少需要执行以下操作。

function isString(arg) {
  if (!arg) {
    return false;
  }
  return typeof arg == "string" || arg.constructor == String;
}

如果您从另一个框架/iframe 传入一个字符串对象,上述函数仍然会失败。那就是:

frames[0].myStringVar.constructor != frames[1].myStringVar.constructor

这是因为每个窗口上下文的构造函数String 是不同的。因此,一个万无一失的 isString 方法将是

function isString(obj) {
   return Object.prototype.toString.call(obj) == "[object String]";
}

【讨论】:

  • 你最新的isString函数不是toString函数吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-26
  • 1970-01-01
  • 2017-01-31
相关资源
最近更新 更多