Prototype :和java 类中定义static方法一样,表示类方法,所有对象使用同一方法。
java中的原型:就像电影里一个真人,然后有许多真人复制品。
就是用new建立一个对象(原xi),然后建立其它对象不用new,直接复制以前建立的属性和方法。
因此java和javascript的Prototype不一样。
一、构造函数的问题
1、建立一个实例,相应的建立一个方法.因此不同的实例,同名的方法名,但是方法的实例不一样。
尽管构造函数模式是有用的,但是有一些缺点。 主要的缺点,建立一个实例,相应的建立一个方法. 记住,函数在in ECMAScript里是对象, 因此,每当一个函数被定义,实际上被实例化了一个对象。逻辑上, 构造器看起来像这样:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function(“alert(this.name)”); //logical equivalent
//this.sayName = function(){ alert(this.name); }
}
类:Function:
实例:new Function
构造函数:Function(“ ”);
上面表示定义一个Function的实例,哪么
var person1 = new Person(“Nicholas”, 29, “Software Engineer”);
var person2 = new Person(“Greg”, 27, “Doctor”);
new Person 一次,sayName ()实例一次,所以同名的函数sayName ()的实例对象不一样。
alert(person1.sayName == person2.sayName); //false做同一件事情,有两个Function实例是无意义的,所以大家都希望建立一个类的无数对象,可以只用一个方法。
2、把函数写在构造函数外面,哪么多个实例只用一个方法,但是缺点是外面有可能写很多方法。
例子:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);g;;;
}
var person1 = new Person(“Nicholas”, 29, “Software Engineer”);
var person2 = new Person(“Greg”, 27, “Doctor”);
在这个例子中,sayName() 函数时定义在构造函数外面. 在构造函数里面sayName 属性等于全局 sayName() 函数. 因此sayName属性现在包含一个指向函数的指针,
person1, person2 共享 在全局作用域里的sayName() 函数 . 这个解决了上面做同一件事情重复函数的问题,
但是 也不好.如果对象需要多个方法,需要建立多个全局函数, 这些问题由Prototype Pattern 解决
二、The Prototype Pattern:就是解决多个实例使用同一个方法的问题。
用一个prototype 属性建立每一个函数,包含属性和方法。一旦构造函数被调用, 对象的原型被建立
使用prototype 的好处是所有的properties和methods 是被所有实例共享,例子
1、prototype的建立
例如:Person:大写表示是一个类,用prototype定义其公共属性和方法。
发现这种方法,只建立一个Person prototype原型。
function Person(){ } c.name = “Nicholas”; Person.prototype.age = 29; Person.prototype.job = “Software Engineer”; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); //”Nicholas” var person2 = new Person(); person2.sayName(); //”Nicholas” alert(person1.sayName == person2.sayName); //true |
上面,属性和sayName()方法时直接加到Person的prototype属性上,留下空的构造函数. 上面person1,person2 实例调用同一方法,结果一样。
2、Prototypes 工作过程:
每建立一个函数,它的 prototype 属性同时建立,同时建立一个Person Prototype类, 所有的Person prototypes自动的得到一个constructor属性,值指向类Person的prototype属性. 在上面例子中, 例如,Person.prototype.constructor指向Person.
当定义一个自己的构造函数Person 时,Person prototype仅默认的得到constructor属性,所有其它方法来自父类Object.每次构造函数被调用去建立一个新实例,实例有一个有一个内部指针指向属性 constructor.
实例(person1,person2)和Person prototype直接相连
但是实例(person1,person2)和构造函数(Person)不相连。
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //”Nicholas”
Person.prototype.isPrototypeOf(obj)方法:判断person1的实例是否和Person prototype对象相连
Object.getPrototypeOf(person1) :返回person1里面[[Prototype]]值。
2.1、查找对象属性的过程
调用 person1.sayName()时, 有两步,
第一: the JavaScript engine 问, “实例 person1,你有一个叫sayName属性吗?” no,因此它继续别找边问,
第二:Person Prototype,你有一个叫sayName属性吗?”,yes,因此存贮在prototype 的函数被访问。
person2.sayName()被调用, 会得到同样的结果,在多个对象中, 利用prototypes会共享属性和方法。
3、解决实例属性和prototype属性名字一样的问题。
function Person(){ } Person.prototype.name = “Nicholas”; Person.prototype.age = 29; Person.prototype.job = “Software Engineer”; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = “Greg”; alert(person1.name); //”Greg” - from instance alert(person2.name); //”Nicholas” - from prototype |
因为实例person1查找name时,从实例对象开始找,找到,则停止
person2没找到,去Prototype里面去找。
可以删除person1中属性name,
delete person1.name;
alert(person1.name); //”Nicholas” - from the prototype
alert(person1.hasOwnProperty(“name”)); //false,实例1是否建立属性name.
person1.hasOwnProperty(“name”)
方法:
1、hasOwnProperty:只在实例person1上有用
2、“name” in person1:在实例person1上有用,在Prototype 上也有用,所以只要name存在一方,会显示true
3、 一个对象的属性是否存在prototype 上,通过 hasOwnProperty()和in结合来判断。
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
4、Object.keys(p1):得到p1实例的所有属性和方法名
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys); //”constructor,name,age,job,sayName”
function Person(){ } Person.prototype.name = “Nicholas”; Person.prototype.age = 29; Person.prototype.job = “Software Engineer”; Person.prototype.sayName = function(){ alert(this.name); }; var p1 = new Person(); p1.name = “Rob”; p1.age = 31; var p1keys = Object.keys(p1); alert(p1keys); //”name,age” |
四、上面prototype重复太多,声明简单写法 ,只有一个prototype,
function Person(){ } Person.prototype = { name : “Nicholas”, age : 29, job : “Software Engineer”, sayName : function () { alert(this.name); } }; |
上面有一个异常: the constructor 属性不再指向 Person对象。
所以你不能用constructor 来确定对象类型
var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); //true
1、更改方法1,直接加属性,如果你需要constructor来判断对象类型
function Person(){ } Person.prototype = { constructor: Person, name : “Nicholas”, age : 29, job : “Software Engineer”, sayName : function () { alert(this.name); } }; |
function Person(){ } Person.prototype = { name : “Nicholas”, age : 29, job : “Software Engineer”, sayName : function () { alert(this.name); } }; //ECMAScript 5 only – restore the constructor Object.defineProperty(Person.prototype, “constructor”, { enumerable: false, value: Person }); |
五、 Dynamic Nature of Prototypes
但是Person Prototype 出现两个,一个是Person Prototype,一个是新的Person Prototype。
var friend= new Person(); Person.prototype.sayHi = function(){ alert(“hi”); }; friend.sayHi(); //”hi” - works! |
类:Person
实例:friend
原型:Person.prototype
流程:当 friend.sayHi() 被调用时,执行new Person();。建立Person,Person Prototype,实例friend,
1、浏览器去实例friend找属性sayHi,没有找到,出现错误。
2、然后执行重写Person Prototype,建立一个新Person Prototype,
function Person(){ } var friend = new Person(); Person.prototype = {//这是建立固定值对象的标准方式,会建立新Person Prototype原型。 constructor: Person, name : “Nicholas”, age : 29, job : “Software Engineer”, sayName : function () { alert(this.name); } }; friend.sayName(); //error |
在这个例子中,当调用 friend.sayName() 时,一个新的Person 实例被建立,然后prototype objec是重写。
这将引起一个错误,因为friend 指向的原型并没有包含name属性,
举例如下:
1、在原型赋值前,建立Person实例friend ,自动建立一个Person Prototype 原型,属性值为空。
2、在原型赋值后,比上面多一个新的Person Prototype 原型,以前的指向变化的是,Person的prototype属性改指向new Person Prototype。而new Person Prototype里面的constructor 指向Person构造函数,我喜欢叫Person类。
在构造函数上重写属性意味着新的实例将引用新的prototype ,而以前的实例指向不变。
六、Combination Constructor/Prototype Pattern
这也许是最好的一种方式,和一个java类一样,分类级别(所有对象共享属性和方法)的和实例级别(属性和方法给特定的对象用)。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = [“Shelby”, “Court”]; } Person.prototype = { constructor: Person, sayName : function () { alert(this.name); } }; var person1 = new Person(“Nicholas”, 29, “Software Engineer”); var person2 = new Person(“Greg”, 27, “Doctor”); person1.friends.push(“Van”); alert(person1.friends); //”Shelby,Court,Van” alert(person2.friends); //”Shelby,Court” alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true |