【问题标题】:Object construction: is the prototype really necessary?对象构造:原型真的有必要吗?
【发布时间】:2015-03-24 20:25:24
【问题描述】:

我正在探索 JavaScript 中遗产的概念,但我认为我遗漏了一些东西。

我的目标:我想创建一个继承自另一个对象的对象。JavaScript 并且我认为我遗漏了一些东西。

作为示例,我创建了一个从对象“Personne”继承的对象“Student”。

我可以通过以下两种方式达到这个目标:

  • 第一种方式:我在构造函数的原型中声明属性和方法(参见示例 1)。
  • 第二种方式:我在构造函数本身中声明属性和方法(参见示例 2)。

在我看来,两种方式都很好。我的意思是:显然,任何一种方式都可以正常工作。

但是,如果我查看第二种方式,我会注意到该对象没有原型。因此,我想:这正常吗?事实上,我更喜欢第二种方式,因为我觉得它更优雅。

注意:我提供了一个可以与 NodeJs 一起使用的即用型脚本。我也给出了执行结果。

所以我的问题是:

  • 两种方式是否等效?
  • 如果是,那么:“原型”属性的用途是什么? (好像按照第二种方式,没有定义)。

谢谢

示例1:我在构造函数的原型中声明了属性和方法

// ---------------------------------------------
// We define a Personne.
// ---------------------------------------------

var Personne = function(inName) { // This is the constructor.
    // Do some initialization.
    console.log("Executing the constructor Personne.");
    if ('undefined' != typeof inName) {
        this.name = inName;
    }
}

var PersonnePrototype = { // This is the prototype.
    name: undefined,
    setName: function(inName) { this.name = inName; },
    getName: function() { return this.name; }
};

Personne.prototype = PersonnePrototype;

// ---------------------------------------------
// We define a Student.
// ---------------------------------------------

var Student = function(inAge, inName) { // This is the constructor.
    // Do some initialization.
    console.log("Executing the constructor Student with inAge=%d and inName=%s.", inAge, inName);
    if ('undefined' !== typeof inAge) {
        this.setAge(inAge);
    }
    if ('undefined' !== typeof inName) {
        this.setName(inName);
    }
}

Student.prototype = new Personne();
Student.prototype.age = undefined;
Student.prototype.setAge = function(inAge) { this.age = inAge; };
Student.prototype.getAge = function() { return this.age; };

示例 2:我在构造函数本身中声明了属性和方法

// ---------------------------------------------
// We define a Personne.
// ---------------------------------------------

var Personne = function(inName) {
    // We define the prototype here.
    this.name = undefined;
    this.setName = function(inName) { this.name = inName; },
    this.getName = function() { return this.name; }

    // Do some initialization.
    console.log("Executing the constructor Personne with inName=%s.", inName);
    if ('undefined' !== typeof inName) {
        this.name = inName;
    }
}

// ---------------------------------------------
// We define a Student.
// ---------------------------------------------

var Student = function(inAge, inName) {
    // We define the prototype here.
    Personne.call(this, inName);
    this.age = undefined;
    this.setAge = function(inAge) { this.age = inAge; };
    this.getAge = function() { return this.age; } 

    // Do some initialization.
    console.log("Executing the constructor Student with inAge=%d and inName=%s.", inAge, inName);
    if ('undefined' !== typeof inAge) {
        this.setAge(inAge);
    }           
}

准备使用脚本

if (process.argv.length < 3) {
    console.log('Usage: node "%s" <test number (1|2)>', process.argv[1]);
    return;
}

var test = process.argv[2];

if (test == 1) {

    // -------------------------------------------------
    // Executing test 1.
    // -------------------------------------------------
    console.log("Executing test 1");

    (function() {

        // ---------------------------------------------
        // We define a Personne.
        // ---------------------------------------------

        var Personne = function(inName) { // This is the constructor.
            // Do some initialization.
            console.log("Executing the constructor Personne.");
            if ('undefined' != typeof inName) {
                this.name = inName;
            }
        }

        var PersonnePrototype = { // This is the prototype.
            name: undefined,
            setName: function(inName) { this.name = inName; },
            getName: function() { return this.name; }
        };

        Personne.prototype = PersonnePrototype;

        // ---------------------------------------------
        // We define a Student.
        // ---------------------------------------------

        var Student = function(inAge, inName) { // This is the constructor.
            // Do some initialization.
            console.log("Executing the constructor Student with inAge=%d and inName=%s.", inAge, inName);
            if ('undefined' !== typeof inAge) {
                this.setAge(inAge);
            }
            if ('undefined' !== typeof inName) {
                this.setName(inName);
            }
        }

        Student.prototype = new Personne();
        Student.prototype.age = undefined;
        Student.prototype.setAge = function(inAge) { this.age = inAge; };
        Student.prototype.getAge = function() { return this.age; };

        var Tom = new Student(12, "Tom");
        console.log("The student %s is %d years old.", Tom.getName(), Tom.getAge());
        var Joe = new Student();
        Joe.setName("Joe");
        Joe.setAge(20);
        console.log("The student %s is %d years old.", Joe.getName(), Joe.getAge());
        console.log(Joe.__proto__);
        console.log(Object.getPrototypeOf(Joe));

    })();

    return;
}

