函数原型有很多用途,比如可以用来向一个函数实例上添加一些属性,但是它的一个最主要用途是使JavaScript能够以面向对象的方式编程。
实例化和原型(Instantiation 和 prototypes)
所有函数默认的都会有一个含有空对象的prototype属性,它的这个特性只有在实例化后才会有用,为了能够理解它的这种特性是多么的重要,需要知道一个重要的原则:函数具有双重功能,它可以作为一个平常的函数,也可以作为一个类。
我们可以通过下面一个小例子来看看如何使用prototype属性向一个函数上添加一个功能- function Ninja(){} Ninja.prototype.swingSword=function(){ return true; } var ninja=Ninja(); alert(ninja);//undefined,说明不是Ninja的一个实例 var ninja2=new Ninja(); alert(ninja2.swingSword);//true
- 从上面的这个例子,我们可以发现以下两个问题:
1.为了产生一个函数的实例对象必须使用new操作符调用该函数。
2.swingSword变成了Ninja的实例ninja2的一个属性。
对于函数Ninja来说,由于使用new操作符调用它,我们也可以认为他是一个构造函数。这就意味着,它使用new操作符调用函数的时候,该函数中this所指向的上下文就是对象自身的实例(即和prototype相同),这段话不是太好理解,来让我们看一个例子:
function Ninja(){ this.swung = false; // Should return true this.swingSword = function(){ return !this.swung; }; } // Should return false, but will be overridden Ninja.prototype.swingSword = function(){ return this.swung; }; var ninja = new Ninja(); alert(ninja.swingSword());
从上面的例子我们可以发现实例对象调用它的同名方法的顺序:
1.绑定在prototype上的属性。
2.绑定在构造函数内的属性。
另外一个需要了解的是绑定在prototype上的属性,他可以被更新和改变,即使一个对象被实例化后:如下
function Ninja(){ this.swung = true; } var ninja = new Ninja(); Ninja.prototype.swingSword = function(){ return this.swung; }; alert(ninja.swingSword());
对象类型
还是以例子说话:
function Ninja(){} var ninja = new Ninja(); alert(typeof ninja); alert(ninja instanceof Ninja); alert(ninja.constructor == Ninja);
从上面的例子中,我们可以看到:
1.所有的示例对象如果使用typeof检测都是object类型
2.instanceof 实例对象是由那个函数生成的。
3.任何实例都有个constructor属性,用来引用生成该实例对象的构造函数。
为了证明第三个观点,下面来看一个简单的例子:
var ninja = new Ninja(); var ninja2 = new ninja.constructor(); alert(ninja2 instanceof Ninja);//true
继承与prototype链
在这里我们还可以看到instanceof的一个重要特性:
function Person(){} Person.prototype.dance = function(){}; function Ninja(){} // Achieve similar, but non-inheritable, results Ninja.prototype = Person.prototype; Ninja.prototype = { dance: Person.prototype.dance }; // Only this maintains the prototype chain Ninja.prototype = new Person(); var ninja = new Ninja(); alert(ninja instanceof Ninja);//true alert(ninja instanceof Person);//true alert(ninja instanceof Object);//true
有很多方法从函数的prototype属性上复制功能(方法和函数),但是只有一种情况能够形成prototype链。即:SubFunction.prototype = new SuperFunction();.
HTML prototypes
在Internet Explorer 8, Firefox, Safari, 和 Opera这些浏览器中这些可以使用prototype扩展dom元素: 来看看一个简短的例子:
<div id="a">I'm going to be removed.</div> <div id="b">Me too!</div> HTMLElement.prototype.remove = function(){ if ( this.parentNode ) this.parentNode.removeChild( this ); }; // Old way var a = document.getElementById("a"); a.parentNode.removeChild( a ); // New way document.getElementById("b").remove();