【问题标题】:Define prototype for {...} objects only仅为 {...} 对象定义原型
【发布时间】:2016-02-10 07:11:46
【问题描述】:

是否可以仅为 {}-Objects 定义原型?

在 Javascript 中,几乎所有东西都是对象。

Object.prototype.test = function() {
    alert("test");
}

所有对象(包括数字、字符串和数组)都将具有此方法。 但我只想要一个结构对象。 > {...}

您可以先为对象创建所有原型,然后将其从其他对象(如 Number、String 和 Array)中删除。

// Overwrite
Array.prototype.test = undefined; // or delete property

这“有效”...原型被覆盖。但密钥仍然存在。

// Show all keys
for (key in arr)
{
    alert(key); // print index of array AND keynames
}

我正在寻找最优雅的方式来做到这一点。

【问题讨论】:

  • 不要修改Object 原型,除非您打算让该属性在每个对象上都可用,而您声明不希望这样做。无论如何,修改Object 原型几乎总是一个坏主意。相反,您应该将该属性放在您控制的某个类的原型上,并根据需要从该原型继承。
  • 另外,从Array 原型中删除属性不会做你想要的。在数组上调用 test 仍然会运行该函数,因为它会沿着原型链向上并在 Object 原型上简单地调用 testjsfiddle.net/x2wysox8
  • 不要从Array.prototype 中删除属性test,如果你想限制对Object.prototype.test 方法的访问,只需在调用时抛出一个错误。但是向Object.prototype 添加方法是不好的做法,在这样做之前要三思而后行
  • 确实!删除不是正确的方法。谢谢!覆盖作品。但这不是最好的方法。不幸的是,对象 {} 没有自己的名称,例如数字或字符串。数组的类似问题。数组类型是“对象”而不是“数组”。实在是太可惜了。您必须检查实例或 Array.isArray。但这是另一个话题。 :)
  • 你为什么不这样做:Object.test = function(){};?并在函数内部检查调用者的类型,然后调用函数,如:Object.test({a:"b"});

标签: javascript object key prototype


【解决方案1】:

您几乎肯定不想修改Object 原型。这个MDN article on JavaScript inheritance 总结得很好:

一个经常使用的错误功能是扩展 Object.prototype 或其他内置原型之一。

这种技术称为猴子补丁并破坏封装。虽然被 Prototype.js 等流行框架所使用,但仍然没有充分的理由将内置类型与额外的非标准功能混为一谈。

在您的示例中,从 Array 中删除 test 属性不会产生预期的效果。当Array.test被调用时,它会简单地向上搜索原型链并执行Object.test。以下代码说明了这一点:

Object.prototype.test = function () {
    alert('In test');
}

delete Array.prototype.test;

//Calls Object.test and shows the alert
[].test();

相反,您可以创建一个您控制的基类,并根据需要从它继承。请参阅下面的工作演示。

function MyBaseClass() {
  this.test = function () {
    log('In test');
  };
}

function Shape() {
  this.shapeAction = function () {
    log('Shape action');
  };
}

Shape.prototype = new MyBaseClass();

function Square() {
  this.squareAction = function () {
    log('Square action');
    this.shapeAction();
  };
}

Square.prototype = new Shape();

//Test function can be called on Square, but not on Array
var s = new Square();

s.squareAction();
s.shapeAction();
s.test();

try {
  [].test();
}
catch (e) {
  log('Exception when trying to run Array.test: ' + e);
}

//Log function for demonstration purposes
function log(s) {
  var e = document.createElement('p');
  e.innerHTML = s;
  document.body.appendChild(e);
}

【讨论】:

    【解决方案2】:

    虽然您可以更改对象原型,但强烈不推荐这样做。那是因为您将使用的所有库都希望 Object 保持不变。这可能导致某些库很难跟踪错误和意外行为。

    你需要学习如何在 JS 中做正确的面向对象。对于 js 初学者来说,这是一个非常令人困惑的主题。我建议您阅读 Nicolas Zakas 的优秀“面向对象 JavaScript 的原则”和 Kyle Simpson 的“你不懂 JS”系列丛书。

    但你可以从我的博客中关于如何在 javascript 中进行面向对象的解释开始:http://www.ideasaboutcode.com/object-orientation-in-javascript/

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      使用Array.prototype.test = undefined; 你的方法值设置为undefinied,这不会删除它。

      如果你真的想删除方法(属性),请使用delete Array.prototype.test;

      附言但在这种情况下(使用{} 对象)-> 你无法使用delete 获得你想要的。

      Array.prototype继承自Object.prototype,删除Array.prototype.test后你的代码会找到Object.prototype.test并调用它。

      我认为制作你想要的东西的最好方法是制作这样的东西:

      Object.defineProperty(Object.prototype, 'test', {
        enumerable: false,
        configurable: false,
        writable: true,
        value: function(test) { alert('In test' + test); }
      });
      
      Object.defineProperty(Array.prototype, 'test', {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function() { throw new Error('You can\'t do it!'); }
      });
      
      try {
          var a = [];
          var keys = "";
      
          for (key in a) {
              keys += key + ', '; // get all keys
          }
          // call array test method -> will throw exception
          a.test('from array');
      
      } catch (e) {
          // check keys -> test is not here
          console.log("Error: " + e.message + ' ' + "KEYS: " + keys);
      }
      
      var b = {};
      // and this will work
      b.test('from object');
      

      【讨论】:

      • 这不会阻止testArray 调用。原型链将在 Object 原型上找到 test 函数并调用:jsfiddle.net/x2wysox8
      猜你喜欢
      • 1970-01-01
      • 2012-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-05
      • 1970-01-01
      • 2020-01-08
      • 1970-01-01
      相关资源
      最近更新 更多