为什么我不能在 javascript 中向字符串对象添加属性?

IT技术 javascript
2021-02-04 22:58:18

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

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

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

2个回答

JavaScript 中有 8 种语言类型:

  • 7 种基本类型:UndefinedNullBooleanNumberBigIntStringSymbol
  • 1 非原始类型:对象

原始类型的值称为原始值,它们不能具有属性。
所述的值对象非原始型被称为对象的它们可具有的特性。

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

foo.bar = 'abc';

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

(a)如果 的值为UndefinedNullfoo类型,则会抛出错误,

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

(c)如果 的值foo是任何其他类型,则 aTypeError将在严格模式下抛出:“不能分配给属性"bar"on foo: not an object”在松散模式下,上述赋值操作将是一个no op在任何一种情况下,变量foo都不会以任何方式改变。

因此,如您所见,只有当这些变量是对象时,才能为变量分配属性。如果不是这种情况,则赋值要么什么也不做,要么抛出错误。


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

test.test = "test inner";

什么都不做。


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

例如:

var str = '';
str.prop;

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

所以,如果这是定义的:

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

那么这个

str.prop;

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

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

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

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

如果使用 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"
它确实有一个缺点。下面返回false: test === 'test'就是它这样做的原因
2021-04-02 22:58:18
要添加到 dgo.a 的注释中,“typeof”也将是“object”而不是“string”。
2021-04-10 22:58:18