JavaScript中的prototype很容易使初学者迷惑不解,我也刚开始学习这门语言,写了一小段代码帮助理解。(千言万语,尽在注释中:)
1、首先是一个简单的函数来判断是否是JavaScript中的对象。JavaScript中的数据类型分为原始类型和对象类型(包括函数)。
1 function isObject(object) { 2 3 //注意,undefined == null,过滤掉null和undefined 4 if(object == null) throw TypeError(); 5 6 //object的类型,过滤掉原始类型如数字,字符串和布尔值 7 var type = typeof object; 8 9 if(type !== "object" && type !== "function") throw TypeError(); 10 11 //object是一个对象 12 return true; 13 }
2、实现一个继承方法,返回一个子对象。
1 function inherit(Base) { 2 3 //Base是一个对象 4 if(isObject(Base)) { 5 6 //Object.create是ES5提出的一种新的对象创建方式,如果存在此方法,则直接返回结果。 7 if(Object.create) 8 return Object.create(Base); 9 10 //一个空构造函数,用于构造Sub对象 11 function Sub() {} 12 13 //将Sub的prototype属性设置为Base,每一个JavaScript的都有这个属性 14 //Sub便"拥有"Base中的属性和方法,如果Sub还有它的自对象,那属于Sub以 15 //及Sub父对象的属性和方法也被继承下来,这就是原型链(prototype chain) 16 Sub.prototype = Base; 17 18 //返回一个Base的子对象,即Sub 19 return new Sub(); 20 } 21 22 }
3,Eva继承自Person,其中我们通过Eva.name="Eva"给她重新取了个名字,此时程序将打印"Hello, Eva"。(Person.greeting = ...此时写在Person对象里面和外面并无区别,一会在讨论对象的大小时会谈到。)
1 /** 2 * Person是一个对象字面量,定义了属性name和方法greeting; 3 * @type {{name: string, greeting}} 4 */ 5 var Person = {name:"cnblogs"}; 6 7 Person.greeting = function() { 8 console.log("Hello, " + this.name); 9 }; 10 11 //Eva继承自Person对象,它将Person的属性和方法都继承过来 12 var Eva = inherit(Person); 13 14 //name继承自Person,将Eva的名字重写为"Eva" 15 Eva.name = "Eva"; 16 17 //greeting继承自Person 18 Eva.greeting();
4、更常用和高效的一种方式,此时程序将打印"Hello, John"。
1 /** 2 * 通过定义构造器来创建Person2类对象,定义了属性name和原型方法greeting; 3 * 4 * @param name 5 * @constructor 6 */ 7 var Person2 = function(name) { 8 this.name = name; 9 }; 10 11 //"动态"给Person2添加一个greeting方法,此方法并不真正属于Person2,而 12 //属于Person2.prototype,这样就实现了给Person2及其子类对象添加方法,但 13 //并不占用多的内存空间。 14 Person2.prototype.greeting = function() { 15 console.log("Hello, " + this.name); 16 }; 17 18 var John = inherit(new Person2("John")); 19 20 John.greeting();
5、此时,如果我们有一个返回对象大小的函数,我们就更能明白prototype在实现继承时是怎么一回事了。
1 /** 2 * 返回一个对象的大小(在这里是一个对象真正占有的属性和方法的个数) 3 * @param object 4 * @returns {number} 5 */ 6 function objectSize(object) { 7 8 var size = 0; 9 10 //这里直接使用了Object的hasOwnProperty方法(注意Object是大写) 11 //Object.prototype.hasOwnProperty = function(propertyName) {}; 12 var hasOwnProperty = Object.prototype.hasOwnProperty; 13 14 //循环遍历object中的所有能"访问"的属性和方法 15 //注意能"访问"并不等于拥有 16 for(var key in object) { 17 18 //将hasOwnProperty函数绑定到object(this)上进行调用 19 //Function.prototype.call = function(thisArg,args) {}; 20 if(hasOwnProperty.call(object, key)) { 21 22 size++; 23 24 } 25 } 26 //返回真正属于object的属性和方法个数 27 return size; 28 }
6、所以在实际编程中,往往只将属性写在对象内部,而使用prototype添加各种方法给本对象和子对象使用,但是这些方法在内存中只有一份!(是不是联想到静态成员函数?)
1 //继承(通过prototype添加)来的不属于我!! 2 3 //程序将打印"2",Person真正拥有name和greeting 4 console.log("Person size: " + objectSize(Person)); 5 6 //程序将打印"1",Eva真正拥有的是重写name属性,如果不重写,将打印"0" 7 console.log("Eva size: " + objectSize(Eva)); 8 9 //程序将打印"1",Person2真正拥有的只是name属性 10 console.log("Person2 size: " + objectSize(new Person2("John"))); 11 12 //程序将打印"0",John啥都没有,都是从"别人"借过来的 13 console.log("John size: " + objectSize(John));
未完待续...