简体   繁体   English

带有原型和闭包的面向对象的javascript

[英]Object oriented javascript with prototypes vs closures

I'm curious what the difference is between the following OOP javascript techniques. 我很好奇以下OOP javascript技术之间的区别。 They seem to end up doing the same thing but is one considered better than the other? 他们似乎最终会做同样的事情,但是一个被认为比另一个更好吗?

function Book(title) {
    this.title = title;
}

Book.prototype.getTitle = function () {
    return this.title;
};

var myBook = new Book('War and Peace');
alert(myBook.getTitle())

vs

function Book(title) {
    var book = {
        title: title
    };
    book.getTitle = function () {
        return this.title;
    };
    return book;
}

var myBook = Book('War and Peace');
alert(myBook.getTitle())

The second one doesn't really create an instance, it simply returns an object. 第二个并没有真正创建一个实例,它只是返回一个对象。 That means you can't take advantage of operators like instanceof . 这意味着您无法利用instanceof之类的运算符。 Eg. 例如。 with the first case you can do if (myBook instanceof Book) to check if the variable is a type of Book, while with the second example this would fail. 在第一种情况下,您可以执行if (myBook instanceof Book)检查变量是否为Book的类型,而在第二种情况下,此操作将失败。

If you want to specify your object methods in the constructor, this is the proper way to do it: 如果要在构造函数中指定对象方法,则这是正确的方法:

function Book(title) {
    this.title = title;

    this.getTitle = function () {
        return this.title;
    };
}

var myBook = new Book('War and Peace');
alert(myBook.getTitle())

