原型继承最佳实践?

发布于 2024-12-27 10:05:01 字数 746 浏览 0 评论 0原文

我刚刚接触 JavaScript,并试图了解原型继承。似乎有多种方法可以达到相同的效果,所以我想看看是否有任何最佳实践或理由以一种方式做事而不是另一种方式。这就是我要说的:

// Method 1
function Rabbit() {
    this.name = "Hoppy";

    this.hop = function() {
        console.log("I am hopping!");
    }
}

// Method 2
function Rabbit() {}

Rabbit.prototype = {
    name: "Hoppy",

    hop: function() {
        console.log("I am hopping!");
    }
}

// Method 3
function Rabbit() {
    this.name = "Hoppy";
}

Rabbit.prototype.hop = function() {
    console.log("I am hopping!");
}

// Testing code (each method tested with others commented out)
var rabbit = new Rabbit();
console.log("rabbit.name = " + rabbit.name);        
rabbit.hop();

所有这些看起来单独都有相同的效果(除非我遗漏了一些东西)。那么一种方法是否优于另一种方法呢?你怎么做?

I'm just getting into JavaScript and I'm trying to wrap my head around prototypal inheritance. It appears that there's multiple ways to achieve the same effect, so I wanted to see if there is any best practices or reasons to do things one way over the other. Here's what I'm talking about:

// Method 1
function Rabbit() {
    this.name = "Hoppy";

    this.hop = function() {
        console.log("I am hopping!");
    }
}

// Method 2
function Rabbit() {}

Rabbit.prototype = {
    name: "Hoppy",

    hop: function() {
        console.log("I am hopping!");
    }
}

// Method 3
function Rabbit() {
    this.name = "Hoppy";
}

Rabbit.prototype.hop = function() {
    console.log("I am hopping!");
}

// Testing code (each method tested with others commented out)
var rabbit = new Rabbit();
console.log("rabbit.name = " + rabbit.name);        
rabbit.hop();

All of these appear to have the same effect individually (unless I'm missing something). So is one method preferred over the other? How do you do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

神也荒唐 2025-01-03 10:05:01

当您将方法放在原型上时,每个实例对象都共享对该方法的相同引用。如果您有 10 个实例,则该方法有 1 个副本。

当您执行示例 1 中的操作时,每个实例对象都有其自己的同一方法版本,因此,如果您创建 10 个对象,则会运行 10 个代码副本。

使用原型之所以有效,是因为 javascript 具有将函数执行与实例相关联的机制,即它为函数的执行设置 this 属性。

因此,高度推荐使用原型,因为它占用的空间更少(当然,除非这就是您想要的)。

在方法 2 中,您通过将原型设置为等于对象字面量来设置原型。请注意,这里您正在设置一个属性,我认为您不打算这样做,因为所有实例都将获得相同的属性。

在方法 3 中,您一次构建一个作业的原型。

对于所有事情,我更喜欢方法 3。即在我的构造函数中我设置了属性值

myObj = function(p1){
   this.p1; // every instance will probably have its own value anyway.
}

myObj.prototype.method1 = function(){..} // all instances share the same method, but when invoked  **this** has the right scope.

When you put a method on the prototype, every instance object shares the same reference to the method. If you have 10 instances, there is 1 copy of the method.

When you do what you did in example 1, every instance object has its own version of the same method, so if you create 10 of your objects, there are 10 copies of the code running around.

Using the prototype works because javascript has machinery for associated a function execution with a instance, i.e. it sets the this property for the execution of the function.

So using the prototype is highly preferred since it uses less space (unless of course, that is what you want).

In method 2, you are setting the prototype by setting it equal to an object literal. Note that here you are setting a property, which I think you don't intend to do, since all instances will get the same property.

In Method 3, you are building the prototype one assignment at a time.

I prefer method 3 for all things. i.e. In my constructor I set my property values

myObj = function(p1){
   this.p1; // every instance will probably have its own value anyway.
}

myObj.prototype.method1 = function(){..} // all instances share the same method, but when invoked  **this** has the right scope.
咆哮 2025-01-03 10:05:01

让我们一次看一个例子。首先:

function Rabbit() {
    this.name = "Hoppy";

    this.hop = function() { //Every instance gets a copy of this method...
        console.log("I am hopping!");
    }
}
var rabbit = new Rabbit();

正如您在问题中所说,上面的代码将会起作用。它将创建一个 Rabbit 类的新实例。每次创建实例时,hop 方法的副本都会存储在该实例的内存中。

第二个示例如下所示:

function Rabbit() {}

Rabbit.prototype = {
    name: "Hoppy",

    hop: function() { //Now every instance shares this method :)
        console.log("I am hopping!");
    }
}
var rabbit = new Rabbit();

这一次,Rabbit 的每个实例都将共享 hop 方法的副本。这要好得多,因为它使用更少的内存。但是,每个 Rabbit 将具有相同的名称(假设您没有在构造函数中隐藏 name 属性)。这是因为该方法是从prototype继承的。在 JavaScript 中,当您尝试访问对象的属性时,将首先在对象本身上搜索该属性。如果在那里找不到,我们会查看 prototype (依此类推,沿着原型链向上,直到到达 prototype 属性为 null)。

你的第三个例子几乎就是我的做法。实例之间共享的方法应在原型上声明。您可能希望在构造函数中设置诸如 name 之类的属性,可以在每个实例的基础上进行声明:

function Rabbit(rabbitName) {
    this.name = rabbitName;
}
Rabbit.prototype.hop = function() {
    console.log("Hopping!");
}

Let's look at your examples one at a time. First:

function Rabbit() {
    this.name = "Hoppy";

    this.hop = function() { //Every instance gets a copy of this method...
        console.log("I am hopping!");
    }
}
var rabbit = new Rabbit();

The above code will work, as you have said in your question. It will create a new instance of the Rabbit class. Every time you create an instance, a copy of the hop method will be stored in memory for that instance.

The second example looked like this:

function Rabbit() {}

Rabbit.prototype = {
    name: "Hoppy",

    hop: function() { //Now every instance shares this method :)
        console.log("I am hopping!");
    }
}
var rabbit = new Rabbit();

This time, every instance of Rabbit will share a copy of the hop method. That's much better as it uses less memory. However, every Rabbit will have the same name (assuming you don't shadow the name property in the constructor). This is because the method is inherited from the prototype. In JavaScript, when you try to access a property of an object, that property will first be searched for on the object itself. If it's not found there, we look at the prototype (and so on, up the prototype chain until we reach an object whose prototype property is null).

