JavaScript中的面向对象【五】
Prototype属性
JavaScript 中的函数是对象,他们包含方法与属性。有一些方法你已经熟悉了,比如apply()和call(),有像length和constructor这样的属性,函数对象的另外一个属性就是prototype。
函数只要一定义prototype属性就会被创建。它的初始值是一个空对象。
function MyFunction() { this.name = 'John'; this.age = 18; this.eat = function () { alert(this.name + ' ' + this.age); } } //“object” document.write(typeof MyFunction.prototype)
就好像你自己像下面这样添加了一个属性一样:
MyFunction.prototype={}
你可以给这个对象添加属性和方法。它们对函数MyFunction ()自己没有任何的影响。
它们只有在MyFunction ()作为一个构造器使用的时候有用,就是这个函数创建的对象可以使用。(就是这个函数创建的对象可以使用)
向原型添加属性和方法
给构造器函数的prototype属性添加方法和属性是另外一种利用构造器函数创建的对象添加功能的方法
//为原型prototype添加属性和方法 MyFunction.prototype.sex = '男'; MyFunction.prototype.run = function (){ alert('跑步!'); }
除了给原型对象添加内容,达到上面的目的的另外一种方式是完全重写原型,用你选择的一个对象来替换它。
//下面这种方式会把上面添加的属性和方法覆盖掉,就不能在访问了 MyFunction.prototype = { address: '广东省xxxxxxx', sleep: function () { alert('睡觉'); } }
使用原型的方法和属性
只要你用构造器创建了对象,所有添加到原型中的属性和方法都直接可以使用。
如果你用MyFunction构造器创建了一个对象my,你可以访问所有已经定义的方法和属性
function MyFunction() { this.name = 'John'; this.age = 18; this.eat = function () { alert(this.name + ' ' + this.age); } } //为原型prototype添加属性和方法 // MyFunction.prototype.sex = '男'; // MyFunction.prototype.run = function (){ // alert('跑步!'); // } //下面这种方式会把上面添加的属性和方法覆盖掉,就不能在访问了 MyFunction.prototype = { address: '广东省阳江市江城区', sleep: function () { alert('睡觉'); } } var my = new MyFunction(); //调用MyFunction的eat方法 my.eat(); //调用MyFunction的prototype的run方法的调用方式与调用MyFunction里的方法一样 //my.run(); my.sleep();
有一个需要注意的重点是原型是“活的”。在JavaScript 中对象是通过引用传值的,所以在每一个创建的对象实例中原型并没有被拷贝。
MyFunction.prototype.get = function (name) { return this[name]; } //即使my是在get()方法定义之前被创建的。my仍将可以访问到这个新的方法 alert(my.get('address'));
自己的属性对原型属性
在上面的例子中, get() 方法在内部用this 指向对象。它也可以用MyFunction.prototype 达到一样的结果:
MyFunction.prototype.get = function (name) { return MyFunction.prototype[name]; } //即使my是在get()方法定义之前被创建的。my仍将可以访问到这个新的方法 alert(my.get('address'));
原型链
当你试图访问my的属性,比方说my.name
JavaScript引擎将会查找对象名字name属性,如果发现了它,将返回它的值
接着脚本引擎定位到创建这个对象的构造器函数的原型(就像my.constructor.prototype一样)。
如果在原型属性中找到了属性,那么这个属性就会被使用
而原型就是一个对象,所以它也一定有一个构造器。
那么也就是它也有一个原型属性。
换句话说你可以:
最终,你将得到Object的toString()
用自己的属性重写原型属性
正如上面所阐述的,如果你的对象它自己没有某个属性,它可以使用原型链上的某处的属性(如果存在的话)。如果对象自己与原型都有一个名字相同的属性会发生什么?
function MyFunction(name) { this.name = name; } MyFunction.prototype.name = 'Jack'; var my = new MyFunction('John'); //此时的name属性取代了prototype的name属性 document.write(my.name); //删除name属性 delete my.name; //因为删除了name属性,所以访问的是prototype的name属性 document.write(my.name); //重新添加一个name属性 my.name = 'John'; //此时的name属性取代了prototype的name属性 document.write(my.name);
可枚举属性
并不是所有的属性会显示在for-in循环中,比如length和constructor 属性就不会显示。可以显示的属性称为可枚举的。
你可以利用每一个对象都有的propertyIsEnumerable()方法来检查哪一个属性是可枚举的。
function MyFunction(name, age) { this.name = name; this.age = age; this.say = function () { return '说话'; } } var my = new MyFunction('John', 18); //propertyIsEnumerable()方法来检查哪一个属性是可枚举的 //false document.write(my.propertyIsEnumerable('constructor') + "</br>"); //true document.write(my.propertyIsEnumerable('name') + "</br>");
注意:propertyIsEnumerable()方法对原型中的属性都会返回false,即使他们都是可枚举的并且能显示在for-in循环中。
function MyFunction(name, age) { this.name = name; this.age = age; this.say = function () { return '说话'; } } MyFunction.prototype.phone = '123141421'; MyFunction.prototype.sex = 'man'; var my = new MyFunction('John', 18); //false //propertyIsEnumerable()方法对原型中的属性都会返回false,即使他们都是可枚举的 document.write(my.propertyIsEnumerable('sex') + "</br>"); //true //当通过prototype的propertyIsEnumerable()方法是会返回ture document.write(my.constructor.prototype.propertyIsEnumerable('sex') + "</br>");
原型链中的属性也会枚举出来,只要他们是可枚举的。你可以用hasOwnProperty()方法来判断是属性是自己的还是原型中的。
//hasOwnProperty()方法判断是属于自己的还是属于原型的 for (var item in my) { if (my.hasOwnProperty(item)) { document.writeln(item + '=' + my[item] + "</br>"); } }
isPrototypeOf()
每一个对象也得到了一个isPrototypeOf()方法。这个方法告诉你指定的对象是否是作为其它对象的原型。
var Person = { name:'John', age:18 } function Man() { } //设置它的原型属性指向Person Man.prototype = Person; var man = new Man(); //这个方法告诉你指定的对象是否是作为其它对象的原型 document.write(Person.isPrototypeOf(man));
扩充内置对象
内置对象,比如构造器函数Array,String甚至Object和Function 都可以通过它们的原型进行扩充,也就说你可以给Array的原型添加方法。通过这种方式,添加的方法对所有的数组都可以使用
//检查inArray方法是否存在 if (!Array.prototype.inArray) { //为Array添加一个inArray方法 Array.prototype.inArray = function (param) { for (var item in this) { if (this[item] === param) { return true; } } return false; } } var arr = ['aa', 'bb', 'cc', 'dd', 'ee']; document.write(arr.inArray('aa')); document.write(arr.inArray('ff'));