【问题标题】:Cannot assign to read only property 'name' of object '[object Object]'无法分配给对象“[对象对象]”的只读属性“名称”
【发布时间】:2017-11-01 11:00:44
【问题描述】:

以下代码只会对name 属性抛出错误。 可以通过在 Object.create 参数中将 name 属性指定为可写来修复它,但 我试图理解为什么会发生这种情况(也许有更优雅的方法来修复它)。

var BaseClass = function (data) {
  Object.assign(this, data);
}

var ExtendedClass = function () {
  BaseClass.apply(this, arguments);
}

ExtendedClass.prototype = Object.create(BaseClass);

console.log(new ExtendedClass({ type: 'foo' }));
new ExtendedClass({ name: 'foo' });

【问题讨论】:

  • 因为所有函数都有一个返回函数名称的原生 name 属性。但是,与 JavaScript 一样,继承的属性可以通过创建新属性来覆盖。
  • @ScottMarcus,感谢您的回答,很明显,我没有想到这一点很遗憾。有没有更强大的方法来绕过类继承中的错误?

标签: javascript oop


【解决方案1】:

name 是您尝试将其设置到的 Function 对象的保留属性。您无法设置它。

name 属性的文档位于 MDN

【讨论】:

  • 但它与ES6 classes 配合得很好,有没有办法让构造函数像ES6 中的类构造函数一样工作?
【解决方案2】:

您不能修改函数的name 属性。描述符说它不是writable...

var BaseClass = function (data) {
  Object.assign(this, data);
};

console.log(Object.getOwnPropertyDescriptor(BaseClass, 'name'));

但既然是configurable,你可以使用Object.defineProperty()

var BaseClass = function (data) {
  Object.assign(this, data);
};

Object.defineProperty(BaseClass, 'name', {
  writable: true,
  value: 'Foo'
});

console.log(BaseClass.name);

编辑

我回来了!所以...正如我之前在 cmets 中所说,我想我已经确定了您的问题。我回答的有点太快了,没看出你的ES5继承有错。

ExtendedClass.prototype = Object.create(BaseClass); 不是你想做的。这样做意味着ExtendedClass 的原型变成了一个构造函数。这显然会产生意想不到的行为。

function BaseClass(data) {
  console.log(this instanceof BaseClass); // "this" is not an instance of "BaseClass"
  console.log(this instanceof Function); // "this" is a function
  console.log(this.name); // "this" is "BaseClass"
  
  Object.assign(this, data);
}

function ExtendedClass() {
  BaseClass.apply(this, arguments);
}
ExtendedClass.prototype = Object.create(BaseClass);

new ExtendedClass({ type: 'foo' });

在您的代码中,this 是一个函数,指的是BaseClass。这就是为什么你不能修改它的名字...

实际上,在 JavaScript 中使用继承时,通常需要这两行代码:

ExtendedClass.prototype = Object.create(BaseClass.prototype);
ExtendedClass.prototype.constructor = ExtendedClass;

这是一个有效的实现:

function BaseClass(data) {
  console.log(this instanceof BaseClass); // "this" is an instance of "BaseClass"
  console.log(this instanceof Function); // "this" is not a function
  console.log(this.name); // "this" has no name yet
  
  Object.assign(this, data);
}

function ExtendedClass() {
  BaseClass.apply(this, arguments);
}
ExtendedClass.prototype = Object.create(BaseClass.prototype);
ExtendedClass.prototype.constructor = ExtendedClass;

var instance = new ExtendedClass({ name: 'foo' });

console.log(instance.name); // foo
console.log(BaseClass.name); // BaseClass
console.log(ExtendedClass.name); // ExtendedClass

【讨论】:

  • 感谢您的详细回答,我实际上已经在我的问题中提到了可写属性修改。但除此之外,如果有的话,我正试图找到一种更优雅的方式来处理它。
  • 最好的解决方案可能是避免使用“name”关键字,就像避免使用保留字如vartypeofarguments... ;)
  • 你是对的,但是name 是类的非常通用的属性,就我而言,我正在为包含name 属性的后端响应创建一个类,因此,更改它会给其他开发者造成混乱。
  • 我想我刚刚正确理解了你的问题。我可能会修改我的答案,给你更多的见解...... :)
  • 哇!非常感谢你的支持,伙计。我很感激。事实上,Stack Overflow 并不总是公平的……你通常会通过快速简短的答案获得更多的声誉,但我通常更喜欢在问题有趣时放慢速度并发布长答案。老实说,就声誉点而言,这不是一项有利可图的业务。我发布了一些深入的答案,但一无所获。但至少,如果它可以在未来帮助某人,那就太好了。快乐的 JavaScript ! :)
【解决方案3】:

如果您在 Angular+TypeScript 中遇到此错误:

错误/无效:

@Outputwhatever_var = new EventEmitter();

好/正确:

@Output()whatever_var = new EventEmitter();

【讨论】:

  • 你救了我一天的兄弟!谢谢
【解决方案4】:

如果您在 Angular+Typescript+NgRX 中遇到此错误:

您可以使用扩展运算符获取只读对象的浅表副本以使其可读,但是根据您的情况,您可能不希望这样做。

let x = [...y];

如果您使用的是 Redux / NgRX,您的选择器可能会返回一个带有对 store 的引用的只读对象,这可能会在尝试通过模板绑定更改该对象属性时引发异常。根据您的情况,您可以进行深层复制以删除商店引用。

let x = JSON.parse(JSON.stringify(y));

【讨论】:

  • 我在 angular+ngrx 错误中收到此错误,但我不明白为什么我为每个对象都收到此错误,即使对于不在状态或减速器中的对象也是如此。
【解决方案5】:

使用 ES7+ 或 TypeScript 扩展运算符功能来克服这个问题

obj = { ...obj, name: { first: 'hey', last: 'there'} }

【讨论】:

    【解决方案6】:

    我在 Angular 中遇到了这个问题,同时从 ActivatedRoute 的 queryParams 设置了一个局部变量,并尝试有条件地覆盖或合并...事先复制就可以了:

    updateQp(qp = {}, force = false) { 
        let qpLoc = Object.assign({}, this.queryParamsLocal)
        this.queryParamsLocal = force ? qp : Object.assign(qpLoc, qp)
    }
    

    【讨论】:

      【解决方案7】:

      在我的例子中,我试图在 redux 的一个数组中交换 2 个元素。这是我如何在避免只读问题的同时更改 redux 状态的简单方法:

      let newData = []
      let data = store.getState().events.value
      
      for(let i = 0; i < data.length; i++) {
        if(i === fromIndex) {
          newData.push(data[toIndex])
        }else if(i === toIndex) {
          newData.push(data[fromIndex])
        }else {
          newData.push(data[i])
        }
      }
      this.setEventsData(newData)
      

      上面的代码创建了一个新的空数组。然后它遍历只读数组并将每个项目推入新数组。这有效地创建了一个新数组,其中只读数组的项目以不同的顺序定位。如果您在编辑值时遇到问题,可以更改上述代码并使用更改后的值创建一个新对象,然后将其插入您要进行更改的位置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-30
        • 2018-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-12
        • 2019-04-27
        • 2017-12-18
        相关资源
        最近更新 更多