Javascript中面向对象的问题

时间:2009-01-14 18:40:21

标签: javascript oop

我一直在使用javascript,但从未学过语言超过基础知识。我正在阅读John Resig的“Pro Javascript技术” - 我想出了一些问题,但我没有在书中或谷歌等上找到答案。

约翰在他的书中给出了这个例子: 功能#1

function User( name, age ){
  this.name = name;
  this.age = age;
}
// Add a new function to the object prototype
User.prototype.getName = function(){
  return this.name;
};
User.prototype.getAge = function(){
  return this.age;
};
var user = new User( "Bob", 44 );
console.log("User: " + user.getName() + ", Age: " + user.getAge());

我还在学习原型属性,所以我尝试写类似的东西:
功能#2

function User (name, age ) {
  this.name = name;
  this.age = age;
  this.getName = function() {
    return this.name;
  };
  this.getAge = function() {
    return this.age;
  };
}
var user = new User( "Bob", 44 );
console.log("User: " + user.getName() + ", Age: " + user.getAge());

它不使用 prototype 属性来创建getName和getAge函数,但输出与John的示例相同。

我更进了一步,创造了这个:
功能#3

var User = {
  name: "",
  age: 0,
  setName: function(name) {
    this.name = name;
  },
  setAge: function(age) {
    this.age = age;
  },
  getName: function() {
    return this.name;
  },
  getAge: function() {
    return this.age;
  }
};
User.setName("Bob");
User.setAge(44);
console.log("User: " + User.getName() + ", Age: " + User.getAge());

再次 - 它看起来与John的例子不同(我必须添加setter方法),但输出是相同的。

问题#1 - 3个功能有什么区别? prototype属性的优点是什么,而且Function#2做错了什么,因为它似乎更直接代码#2而不是#1(尽管我确信#1在John创建它时更好看)

问题#2 - 如何修改功能#3不使用setName和setAge方法,但仍保留{...}简写? {...}速记可以有构造函数吗?

提前感谢您帮助我学习!

修改 我认为我的第二个问题有点令人困惑。我的意思是我怎么能用{...}简写来创建一个User对象,但是在我创建了对象之后,请说:

var user = new User("Bob", 44);

就像在功能#1中一样 - 或者这是不可能的?

编辑#2 哇!谢谢大家的精彩回答。这真的让我更加清楚。因此,如果我理解正确,#1和#2之间的区别不是太大。如果我只创建一个“用户”对象 - 它们可能完全不同。但是如果我的程序创建了许多User对象,那么#1很可能会更有效并且使用更少的内存,因为所有对象都将共享相同的功能。

我真的很感谢所有伟大的答案 - 谢谢!

7 个答案:

答案 0 :(得分:22)

每次评估函数(){}时,它都会创建一个新的函数对象。因此,在#1中,所有User对象共享相同的getName和getAge函数,但在#2和#3中,每个对象都有自己的getName和getAge副本。所有不同的getName函数都表现完全相同,因此您无法看到输出中的任何差异。

{...}简写的构造函数。在计算时,它构造一个具有给定属性的新“对象”。当您运行“新用户(...)”时,它会构造一个新的“用户”。您碰巧创建了一个与用户具有相同行为的Object,但它们的类型不同。

对评论的回应:

你不能,直接。您可以创建一个按#3创建新对象的函数。例如:

function make_user(name, age) {
    return {
        name: name,
        age: age,
        getName: function() { return name; },
        getAge: function() { return age; },
    };
}

var user = make_user("Joe", "18");

答案 1 :(得分:12)

如果你想在JavaScript中做OOP,我强烈建议你查看闭包。我开始用这三个网页学习这个主题:

http://www.dustindiaz.com/javascript-private-public-privileged/

http://www.dustindiaz.com/namespace-your-javascript/

http://blog.morrisjohns.com/javascript_closures_for_dummies

1,2和3之间的差异如下: 1)是向现有对象添加新方法的示例。 2)与#1相同,但用户功能中的对象中包含某些方法。 3)是使用JSON定义对象的示例。缺点是您不能使用new(至少不使用该示例)来定义该对象的新实例。但是,您确实可以获得方便的JSON编码风格。

如果你还不知道,你肯定应该阅读JSON。当你理解JSON时,JavaScript会更有意义。

修改的 如果要在函数#3中使用new,可以将其写为

function User() {
  return {
    name: "",
    age: 0,
    setName: function(name) {
      this.name = name;
    },
    setAge: function(age) {
      this.age = age;
    },
    getName: function() {
      return this.name;
    },
    getAge: function() {
      return this.age;
    }
  };
}

当然,所有这些功能和属性都将是公开的。要将它们设为私有,您需要使用闭包。例如,您可以使用此语法将年龄和名称设为私有。

function User() {
  var age=0;
  var name="";
  return {
    setName: function(name_) {
      name = name_;
    },
    setAge: function(age_) {
      age = age_;
    },
    getName: function() {
      return name;
    },
    getAge: function() {
      return age;
    }
  };
}

答案 2 :(得分:5)

2:

您可以在不使用此类功能的情况下访问姓名和年龄。在javascript中,你必须使用不同的黑客来保护私密或受保护的东西。

User.name = "BoB";
User.age = 44;

将产生与您的示例相同的输出。

没有其他语言中出现的构造函数。最简单的方法是定义init()函数,并在实例化对象后立即调用它。

但我最重要的提示是调查http://www.prototypejs.org/。它是一个具有很多很酷功能的javascript库,试图使javascript“更多OO *”。

使用原型库可以使类的行为更像真正的OOP类。它还具有构造函数。

编辑: 至于你在评论中提到的内容:

person = new User();
person.name = "Bob";
person.age = 44;

答案 3 :(得分:4)

您的示例#1显示了prototype属性的用法。此属性可用于您创建的所有javascript对象,并允许您向对象声明添加属性或函数,因此您有一个具有2个属性的对象,稍后您添加了4个函数(getter和setter)。

你应该看到prototype属性作为在运行时修改对象规范的方法,比如你有一个名为name的对象:

var Name = {
  First: "",
  Last: ""
};

您可以稍后使用原型添加函数getFullName():

Name.prototype.getFullName = function() { return this.First + " " + this.Last; }

在示例2中,您在对象声明中内联了这些getter和setter的声明,因此最终它们是相同的。最后在第3个示例中,您将使用JavaScript对象表示法,您应该看到JSON

关于您的问题2您可以将对象声明为:

var User = {
  name: "",
  age: 0
};

这将为您提供没有getter和setter的相同对象。

答案 4 :(得分:1)

问题#1

prototypemonkey patching的好处。如第一个示例所示,该功能是在事后添加的。您可以继续此操作来添加或替换您需要的任何方法(但需要公平警告)。

定义像#2这样的对象更像是经典的OOP。但是,再一次,所有OOP语言都不允许猴子修补。

问题#2

在您的第3个函数中,您甚至不需要getset函数 - nameage是公共属性({{}的潜在缺点1}})。

{}

使用var User = { name: "", age: 0 }; User.name = 'Bob'; User.age = 44; console.log("User: " + User.name + ", Age: " + User.age); (对象文字)创建对象时,{}是构造函数(在浏览器上有所不同)。但是,基本上,你不能以这种格式使用构造函数。

答案 5 :(得分:0)

您似乎在这里有一些好的答案,但您可能想看看这个问题:best-approach-to-member-variables-in-object-oriented-javascript。这是my answer,它描述了差异和相似之处。

答案 6 :(得分:0)

如果您对JSON样式的JavaScript类声明会话感兴趣...

http://mahtonu.wordpress.com/2010/04/13/json-style-javascript-object-declaration/