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

Sha*_*awn 61 javascript

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

<html>
<script>
var test = "test";
test.test = "test inner";
console.log(test);
console.log(test.test);
</script>
Run Code Online (Sandbox Code Playgroud)

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

Šim*_*das 82

JavaScript中有6种语言类型:

  • 5种原始类型:String,Number,Boolean,Null,Undefined
  • 1非基本类型:对象

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

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

foo.bar = 'abc';
Run Code Online (Sandbox Code Playgroud)

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

(a)中,如果值foo是类型的未定义,则一个错误将被抛出,

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

(c)如果值为Number,StringBooleanfoo类型,则该变量不会以任何方式更改.在这种情况下,上述赋值操作将是noop.foo

因此,正如您所看到的,如果这些变量是对象,则仅将属性分配给变量才有意义.如果不是这种情况,那么赋值将根本不执行任何操作,甚至抛出错误.


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

test.test = "test inner";
Run Code Online (Sandbox Code Playgroud)

什么都不做.


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

例如:

var str = '';
str.prop;
Run Code Online (Sandbox Code Playgroud)

str是一个包含String值的变量.因此,访问该变量的属性应该是no-op(str.prop仅返回undefined).这是一个例外:如果String.prototype包含'prop'具有已定义的getter 的访问器属性,则将调用该getter.

所以,如果定义了这个:

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

那么这个

str.prop;
Run Code Online (Sandbox Code Playgroud)

将调用该getter函数.

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

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

  • "你不能为这些值分配属性"有点误导:由于自动装箱(更具体地说,是ECMA-262第5版第8.7.2节中描述的算法),将属性分配给基元是完全有效的.但是,属性将被添加到纯临时包装器对象而不是基元,因此无法获取属性(包装器对象不替换基元); 因此,将属性分配给基元是noop,除非赋值具有副作用(例如,如果属性是通过访问器函数实现的) (13认同)
  • @Šime:你的答案准确地描述了ECMAScript3的行为:根据11.2.1节,属性访问调用`ToObject()`,它将抛出undefined和null,为值创建包装器对象并返回对象的参数; 在ES5中,情况稍微复杂一些,因为您可以将一个setter函数添加到基元的原型对象,即在基元上设置属性可能会产生副作用...... (2认同)
  • @Mike当访问原始String值的属性(或调用方法)时,JavaScript引擎将值包装在String对象中(包装器),特别是执行new String(str)。然后从该对象中检索“ length”属性,然后丢弃该对象。 (2认同)

tre*_*int 30

如果使用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"
Run Code Online (Sandbox Code Playgroud)

  • 它确实有一个缺点.以下返回false:`test ==='test'`.这是[为什么会这样做](http://stackoverflow.com/questions/10951906/why-does-foo-new-stringfoo-evaluate-to-false-in-javascript). (8认同)
  • 要添加到 dgo.a 的注释中,“typeof”也将是“object”而不是“string”。 (2认同)