面向对象编程:我应该使用函数还是对象文字

时间:2011-10-23 23:00:37

标签: javascript oop constructor

我知道一点Java,我喜欢这整个类和OO编程风格。好吧我正在阅读JavaScript,我发现没有像Java和C ++那样的“经典”OO风格,甚至不是简单的构造函数。程序员有很多选择。好吧,我做了这个,你怎么看待它。可以编写像这样的构造函数吗?

//constructor of Human
function Human(name, age, size, married) {
    this.n = name;
    this.a = age;
    this.s = size;
    this.m = married
    this.printInformation = function() {
        return "Name: " + this.n + ", Age: " + this.a + ", Size: " + this.s + ", Married: " + this.m;
    };
}

var human1 = new Human("Lenny Linux", 42, "142cm", false);
window.alert(human1.printInformation());

它的工作,所以我认为它可以。但是有一个问题:我也有其他选择。喜欢使用这些“对象文字”或他们称之为。我能做到这一点:

var human1 = {
    name: "Lenny Linux",
    age: 42,
    size: "142cm",
    married: false,
    printInformation: function() {
        //...
    }
}

那么这个更快吗?在我写完之后,有一个人类的“对象”,另一个我必须初始化。但我更喜欢另一个。我更容易忽略我的代码。但是第一个例子不是典型的JavaScript吗?所以我必须忘记Java的一切并使用特殊的JavaScript风格?或者我可以保留我的Java代码风格吗?你觉得怎么样?

PS:第一个例子的另一个问题。它让我疯狂,我不能强迫程序员,如果他初始化构造函数只使用布尔值,如果他在“已婚”变量。但我真的想强迫他。年龄相同:只能是整数...任何想法?

谢谢!

2 个答案:

答案 0 :(得分:5)

您描述的两种方法实际上是完全等效(除了语法之外),并且从Javascript的角度来看大多数都可以。也就是说,您可能不应该根据您现在所熟悉的内容选择做什么 - 从长远来看,您需要习惯使用语言而不是使用语言。继续......

如果我想强制我的字段成为某种类型,如在Java中那么?

Javascript是动态类型的,因此您将很难尝试应用相同的静态类型范例。您可以尝试在对象构造函数中执行运行时检查(使用typeof),但这通常不值得,因为检查仍然在运行时,不检查可能会导致类似的错误,而且typeof非常有限(检查是否很棘手)东西是一个数组,它很烦人检查接口,让我们不要开始使用“外来”浏览器对象......)

最后,不要过于强调动态打字 - 你很快就会习惯它。

如果你说对象文字方法和构造函数方法返回了相同的结果,那么差异是什么呢?

首先,虽然对象文字是一个非常简洁的语法,但有些事情需要在多个语句中拆分,所以你需要一个函数来实现它们:

//Note: lowercase name since I won't be using 'new here...
//there is a good convention for only using capital names on 
// "real" constructors
function create_human(name, age){
    var obj = {};
    obj.name = name;
    obj.age = age;

    //this needs to be on a separate statement
    //since it involves the other fields
    obj.isAdult = (obj.age >= 21);

    return obj;
}

//not using 'new ...yet
var that_penguin = create_human("Lenny", 42);

请注意,对象文字在这里仍然非常有用,并且在通常具有大型参数列表的情况下使用它们来提供命名和默认参数非常受欢迎:

function  create_human(args){
    var obj;
    obj.name = args.name;
    //...
}

var x  = create_human({
    name: 'Lenny',
    age: 42,
    //...
});

请记住:到目前为止,使用函数构建对象与对象文字只是风格和组织问题,最好的方法通常取决于您处理的具体情况用。在我的经验中,对象文字非常适合创建单例和配置字典,而函数对于在复杂对象中强制执行不变量非常有用,这些对象提供普通的简写。

那么“真正的”构造函数, new this 的处理是什么呢?

手工明确地构造对象的缺点是我们错过了我们习惯的一些OO善良。通过给每个对象提供其方法的副本,我们不仅浪费空间(用经典语言存储在类中),而且我们失去差异继承(因为一切都是静态的)。 Javascript处理此问题的方式是使用 Prototypes 。所有对象都有一个原型,当查找属性(或方法)时,如果没有立即找到它,则会在原型中递归搜索。

原型的一个常见用例是使一类对象自己保留实例变量但共享方法:

lenny:
    name: "Lenny"
    age: 42
    __proto__: Person.prototype

glenda:
    name: "Glenda"
    age: 19
    __proto__: Person.prototype

Person.prototype:
    printInformation: ...
    tons of methods: ...

这样我们就可以访问lenny.printInformation,甚至没有注意到这个方法正在与Glenda共享。

要使用原型创建对象,您可以使用Object.create(至少在较新的浏览器上)或使用构造函数和新运算符的旧方法:

function Person(name, age){
    //The 'new operator provides an empty
    // 'this' object with a suitable prototype.
    // The constructor function just needs to fill in the 
    // instance variables.

    this.name = name;
    this.age = age;

    //note: no return statement!

    //and no methods as well
    //(unless they need to be closures but thats another thing)...
}

//Methods in Person.prototype, will be shared by all Person instances:
Person.prototype = {
    printInformation: function(){
        console.log('my age is', this.age);
    }
};

var lenny = new Person("Lenny", 42);

总结

如果要使用该语言的原型功能,请使用构造函数和new运算符。

否则使用普通函数或对象文字。

答案 1 :(得分:1)

您应该从此book了解原型继承。

    //constructor of Human
    function Human(name, age, size, married) {
        this.n = name;
        this.a = age;
        this.s = size;
        this.m = married;
        this.printInformation = function() {
            return "Name: " + this.n + ", Age: " + this.a + ", Size: " + this.s +
                 ", Married: " + this.m;
        };
    }

    var human1 = new Human("Lenny Linux", 42, "142cm", false);
    window.alert(human1.printInformation());

因此,printInformation是一个很好的候选者,可以使用Human的原型来创建Human实例之间的共享方法。定义Human时,对this的所有引用都将是在幕后设置为空对象的对象文字。此外,在幕后,隐藏的_proto_属性被分配给对象,该对象实际上是对Human.prototype的对象引用。因此,只要您不对Human.prototype引用新对象,那么Human的所有实例都将具有与Human.protoype相同的函数指针。所以,这是您想要提高效率的标准示例。

    //constructor of Human
    function Human(name, age, size, married) {
        this.n = name;
        this.a = age;
        this.s = size;
        this.m = married;
    };
    Human.prototype.printInformation = function() {
        return "Name: " + this.n + ", Age: " + this.a + ", Size: " + this.s +
            ", Married: " + this.m;
    };
    var human1 = new Human("Lenny Linux", 42, "142cm", false);
    window.alert(human1.printInformation());

在运行时,解释器将尝试在实例本身上查找printInformation,并且在这种情况下不会找到它。因此,它将遵循_proto_链接。它在那里找到它,因为Human.prototype.printInformation是一个函数。

这是有效的原因是因为所有实例都指向同一个对象:Human.prototype;然而,当你在构造函数中说this.method = function() {}时,每个实例都会获得一个分配给它的新函数。