Javascript何时使用原型
我想了解什么时候适合在js中使用原型方法。 他们应该总是使用? 或者有没有使用它们的情况是不优选的和/或是否会导致性能损失?
在js中寻找常用的命名空间方法的网站上,大多数人使用非基于原型的实现:简单地使用一个对象或者一个函数对象来封装一个名字空间。
来自一个基于类的语言,很难不尝试和绘制相似之处,并认为原型就像“类”,我提到的名称空间实现就像静态方法。
原型是一个优化 。
使用它们的一个很好的例子就是jQuery库。 每当你通过使用$('.someClass')
获得一个jQuery对象,那个对象就有几十个“方法”。 该库可以通过返回一个对象来实现:
return { show: function() { ... }, hide: function() { ... }, css: function() { ... }, animate: function() { ... }, // etc... };
但是,这意味着内存中的每个jQuery对象都会有几十个包含相同方法的命名空间。
相反,这些方法是在一个原型上定义的,所有的jQuery对象都“inheritance”这个原型,以便以很less的运行时间获得所有这些方法。
jQuery如何正确的一个非常重要的部分是,这是程序员隐藏的。 这纯粹是一种优化,而不是你在使用库时必须担心的事情。
JavaScript的问题是,裸构造函数需要调用者记住用new
前缀,否则他们通常不工作。 这没有什么好的理由。 jQuery通过在一个普通的函数$
后面隐藏这个无意义来得到正确的结果,所以你不必关心对象是如何实现的。
所以你可以方便地用指定的原型创build一个对象,ECMAScript 5包含一个标准的函数Object.create
。 它的一个非常简化的版本将如下所示:
Object.create = function(prototype) { var Type = function () {}; Type.prototype = prototype; return new Type(); };
它只是照顾编写构造函数的痛苦,然后用new
调用它。
你什么时候会避免原型?
一个有用的比较是stream行的OO语言,如Java和C#。 这些支持两种inheritance:
- 接口inheritance,在那里你
implement
一个interface
,使得类为接口的每个成员提供了自己独特的实现。 - 实现inheritance,在这里
extend
一个提供某些方法的默认实现的class
。
在JavaScript中,原型inheritance是一种实现inheritance。 因此,在那些(在C#或Java中)你可以从基类派生出默认行为的情况下,你可以通过重写做一些小的修改,然后在JavaScript中,原型inheritance是有意义的。
但是,如果您处于以C#或Java方式使用接口的情况,那么在JavaScript中不需要任何特定的语言function。 没有必要显式声明表示接口的东西,也不需要将对象标记为“实现”该接口:
var duck = { quack: function() { ... } }; duck.quack(); // we're satisfied it's a duck!
换句话说,如果每个“types”的对象都有自己的“方法”的定义,那么从原型inheritance就没有价值。 之后,这取决于您为每种types分配了多less个实例。 但在许多模块化devise中,只有一个给定types的实例。
事实上, 有很多人提出实现inheritance是邪恶的 。 也就是说,如果一个types有一些共同的操作,那么如果它们没有被放到一个基类/超类中,或者只是作为普通的函数暴露在某个模块中,你想让他们操作。
如果你想声明对象的“非静态”方法,你应该使用原型。
var myObject = function () { }; myObject.prototype.getA = function (){ alert("A"); }; myObject.getB = function (){ alert("B"); }; myObject.getB(); // This works fine myObject.getA(); // Error! var myPrototypeCopy = new myObject(); myPrototypeCopy.getA(); // This works, too.
使用内置的prototype
对象的一个原因是如果你将复制一个对象多次,将共享共同的function。 通过将方法附加到原型,可以节省每个new
实例创build的重复方法。 但是当你把一个方法附加到prototype
,所有的实例都可以访问这些方法。
假设你有一个基类Car()
类/对象。
function Car() { // do some car stuff }
那么你创build了多个Car()
实例。
var volvo = new Car(), saab = new Car();
现在,你知道每辆汽车将需要驾驶,打开等。而不是将方法直接附加到Car()
类(每个创build的实例占用内存),您可以将方法附加到原型(创build方法只有一次),因此可以将这些方法同时用于新volvo
和saab
。
// just mapping for less typing Car.fn = Car.prototype; Car.fn.drive = function () { console.log("they see me rollin'"); }; Car.fn.honk = function () { console.log("HONK!!!"); } volvo.honk(); // => HONK!!! saab.drive(); // => they see me rollin'
当你要创build大量的特定types的对象的副本,并且他们都需要共享共同的行为时,将函数放在原型对象上。 通过这样做,每个函数只需要一个副本就可以节省一些内存,但这只是最简单的好处。
更改原型对象上的方法或添加方法会立即改变相应types的所有实例的性质。
现在到底为什么你要做所有这些事情主要是你自己的应用程序devise的function,以及你需要在客户端代码中做的事情。 (一个完全不同的故事将会是服务器内的代码;更容易想象在那里做更多的大规模的“OO”代码。)
如果我在基于类的术语解释那么Person是类,walk()是Prototype方法。 所以walk()只有在你用这个实例化新对象之后才会有它的存在。
所以,如果你想创build像Person这样的对象的副本,你可以创build许多用户。Prototype是一个很好的解决scheme,因为它通过为内存中的每个对象共享/inheritance相同的函数副本来节省内存。
静态在这种情况下不是很有帮助。
function Person(){ this.name = "anonymous"; } // its instance method and can access objects data data Person.prototype.walk = function(){ alert("person has started walking."); } // its like static method Person.ProcessPerson = function(Person p){ alert("Persons name is = " + p.name); } var userOne = new Person(); var userTwo = new Person(); //Call instance methods userOne.walk(); //Call static methods Person.ProcessPerson(userTwo);
所以这更像是实例方法。 该对象的方法就像静态方法。
https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript