使用构造函数创建对象和返回对象有区别吗

Is there a difference in using a constructor to create an object versus returning an object?

本文关键字:对象 有区别 返回 构造函数 创建对象      更新时间:2024-01-15

这些函数的操作方式有什么不同吗?第一个更典型的是我在考虑构造函数时的想法。

示例1:使用this命名和设置属性。然后使用new创建一个新的Book对象。

    function Book(name, numPages) {
        this.name = name;
        this.numPages = numPages;
    }
    var myBook = new Book('A Good Book', '500 pages');

示例2:使用new返回一个对象,只调用函数本身。

    function Movie(name, numMinutes) {
        return { name:name, numMinutes:numMinutes };
    }
    var best = new Movie('Forrest Gump', '150');
    var other = Movie('Gladiator', '180');

我想我想弄清楚的是,它们创建对象的方式是否不同?如果是这样,一个比另一个好吗?有没有不同的情况下,一个会比另一个工作得更好?

第一个是构造函数,因此可以由prototype扩展,您可以通过instanceof测试结果是否为此类型的实例。缺点:如果你忘记了new-关键字,你的代码就会爆炸(除非你在每个constuctor中都写了一个解决方法)

当实例化一个新的Object时,不能真正将apply()与构造函数一起使用来传递参数数组;另一方面,不要那样做,即使你可以。

第二个是工厂,而不是建造商。与是否使用new-关键字无关。通过该实现,它创建了看起来相同但不共享类型或原型的对象(尽管底层JS引擎将它们识别为相似,因此只要它们具有相同的属性、以相同的顺序添加、…不同的主题,它们就共享相同的隐藏类)
长话短说,无论是性能还是内存占用都不会受到这种方法的影响

但你无法检查它们是否属于同一类型,也没有一个可能影响所有实例的共享原型(可能是赞成或反对)

我的goto方法如果我需要继承,是两者的混合:
(如果我只需要一个数据对象,我通常使用工厂和普通对象)。

function Book(conf) {
    var book = Object.create(Book.prototype);
    //a book like this usually has multiple configs/properties
    if(typeof conf === "object"){
        for(var k in conf) book[k] = conf[k];
    }else if(conf){
        //assuming that I get at least the name passed
        book.name = String(conf);
    }
    return book;
}
//I have a prototype that can be extended
//with default-values for example; no idea for a good method 
//to add to the prototype in this example ;)
Book.prototype.numPages = 0;
//but I can also use it like a plain function; no error if you 
var myBook1 = Book("Peter Pan");
var myBook2 = Book({
    name: "American Gods",
    author: "Neil Gaiman"
});

如果我在函数的顶部添加以下行,我也可以将其用作一种方法,将任何内容强制转换到Book的实例中,而无需克隆现有实例

function Book(conf) {
    //with this simple line I can also use this as a function to cast anything into a "Book"
    if(conf instanceof Book) return conf;
    var book = Object.create(Book.prototype);
    //...
    return book;
}
var data = [
    "Peter Pan",
    {name: "American Gods"},
    //...
];
var books = data.map(Book);

在我看来,这种方法对我来说是两全其美。

基本上,当您使用new时,JS引擎会为您创建一个全新的对象,并将其作为this的值注入。它还自动为您提供任何附加到构造函数原型的方法。使用构造函数还可以更容易地检查对象是否是instanceof

function MovieA(title) {
    this.title = title;
}
MovieA.prototype.getTitle = function() {
    return this.title;
};
function MovieB(title) {
    return {
    title: title
  };
}
MovieB.prototype.getTitle = function() {
    return this.title;
};
var a = new MovieA('A');
console.log(a instanceof MovieA); // true
console.log(a.getTitle()); // 'A'
var b = MovieB('B');
console.log(b instanceof MovieB); // false
console.log(b.getTitle()); // Uncaught TypeError: b.getTitle is not a function

new为您提供的一切都可以通过其他方法获得,但需要更多的体力劳动。

第二种方法,工厂,往往更适用于单元测试、自定义对象创建和函数编程。它在单元测试中效果更好,因为如果你有一个生产所有对象的工厂,你只需要用一个模型来替换那个工厂,就可以测试不同的用例。

var Factory = {
  makeThing: function() {
    return { name: 'thing' };
  }
};
// Want to test the case for if makeThing fails inside of some other code
var MockFactory = {
  makeThing: function() {
    return null;
  };
};

至于你什么时候使用,这完全取决于情况。有些人根本不使用new。其他仅使用new。这一切都取决于你是否需要上面列出的任何东西,你需要对对象的创建有多大的控制,你什么时候想使用this,等等。最后,这都是你的偏好问题。

不同之处在于用于创建返回对象的构造函数。

new Book('A Good Book', '500 pages');

创建一个Book对象实例,该实例继承Book.prototype的属性,包括Bookconstructor属性值。Book.prototype对象本身继承自Object.prototype

var other = Movie('Gladiator', '180');

使用Movie作为工厂函数(不需要new),并返回一个Object对象实例,该实例直接继承Object.prototype的属性,包括Objectconstructor属性值。

更简单地说,Object字面语法创建一个Object对象。