if (test == 2) {

    // -------------------------------------------------
    // Executing test 2.
    // -------------------------------------------------
    console.log("Executing test 2");

    (function() {

        // ---------------------------------------------
        // We define a Personne.
        // ---------------------------------------------

        var Personne = function(inName) {
            // We define the prototype here.
            this.name = undefined;
            this.setName = function(inName) { this.name = inName; },
            this.getName = function() { return this.name; }

            // Do some initialization.
            console.log("Executing the constructor Personne with inName=%s.", inName);
            if ('undefined' !== typeof inName) {
                this.name = inName;
            }
        }

        // ---------------------------------------------
        // We define a Student.
        // ---------------------------------------------

        var Student = function(inAge, inName) {
            // We define the prototype here.
            Personne.call(this, inName);
            this.age = undefined;
            this.setAge = function(inAge) { this.age = inAge; };
            this.getAge = function() { return this.age; } 

            // Do some initialization.
            console.log("Executing the constructor Student with inAge=%d and inName=%s.", inAge, inName);
            if ('undefined' !== typeof inAge) {
                this.setAge(inAge);
            }           
        }

        var Tom = new Student(12, "Tom");
        console.log("The student %s is %d years old.", Tom.getName(), Tom.getAge());
        var Joe = new Student();
        Joe.setName("Joe");
        Joe.setAge(20);
        console.log("The student %s is %d years old.", Joe.getName(), Joe.getAge());
        console.log(Joe.__proto__);
        console.log(Object.getPrototypeOf(Joe));

    })();

    return;
}

console.log("Bad test number %d.", test);

执行:

$ node oo.js 1
Executing test 1
Executing the constructor Personne.
Executing the constructor Student with inAge=12 and inName=Tom.
The student Tom is 12 years old.
Executing the constructor Student with inAge=NaN and inName=undefined.
The student Joe is 20 years old.
{ age: undefined, setAge: [Function], getAge: [Function] }
{ age: undefined, setAge: [Function], getAge: [Function] }

$ node oo.js 2
Executing test 2
Executing the constructor Personne with inName=Tom.
Executing the constructor Student with inAge=12 and inName=Tom.
The student Tom is 12 years old.
Executing the constructor Personne with inName=undefined.
Executing the constructor Student with inAge=NaN and inName=undefined.
The student Joe is 20 years old.
{}
{}

【问题讨论】:

  • 有必要吗?这是 Javascript 语言中内置的设计选择。你已经证明了这两种方法都可以。
  • 一个 OT nit:这是一个非常强大的 JS 约定,只有构造函数以大写字母开头。 joetom 应为小写。
  • 检查这两个示例之间的区别:Joe.getAge === Tom.getAge。在原型一中,两个对象共享一个函数副本。在另一个中,它们各有一个功能,使用更多内存。两者都有优势,但这是需要做出的主要权衡。原型版本的另一个有趣的优势是它是实时的。如果您想在最后一刻将日志记录添加到所有 setAge 调用中,您可以这样做,甚至是已经创建的对象。
  • 嗨,斯科特,感谢您的解释。你关于“活的原型”的论点让我信服。
  • @DenisBeurive:您可以查找Object.create 以获得在现代环境中使用原型的更简单语法。

标签: javascript inheritance constructor prototype


【解决方案1】:
  1. 在您的示例中,它们确实是等价的。

  2. 请注意,在构造函数中定义属性将为对象的每个实例创建一个副本,但在原型中定义它们只会创建一个副本(原型中的那个)。

【讨论】:

  • 感谢您的回答。它有帮助。
猜你喜欢
  • 2018-04-05
  • 2021-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多