【问题标题】:Understanding javascript object (different type of declaration)理解 javascript 对象(不同类型的声明)
【发布时间】:2013-04-27 09:14:58
【问题描述】:

我正在尝试理解 javascript 对象,但我很难理解不同类型的声明。

作为I read,定义对象有两种主要方式

方法一:

var student1 = {
    name: "Peter Foti",
    course: 'JavaScript',
    grade: 'A',
    dispInfo: function(){
         return this.name + ' has an ' + this.grade; 
    }
};

方法二:

function student (a, b, c) {
    this.name = a;
    this.course= b;
    this.grade = c;
    this.dispInfo = function(){
         return this.name + ' has an ' + this.grade; 
    }
}

对于方法2,我理解这个概念,如果我想创建一个student类型的变量,我只需要调用:

student1 = new student("Jean Dupont", "wine tasting", "A");
console.log(student1.dispInfo);

但是,使用方法1,如何在不重新编写dispInfo等所有内部函数的情况下创建student2?

我想做类似的事情

var student2 = {
    name: "Olivier Perraut",
    course: 'Pétanque',
    grade: 'F'
};

console.log(student2.getInfo);

【问题讨论】:

    标签: javascript class object initialization


    【解决方案1】:

    第一种方法是创建一个您只打算拥有一个的对象。这是给单身人士的。它直接创建student1 对象。

    第二种方法是构造函数。构造函数可以反复使用,根据需要创建任意数量的对象。

    按照惯例,构造函数最初应该是有上限的(例如,Student 而不是 student),就像 JavaScript 自己的对象(DateRegExp、...)一样。

    您可以使用 JavaScript 原型链,以便所有 Student 对象使用相同的 dispInfo 函数(具有不同的 this 值),而不是为每个对象创建一个 dispInfo

    function Student (a, b, c) {
        this.name   = a;
        this.course = b;
        this.grade  = c;
    }
    Student.prototype.dispInfo = function(){
         return this.name + ' has an ' + this.grade; 
    };
    
    var s1 = new Student("Mary", "Algebra", "A");
    var s2 = new Student("Joe", "Classical Sculpture", "B+");
    

    从 ES5 开始(对于旧版浏览器,“shims”也可以做到这一点),您不必使用构造函数来拥有共享原型的对象,您可以使用 Object.create 来做到这一点.我更喜欢构造函数,但你也可以使用 builder 函数:

    var StudentPrototype = {
        dispInfo: function(){
            return this.name + ' has an ' + this.grade; 
        }
    };
    function BuildStudent(a, b, c) {
        var student    = Object.create(StudentPrototype);
        student.name   = a;
        student.course = b;
        student.grade  = c;
        return student;
    }
    var s1 = BuildStudent("Mary", "Algebra", "A");
    var s2 = BuildStudent("Joe", "Classical Sculpture", "B+");
    

    请注意,我们new 与构建器函数一起使用,仅与构造函数一起使用。 (如果你这样做通常是无害的,但它对任何阅读代码的人来说都是不必要的和误导,所以你不想这样做。)

    或者在那种简单的情况下你甚至不需要builder函数,你可以直接使用Object.create,但这有点麻烦,因为如果你传入属性描述符(第二个参数),每个必须 是描述属性的对象,而不仅仅是它的值(这是有充分理由的),所以您必须这样做 {value: "the value"}(当然,您可能想要指定属性的其他内容,例如是否为enumerable 等):

    var StudentPrototype = {
        dispInfo: function(){
             return this.name + ' has an ' + this.grade; 
        }
    };
    var s1 = Object.create(StudentPrototype, {
            name:   {value: "Mary"},
            course: {value: "Algebra"},
            grade:  {value: "A"}
    });
    var s2 = Object.create(StudentPrototype, {
            name:   {value: "Joe"},
            course: {value: "Classical Sculpture"},
            grade:  {value: "B+"}
    });
    

    就个人而言,我更喜欢构造函数,但 JavaScript 的优点在于它支持多种编程风格,包括像构建器这样的东西或直接使用 Object.create 更合适。

    【讨论】:

    • 谢谢你的答案。问题是我不会总是想用所有参数初始化对象。这就是为什么某种数组会很好的原因。我知道我总是可以使用 s.name = "Mary"
    • @PaulFournel:你可以去掉那些你想去掉的(如果它们在最后);在构造函数中,它们将是undefined。例如,var s = new Student("Mary"); 会生成具有 name 和值 undefined 的学生,用于 coursegrade。您还可以将“选项”对象传递给构造函数,该对象从该对象的属性中获取所需的内容(jQuery——您可能听说过它——将其用于$.ajax 等,其他库也是如此)。
    • @PaulFournel:或者,当然,对于您正在做的事情,直接使用Object.create 可能对您更有意义(请参阅答案的末尾)。我不喜欢直接使用它,但有很多人喜欢。 :-)
    • 很好的答案,再次感谢!我可能需要一点时间来处理这一切。 :D
    【解决方案2】:

    JavaScript 是一种所谓的基于原型的面向对象语言。这意味着对象不会获得由类、特征、mixin 等定义的行为,而是直接从其他对象继承。

    因此,如果您希望 student2 的行为与 student1 相似,但只有一些细微差别,那么您只需从 student1 继承并覆盖那些不同的属性。在 JavaScript 中,原型继承是使用 Object.create 函数设置的,该函数接受要继承的对象和带有覆盖属性的可选属性描述符映射。

    var student1 = {
        name: "Peter Foti",
        course: 'JavaScript',
        grade: 'A',
        dispInfo: function() { return this.name + ' has an ' + this.grade; }
    },
    
        student2 = Object.create(student1, {
        name:   { value: 'Olivier Perraut' },
        course: { value: 'Pétanque' },
        grade:  { value: 'F' }
    });
    
    console.log(student2.dispInfo());
    // Olivier Perraut has an F
    

    通常,您会看到一种模式出现,在这种模式下,您将设置一个原始的“模板”对象,并让您的所有业务对象都从该对象继承,而不是让“业务”对象直接相互继承,有点像这个:

    var studentTemplate = {
        dispInfo: function() { return this.name + ' has an ' + this.grade; }
    },
    
        student1 = Object.create(studentTemplate, {
        name:   { value: 'Peter Foti' },
        course: { value: 'JavaScript' },
        grade:  { value: 'A' }
    }),
    
        student2 = Object.create(studentTemplate, {
        name:   { value: 'Olivier Perraut' },
        course: { value: 'Pétanque' },
        grade:  { value: 'F' }
    });
    
    console.log(student1.dispInfo());
    // PeterFoti has an A
    
    console.log(student2.dispInfo());
    // Olivier Perraut has an F
    

    顺便说一句,我认为将dispInfo 设为getter 属性而不是方法是个好主意:

    var studentTemplate = {};
    
    Object.defineProperty(studentTemplate, 'info', {
        get: function() { return this.name + ' has an ' + this.grade; }
    });
    
    var student1 = Object.create(studentTemplate, {
        name:   { value: 'Peter Foti' },
        course: { value: 'JavaScript' },
        grade:  { value: 'A' }
    });
    
    console.log(student1.info);
    // PeterFoti has an A
    

    【讨论】:

    • 感谢您的回答。有没有一种方法可以定义对象而不必使用name: { value: 'Olivier Perraut' } 而只需使用name: 'Olivier Perraut'
    • @PaulFournel:不在Object.create 调用本身中(不过,您可以在Object.create 之后分配student1.name = "Joe")。这是因为描述符必须是一个对象,它不能只是一个值(因为否则会有歧义——如果你使用一个对象,你的意思是将对象作为属性的值还是作为描述符?)。这就是为什么我更喜欢构造函数或构建函数而不是直接使用Object.create 的部分原因。直接使用它可能有点冗长。
    【解决方案3】:

    试试这个。虽然我更愿意使用方法 2 来实现这一点。

    var student1 = {
        name: "Peter Foti",
        course: 'JavaScript',
        grade: 'A',
        dispInfo: function(){
             return this.name + ' has an ' + this.grade; 
        }
    };
    
    function GenerateObject (objValues)
    {
        var object = Object.create(student1);
        for(var i in objValues)
        {
            object[i] = objValues[i]
        }
        return object;
    }
    var student2 = GenerateObject({
        name: "Olivier Perraut",
        course: 'Pétanque',
        grade: 'F'
        });
    

    【讨论】:

    • 是的,方法 2 似乎是这种情况下更好的解决方案。谢谢你的快速回答:)
    猜你喜欢
    • 2013-10-12
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多