本篇文章参考了http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html
本篇文章参考了http://www.cnblogs.com/dolphinX/p/3286177.html
1.简介
prototype是原型的意思,JavaScript中没有“类”和“实例”的概念,更没有“父类”和“子类”的概念,它的继承全靠“原型链”的模式。
2.构造函数
构造函数内定义的变量和函数,如果不对外提供接口是访问不到的,我们称之为私有变量和私有函数。
通过this关键字使用this变量定义的变量和函数则是实例变量和实例函数,在用new创建实例时,this变量会绑定在实例对象上。
最后是通过在函数外使用函数名加”.”为其添加的属性和函数,这个通过函数对象本身可以访问得到,而通过该构造函数new出来的实例访问不到,我们称之为静态变量和静态函数。
function Animal(weight) {
var name = "动物"; //私有变量
var getName = function() { //私有方法
return name;
};
this.weight = weight; //实例变量
this.getWeight = function() { //实例方法
return this.weight;
};
}
Animal.color = "绿色"; //静态变量
Animal.getColor = function() { //静态方法
return Animal.color;
};
var animal = new Animal(10);
console.log(animal.name); //undefined
//console.log(animal.getName()); //报错,getName不是一个函数
console.log(animal.weight); //10
console.log(animal.getWeight()); //10
console.log(animal.color); //undefined
//console.log(animal.getColor()); //报错,getColor不是一个函数
console.log(Animal.color); //绿色
console.log(Animal.getColor()); //绿色
下面这个例子证明了多个实例的同名属性和同名方法不是同一个对象的引用,有多少个实例,就会有多少个kind数组副本,这造成了可共用的资源的浪费。使用prototype可以解决这个问题。每个函数都有保留属性prototype,其值是个对象,如果用该构造函数new了一个实例,那么该实例的保留属性__proto__(一个对象自动拥有的属性)的值就会是构造函数prototype属性的值。
一个对象的实例方法包括本地的(this指定的)和引用的(继承prototype),创建实例时,本地的每一个属性都是一个新的副本,而引用的每一个属性都是指向同一个对象。要注意的是,我们在修改某个实例的引用属性不要给它赋一个新的对象(除非你有理由这样做),否则该属性指向的对象就是新的对象,而不是prototype对象。
function Animal() {
this.kind = []; //之所以用数组是因为修改基本类型会被一个新对象覆盖,无法验证是否是同一个对象的引用
}
var first_animal = new Animal();
var second_animal = new Animal();
first_animal.kind.push(1);
second_animal.kind.push(2);
console.log(second_animal.kind); //[2]
function Animal() {
}
Animal.prototype.kind = [];
var first_animal = new Animal();
var second_animal = new Animal();
first_animal.kind.push(1); //如果这里改成first_animal.kind = [1],那么first_animal.kind指向的就不是原来的那个对象了(也可以说是为first_animal新建了属性kind,与prototype对象的kind已经无关了),而是新对象[1]。
second_animal.kind.push(2);
console.log(second_animal.kind); //[1,2]
3.prototype chain
当读取某个对象的某个属性时,javaScript解析引擎会执行一遍搜索。首先是该对象的实例的属性,找到则返回,没找到则查找该对象的__proto__指向的prototype对象,如果没找到则继续递归prototype对象的__proto__属性,直到Object对象的__proto__指向的prototype对象为止。这就是prototype chain。
下面一个例子展现了最常见的使用prototype实现的继承。
function Animal() {
this.name = "动物";
this.kind = ["鱼","老虎","狮子"];
this.color = "绿色";
}
function Fish() {
this.name = "鱼";
this.kind = ["金鱼","鲤鱼"];
}
function GoldFish() {
this.name = "金鱼";
}
Animal.prototype.extra = "ABC";
Fish.prototype = new Animal(); //注意,这一句必须写在GoldFish.prototype=newFish()之前,因为下面那句会使goldfish.prototype被覆盖,而上面的不用是因为它只是修改了属性,没有被覆盖
GoldFish.prototype = new Fish();
var goldfish = new GoldFish();
console.log(goldfish.name); //金鱼
console.log(goldfish.kind); //["金鱼","鲤鱼"]
console.log(goldfish.color); //绿色
console.log(goldfish.extra); //ABC
4. prototype验证方法
- isPrototypeOf() 判断某个prototype对象是否某个实例的原型。
- hasOwnProperty() 判断某个属性是本地属性还是引用属性。
- in运算符 判断某个实例是否含有某个属性,包括本地属性和引用属性。
function Animal() {
this.name = "动物";
this.kind = ["鱼","老虎","狮子"];
this.color = "绿色";
}
function Fish() {
this.name = "鱼";
this.kind = ["金鱼","鲤鱼"];
}
function GoldFish() {
this.name = "金鱼";
}
Animal.prototype.extra = "ABC";
Fish.prototype = new Animal(); //注意,这一句必须写在GoldFish.prototype=newFish()之前
GoldFish.prototype = new Fish();
var goldfish = new GoldFish();
console.log(GoldFish.prototype.isPrototypeOf(goldfish)); //true
console.log(Fish.prototype.isPrototypeOf(goldfish)); //true
console.log(Animal.prototype.isPrototypeOf(goldfish)); //true
console.log(Object.prototype.isPrototypeOf(goldfish)); //true
console.log(goldfish.hasOwnProperty("name")); //true
console.log(goldfish.hasOwnProperty("kind")); //false
console.log(goldfish.hasOwnProperty("color")); //false
console.log(goldfish.hasOwnProperty("extra")); //false
console.log("name" in goldfish); //true
console.log("kind" in goldfish); //true
console.log("color" in goldfish); //true
console.log("extra" in goldfish); //true