Your third example is pretty much the way I would do it. Methods shared between instances should be declared on the prototype. Properties like name, which you may well want to set in the constructor, can be declared on a per-instance basis:

function Rabbit(rabbitName) {
    this.name = rabbitName;
}
Rabbit.prototype.hop = function() {
    console.log("Hopping!");
}
只想待在家 2025-01-03 10:05:01

这是一个经常被误解的重要问题。这取决于你想做什么。总体来说,hvgotcode的回答是对的。任何频繁实例化的对象都应该将方法和属性附加到原型上。

但在非常特殊的情况下,其他人也有优势。阅读本文,包括评论: http://net.tutsplus.com/tutorials/javascript-ajax/stop-nesting-functions-but-not-all-of-them/

有时上述方法 1 会有所帮助,使您能够“私有”可读/可写属性和方法。虽然在大量实例化的对象中,这通常不值得牺牲,但对于仅实例化一次或几次的对象,或者没有许多内部分配,或者如果您处于具有许多不同技能水平和敏感性的开发团队环境中,那么可能会有帮助。

一些开发人员采用了另一种好的策略,试图弥补其他策略的一些缺点。那是:

var Obj = function() {
    var private_read_only = 'value';

    return {
        method1: function() {},
        method2: function() {}
    };
};

This is an important issue that is often misunderstood. It depends what you're trying to do. Generally speaking, hvgotcode's answer is right on. Any object that will be instantiated frequently should attach methods and properties to the prototype.

But there are advantages to the others in very specific situations. Read this, including the comments: http://net.tutsplus.com/tutorials/javascript-ajax/stop-nesting-functions-but-not-all-of-them/

There are occasions when method 1 above helps, enabling you to have "private" readable/writable properties and methods. While this often isn't worth the sacrifice in heavily instantiated objects, for objects instantiated only once or a few times, or without many internal assignments, or if you're in a dev team environment with lots of different skill levels and sensibilities, it can be helpful.

Some devs incorporate another good strategy that attempts to bridge some of the shortcomings of the others. That is:

var Obj = function() {
    var private_read_only = 'value';

    return {
        method1: function() {},
        method2: function() {}
    };
};
拥抱我好吗 2025-01-03 10:05:01
// option 4
var Rabbit {
    constructor: function () {
        this.name = "Hoppy";
        return this;
    },

    hop: function() {
        console.log("I am hopping!");
    }
};

var rabbit = Object.create(Rabbit).constructor();
console.log("rabbit.name = " + rabbit.name);        
rabbit.hop();

在使用 new 进行原型 OO 时,构造函数是完全可选的。

正如已经指出的,如果您可以通过原型共享某些内容,请这样做。原型在内存方面更有效,并且在实例化时间方面更便宜。

然而,一个完全有效的替代方案是

function Rabbit() {
    // for some value of extend https://gist.github.com/1441105
    var r = extend({}, Rabbit);
    r.name = "Hoppy";
    return r;
}

使用“原型”的属性扩展“实例”。真正的原型面向对象的唯一优点是它是实时链接,这意味着对原型的更改会反映到所有实例。

// option 4
var Rabbit {
    constructor: function () {
        this.name = "Hoppy";
        return this;
    },

    hop: function() {
        console.log("I am hopping!");
    }
};

var rabbit = Object.create(Rabbit).constructor();
console.log("rabbit.name = " + rabbit.name);        
rabbit.hop();

When doing prototypical OO using new and constructor functions is completely optional.

As has already been noted, if you can share something through the prototype do so. Prototypes are more efficient memory wise and they are cheaper in terms of instantiation time.

However a perfectly valid alternative would be

function Rabbit() {
    // for some value of extend https://gist.github.com/1441105
    var r = extend({}, Rabbit);
    r.name = "Hoppy";
    return r;
}

Here your extending "instances" with the properties of the "prototype". The only advantage real prototypical OO has is that it's a live link, meaning that changes to the prototype reflect to all instances.

回心转意 2025-01-03 10:05:01

做一些性能测试(声明大约 100 万个兔子变量)。第一种方法将是最消耗时间和内存的。

Do some performance testing (declare around 1 milion rabbit variables) . First method will be the most time and memory consuming.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文