【问题标题】:Why can't I add properties to a string object in javascript?为什么我不能在 javascript 中向字符串对象添加属性?
【发布时间】:2011-03-05 02:05:39
【问题描述】:

我继承了其他开发人员编写的一些 javascript 代码。他不喜欢我们在整个项目中使用的网格组件,所以他决定自己编写。他写的网格不能对日期进行排序,因为它只能绑定字符串/数字。他在使用它们之前将所有日期转换为字符串。我查看了他编写的日期函​​数的字符串格式,并认为我可以将日期属性添加到具有原始值的字符串中,然后在排序时查看字符串是否具有日期属性并根据该属性进行排序。但是,您似乎无法在 javascript 中向字符串添加属性。我不知道有些类型你不能添加属性。例如:

<html>
<script>
var test = "test";
test.test = "test inner";
console.log(test);
console.log(test.test);
</script>

test.test 将是未定义的。奇怪的。我的问题是为什么这段代码不起作用?此外,如果您能想到在该网格上对日期进行排序的任何解决方法(除了实际绑定到日期对象而不是字符串,这将很难解决),那将非常有帮助。

【问题讨论】:

标签: javascript


【解决方案1】:

JavaScript 中有 8 种语言类型:

  • 7 种原始类型:UndefinedNullBooleanNumberBigInt字符串符号
  • 1 个非原始类型:对象

原始类型的值称为原始值,它们不能具有属性。
Object 非原始类型的值称为对象,它们可以具有属性。

当您尝试将名为 'bar' 的属性分配给变量 foo 时,如下所示:

foo.bar = 'abc';

那么结果将取决于foo的值的类型:

(a) 如果foo 的值是UndefinedNull 类型,则会抛出错误,

(b) 如果foo 的值属于Object 类型,那么将在对象foo 上定义命名属性'bar' (如有必要),其值将设置为'abc'

(c) 如果foo 的值是任何其他类型,则在严格模式下将抛出TypeError“无法分配给属性@987654334 @ on foo:不是对象”。在松散模式下,上述赋值操作将是一个no op。无论哪种情况,变量foo 都不会以任何方式更改。

因此,如您所见,将属性分配给变量只有在这些变量是对象时才有意义。如果不是这样,那么这个赋值要么什么都不做,甚至抛出一个错误。


在您的情况下,变量 test 包含 String 类型的值,因此:

test.test = "test inner";

什么都不做。


但是,由于 ES5 引入了访问器属性,我上面所说的有一个例外。访问器属性允许我们定义在检索或设置属性时调用的函数。

例如:

var str = '';
str.prop;

这里的str 是一个持有字符串 值的变量。因此,访问该变量的属性应该是无操作的(str.prop 仅返回undefined)。这是正确的,但有一个例外:如果 String.prototype 包含带有已定义 getter 的访问器属性 'prop',则将调用该 getter。

所以,如果这样定义:

Object.defineProperty( String.prototype, 'prop', {
    get: function () {
        // this function is the getter
    }
}); 

然后这个

str.prop;

将调用该 getter 函数。这也适用于严格模式。

现场演示: http://jsfiddle.net/fmNgu/

但是,我认为将访问器属性添加到内置原型中并不是一个好习惯。

【讨论】:

  • 'you cannot assign properties to the values'有点误导:由于自动装箱(更具体地说,ECMA-262 第 5 版第 8.7.2 节中描述的算法),它完全有效将属性分配给基元;但是,属性将被添加到纯粹的临时包装对象而不是原始对象,因此无法获取属性(包装对象不会替换原始对象);因此,将属性分配给原语是一个 noop,除非分配有副作用(例如,如果属性是通过访问器函数实现的)
  • @Christoph 是的,你是对的。我已经更新了我的答案。能否请您进行事实核查?
  • @Šime:您的回答准确地描述了 ECMAScript3 的行为:根据第 11.2.1 节,属性访问调用ToObject(),它将抛出未定义和空值,为值创建包装器对象并返回对象的论据;在 ES5 中,情况稍微复杂一些,因为您可以向基元的原型对象添加一个 setter 函数,即在基元上设置属性可能会产生副作用...
  • 可能值得指出的是,正如 Christoph 所说,toObject 被调用,因此可以在“字符串原语”上调用“字符串”方法。我认为这就是很多混乱开始的地方。
  • @Mike 当对原始 String 值访问属性(或随后调用方法)时,JavaScript 引擎将值包装在 String(包装器)对象中,特别是它执行 new String(str)。然后从该对象中检索 length 属性,然后丢弃该对象。
【解决方案2】:

如果你使用 String 对象,你可以添加属性:

var test = new String("test");
test.test = "test inner";
console.log(test.toString()); // prints out "test"
console.log(test.test); // prints out "test inner"

【讨论】:

  • 它确实有一个缺点。以下返回错误:test === 'test'。这里是why it does that
  • 要添加到 dgo.a 的注释中,“typeof”也将是“object”而不是“string”。
猜你喜欢
  • 1970-01-01
  • 2020-04-26
  • 2019-11-24
  • 2012-05-28
  • 2010-11-20
  • 2011-12-07
  • 1970-01-01
  • 2016-12-30
  • 2015-10-28
相关资源
最近更新 更多