JavaScript:覆盖函数的原型 - 是一种不好的做法吗?

29

由于在声明函数时,我们会使其原型的构造函数指向该函数本身,因此像这样覆盖函数的原型是否是一种不好的做法:

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 () { ... }

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

那么,通过覆盖函数的原型对象以方便起见从原型中删除构造函数引用是否会产生任何影响或缺点,就像第一个例子中那样?


1
一个有趣的问题。我打算在谷歌上转一圈,看看能找到什么。 - Robin Winslow
1
LolCat.prototype = { constructor: LolCat, ... }使用这个将会保留构造函数。 - dubucha
6个回答

24

我没有看到任何人提到最佳实践,因此我认为这取决于您是否认为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()


6

这并不是坏的编程习惯,但你需要知道自己在做什么以及为什么要这样做。它非常适用于原型继承。你重写的原型对象将获得你分配给它的对象的所有属性:

使用以下方式可以使一个对象继承另一个对象:

ChildClassName.prototype = new ParentClass();.

现在ChildClassName具有ParentClass的所有功能,但失去了在其原型之前分配给它的任何功能。您需要记住使用来重置对象的构造函数属性。
ChildClassName.prototype.constructor=ChildClassName. 

否则,当测试对象类型时,该对象将被报告为ParentClass类型而不是ChildClassName类型。现在,您可以按照您自己描述的方式向ChildClassName对象添加更多方法。
ChildClassName.prototype.myMethod = function(){
    //do stuff
}

其结果是一个父对象/类(当然,在Javascript中没有真正的类),以及一个继承自它并扩展其功能的子对象/类。

你只需要知道,如果你重写了原型,任何已分配给它的属性都将消失。在构建继承对象时,这可能正是你想要的。


当你说“被报告为ParentClass类型”时,你的意思是在使用instanceof测试时,它会针对子类返回false吗? - chrisfrancis27

3

这个表单:

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

销毁所有现有的方法和公共属性。在这个例子中,如所述,这并不重要,因为新创建的LolCat没有任何属性或方法。然而,在更复杂的代码中,应该注意这一点。

这种形式:

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

在现有对象中添加一个新方法并保持原有内容不变。

真的,但是在类定义的上下文中,就像OP的例子一样,这并不是一个问题,因为还没有任何公共内容“存在”。 - chrisfrancis27
1
哦,我同意在这个例子中像给定的那样是正确的。我修改了我的答案以澄清。谢谢你。 - Jeremy J Starcher

1
许多年来,我一直在使用JavaScript。最近我遇到了一个奇怪的错误。当我创建一个对象的新实例并将其传递给mixin时,该实例丢失了它的__proto__属性。这是因为该实例的prototype类被声明为:
MyClass.prototype = {
   constructor: MyClass,
   myMethod(){ ...}
};

使用时不再有任何问题:

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

奖励:您不再需要重新分配构造函数。

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


1

在使用原型继承时覆盖constructor并不是一种不好的做法。事实上,很多人都这样做:

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

你会说这完全是可选的吗?我的意思是,如果不破坏instanceof运算符,那么完全省略它不会有什么负面影响吧? - chrisfrancis27
1
@Chris:https://dev59.com/questions/XG865IYBdhLWcg3wCqLI?rq=1 - Felix Kling
@FelixKling 谢谢,那是一个很好的链接!我认为Jacob的答案对我来说最有意义 - 基本上,显式重新分配constructor属性是试图将JavaScript的方形钉子塞进经典继承的圆孔的一种方式! :) - chrisfrancis27

0

这不是一个坏的实践。但是有一种简单和标准的方法可以覆盖函数,如下所示。每当我们在全局定义一个“function LolCat()”时,它会在窗口下创建,因此您始终可以像下面这样编码。

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

我认为这并没有真正回答问题,而且在全局命名空间中放置构造函数也不是一个好主意。 - chrisfrancis27

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接