【问题标题】:Javascript: Overwriting function's prototype - bad practice?Javascript:覆盖函数的原型 - 不好的做法?
【发布时间】:2012-09-04 09:21:20
【问题描述】:

由于当我们声明一个函数时,我们将其原型的构造函数属性指向函数本身,这样覆盖函数的原型是不是一种不好的做法:

function LolCat() {
}

// at this point LolCat.prototype.constructor === LolCat

LolCat.prototype = {
    hello: function () {
        alert('meow!');
    }
    // other method declarations go here as well
};

// But now LolCat.prototype.constructor no longer points to LolCat function itself

var cat = new LolCat();

cat.hello(); // alerts 'meow!', as expected

cat instanceof LolCat // returns true, as expected

我不是这样做的,我还是更喜欢下面的方法

LolCat.prototype.hello = function () { ... }

但我经常看到其他人这样做。

那么,为了方便起见,如第一个示例中那样,为了方便起见,通过覆盖函数的原型对象从原型中删除构造函数引用有什么影响或缺点吗?

【问题讨论】:

标签: javascript constructor prototype


【解决方案1】:

就这一点而言,我看不到任何人提到最佳实践,所以我认为这取决于您是否可以看到 constructor 属性曾经有用。

值得注意的一点是constructor 属性,如果你不销毁它,它也将在创建的对象上可用。在我看来,这可能很有用:

var ClassOne = function() {alert("created one");}
var ClassTwo = function() {alert("created two");}

ClassOne.prototype.aProperty = "hello world"; // preserve constructor
ClassTwo.prototype = {aProperty: "hello world"}; // destroy constructor

var objectOne = new ClassOne(); // alerts "created one"
var objectTwo = new ClassTwo(); // alerts "created two"

objectOne.constructor(); // alerts "created one" again
objectTwo.constructor(); // creates and returns an empty object instance

所以在我看来,这是一个架构决定。您是否要允许创建的对象在实例化后重新调用其构造函数?如果是这样保存它。如果没有,就销毁它。

请注意,objectTwo 的构造函数现在完全等同于标准的 Object 构造函数 - 没用。

objectTwo.constructor === Object; // true

所以调用new objectTwo.constructor() 等价于new Object()

【讨论】:

    【解决方案2】:

    这不是坏习惯,但你必须知道你在做什么以及为什么。它对于原型继承非常有用。您覆盖其原型的对象将获得您分配给它的原型的对象的所有属性:

    你使一个对象继承使用

    ChildClassName.prototype = new ParentClass();.
    

    现在 ChildClassName 拥有 ParentClass 的所有功能,但失去了之前分配给它的原型的任何功能。您需要记住使用

    重置对象的构造函数属性
    ChildClassName.prototype.constructor=ChildClassName. 
    

    否则,对象将被报告为(在测试对象类型时)ParentClass 类型而不是 ChildClassName 类型。

    现在您可以按照您自己描述的方式向 ChildClassName 对象添加更多方法。

    ChildClassName.prototype.myMethod = function(){
        //do stuff
    }
    

    结果是一个父对象/“类”(当然,javascript 中没有真正的类)和一个从它继承并扩展其功能的子对象/“类”。

    您只需要知道,如果您覆盖原型,分配给它的任何属性都将消失。在构造继承对象时,这可能正是您想要的。

    【讨论】:

    • 当您说“报告为 ParentClass 类型”时,您的意思是当使用 instanceof 进行测试时,它将针对子类返回 false
    【解决方案3】:

    这个表格:

    LolCat.prototype = {
      hello: function () {
          alert('meow!');
      }
    };
    

    销毁任何现有方法和公共属性。在示例中,as given,这并不重要,因为新创建的 LolCat 没有任何属性或方法。但是,在更复杂的代码中应该注意这一点。

    这个表格:

    LolCat.prototype.hello = function () { ... }
    

    向现有对象添加新方法并保持现有对象不变。

    【讨论】:

    • 没错,但是在像 OP 示例这样新定义的类的上下文中,这并不是真正的问题,因为还没有任何公共“存在”。
    • 哦,我同意在示例中,as given,这是真的。我修改了我的答案以澄清。谢谢。
    【解决方案4】:

    在使用原型继承时覆盖constructor 并不是一个坏习惯。事实上很多人都这样做:

    LolCat.prototype = {
        constructor: LolCat,
        hello: function () {
            alert('meow!');
        }
    };
    

    【讨论】:

    • 那你会说它完全是可选的吗?我的意思是,如果它不破坏 instanceof 运算符,那么完全不使用它就没有真正的缺点?
    • @FelixKling 谢谢,这是一个很好的链接!我认为 Jacob 的回答对我来说最有意义 - 基本上,constructor 属性的显式重新分配是一种尝试通过经典继承的圆孔来适应 Javascript 的方钉的方式! :)
    【解决方案5】:

    在使用 javascript 多年后,我偶然发现了一个奇怪的错误,即当它的 prototype 类被声明为时,一个新的对象实例,然后传入一个 mixin 丢失了它的 __proto__

    MyClass.prototype = {
       constructor: MyClass,
       myMethod(){ ...}
    };
    

    使用时没有问题了:

    Object.assign( MyClass.prototype, {
       myMethod(){ ...}
    });
    

    奖励:您不必再重新签名构造。

    我猜是因为当我们完全覆盖原型属性时,我们也擦除了它的特殊属性并将其转换为普通属性...也许这个默认情况下不应该是可写的...

    【讨论】:

      【解决方案6】:

      这不是一个坏习惯。但是有一种简单而标准的方法可以覆盖下面的函数。当我们在全局范围内定义一个“函数 LolCat()”时,它会在窗口下创建,因此您始终可以像下面这样编写代码。

      window.LolCat = function() {...};
      LolCat.prototype.hello = function () { ... }
      

      【讨论】:

      • 我认为这并不能真正回答问题,而且将构造函数放在全局命名空间中并不是一个好主意。
      猜你喜欢
      • 2013-04-06
      • 1970-01-01
      • 2021-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多