While in this example the both behave the exact same way, there are differences. 尽管在此示例中,两者的行为完全相同,但存在差异。 With closure-based implementation you can have private variables and methods (just don't expose them in the this object). 使用基于闭包的实现,您可以拥有私有变量和方法(只是不要在this对象中公开它们)。 So you can do something such as: 因此,您可以执行以下操作:

function Book(title) {
    var title_;

    this.getTitle = function() {
        return title_;
    };

    this.setTitle = function(title) {
        title_ = title;
    };

    // should use the setter in case it does something else than just assign
    this.setTitle(title);
}

Code outside of the Book function can not access the member variable directly, they have to use the accessors. Book函数之外的代码无法直接访问成员变量,因此必须使用访问器。

Other big difference is performance; 另一个很大的不同是性能。 Prototype based classing is usually much faster, due to some overhead included in using closures. 基于原型的分类通常要快得多,这归因于使用闭包所带来的一些开销。 You can read about the performance differences in this article: http://blogs.msdn.com/b/kristoffer/archive/2007/02/13/javascript-prototype-versus-closure-execution-speed.aspx 您可以在本文中阅读有关性能差异的信息: http : //blogs.msdn.com/b/kristoffer/archive/2007/02/13/javascript-prototype-versus-closure-execution-speed.aspx

Which is better can sometimes be defined by the context of their usage. 有时可以通过使用它们的上下文来定义哪个更好

Three constraints of how I choose between Prototype and Closure methods of coding (I actively use both): 原型封闭编码方法之间进行选择的三个限制(我同时使用两者):

  1. Performance/Resources 绩效/资源
  2. Compression requirements 压缩要求
  3. Project Management 项目管理

1. Performance/Resources 1.绩效/资源

For a single instance of the object, either method is fine. 对于对象的单个实例,两种方法都可以。 Any speed advantages would most likely be negligible. 任何速度上的优势很可能都可以忽略不计。

If I am instantiating 100,000 of these, like building a book library, then the Prototype Method would be preferred. 如果要实例化其中的100,000个,例如构建图书库,则首选“ 原型方法” All the .prototype. 所有.prototype。 functions would only be created once, instead of these functions being created 100,000 times if using the Closure Method . 函数仅创建一次,而使用Closure Method则不能创建100,000次。 Resources are not infinite. 资源不是无限的。

2. Compression 2.压缩

Use the Closure Method if compression efficiency is important (ex: most browser libraries/modules). 如果压缩效率很重要(例如:大多数浏览器库/模块),请使用“ 关闭方法” See below for explanation: 参见以下说明:

Compression - Prototype Method 压缩-原型方法

function Book(title) {
    this.title = title;
}

Book.prototype.getTitle = function () {
  return this.title;
};

Is YUI compressed to YUI被压缩为

function Book(a){this.title=a}Book.prototype.getTitle=function(){return this.title};

A savings of about 18% (all spaces/tabs/returns). 节省约18%(所有空格/制表符/返回)。 This method requires variables/functions to be exposed (this.variable=value) so every prototype function can access them. 此方法要求公开变量/函数(this.variable = value),以便每个原型函数都可以访问它们。 As such, these variables/functions can't be optimized in compression. 因此,这些变量/函数无法在压缩中进行优化。

Compression - Closure Method 压缩-封闭法

function Book(title) {
  var title = title; // use var instead of this.title to make this a local variable

this.getTitle = function () {
  return title;
};
}

Is YUI compressed to YUI被压缩为

function Book(a){var a=a;this.getTitle=function(){return a}};

A savings of about 33%. 节省约33%。 Local variables can be optimized. 局部变量可以优化。 In a large module, with many support functions, this can have significant savings in compression. 在具有许多支持功能的大型模块中,这可以显着节省压缩量。

3. Project Management 3.项目管理

In a project with multiple developers, who could be working on the same module, I prefer the Prototype Method for that module, if not constrained by performance or compression. 在一个有多个开发人员的项目中,他们可能正在同一个模块上工作,因此,如果不受性能或压缩的限制,我更喜欢该模块的原型方法

For browser development, I can override the producton.prototype.aFunction from "production.js" in my own "test.js" (read in afterwords) for the purpose of testing or development, without having to modify the "production.js", which may be in active development by a different developer. 对于浏览器开发,出于测试或开发的目的,我可以在我自己的“ test.js”(用后记单词读入)中覆盖“ production.js”中的producton.prototype.aFunction,而无需修改“ production.js” ,可能由其他开发人员进行积极开发。

I'm not a big fan of complex GIT repository checkout/branch/merge/conflict flow. 我不喜欢复杂的GIT存储库结帐/分支/合并/冲突流程。 I prefer simple. 我更喜欢简单。

Also, the ability to redefine or "hijack" a module's function by a testbench can be beneficial, but too complicated to address here... 同样,通过测试平台重新定义或“劫持”模块功能的功能可能会有所帮助,但是太复杂了,无法在此处解决...

The former method is how JavaScript was intended to be used. 前一种方法是打算使用JavaScript的方式。 The latter is the more modern technique, popularised in part by Douglas Crockford. 后者是一种更现代的技术,部分由Douglas Crockford推广。 This technique is much more flexible. 该技术更加灵活。

You could also do: 您也可以这样做:

function Book(title) {
    return {
        getTitle: function () {
            return title;
        }
    }
}

The returned object would just have an accessor called getTitle , which would return the argument, held in closure. 返回的对象只有一个名为getTitle ,该访问器将返回闭包中保存的参数。

Crockford has a good page on Private Members in JavaScript - definitely worth a read to see the different options. 克罗克福德(Crockford) 在JavaScript上有一个关于私人会员的好页面-绝对值得一读以了解不同的选择。

It's also a little bit about re-usability under the hood . 关于引擎盖下的可重用性也有一点。 In the first example with the Function.prototype property usage all the instances of the Book function-object will share the same copy of the getTitle method. 在具有Function.prototype属性用法的第一个示例中,Book函数对象的所有实例将共享getTitle方法的相同副本。 While the second snippet will make the Book function execution create and keep in the heap 'bookshelf' different copies of the local closurable book object. 第二个片段将使Book函数的执行创建并在堆' bookshelf '中保留本地可关闭 book对象的不同副本。

function Book(title) {
    var book = {
        title: title
    };
    book.getTitle = function () {
        return this.title += '#';
    };
    return book;
}

var myBook = Book('War and Peace');
var myAnotherBook = Book('Anna Karenina');
alert(myBook.getTitle()); // War and Peace#
alert(myBook.getTitle()); // War and Peace##
alert(myAnotherBook.getTitle()); // Anna Karenina#
alert(myBook.getTitle());// War and Peace###

The prototype members exist in the only copy for all the new instances of the object on the other hand. 另一方面,原型成员存在于对象的所有new实例的唯一副本中。 So this is one more subtle difference between them that is not very obvious from the first sigh due to the closure trick. 因此,这是它们之间的另一个细微差别,由于闭合技巧,乍一看并不太明显。

here is an article about this in general Book inharets from Book.prototype. 这是来自Book.prototype的一般Book inharets中与此相关的文章。 In first example you add function to getTitle Book.prototype 在第一个示例中,将函数添加到getTitle Book.prototype

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM