@greenfavo
2015-12-14T20:46:21.000000Z
字数 3003
阅读 674
博客
javascript没有像java这种面向对象语言的类继承模型,而是基于原型的继承模型。对于原型模式,我们可以利用JavaScript特有的原型继承特性去创建对象的方式,也就是创建的一个对象作为另外一个对象的prototype属性值。原型对象本身就是有效地利用了每个构造器创建的对象,例如,如果一个构造函数的原型包含了一个name属性,那通过这个构造函数创建的对象都会有这个属性。
js有6种数据类型:Number,String,Boolean,Object,null,undefined,每个对象都有一个__proto__属性指向它的原型,于是该对象就获得了它原型对象上的一些属性和方法。如果没有为这个对象显式地设置原型对象,它的原型就是默认的原型对象。这是因为类型在使用方法时会转换为相应的对象。比如:
var a=1;
a.__proto__;//Number
var str="hello";
str.__proto__;//String
var obj={};
obj.__proto__;//Object
如果为一个对象设置了原型,那该对象就具有了原型对象的属性和方法。
var obj={
x:1,
y:2,
add:function(){
return this.x+this.y;
}
};
var obj2={};
obj2.__proto__=obj;
obj2.add();//3
obj2.add=function(m,n){
return m+n+1;
}
obj2.add(1,3);//5
通过这样的方式obj2就获得了obj的属性和方法,这就实现了基于原型的继承。当然obj2也可以覆盖obj中的同名方法和属性。
构造函数是一种特殊的函数,一般首字母大写,用new关键字可以创建并初始化一个对象。
function People(name,age){//构造函数
this.name=name;
this.age=age;
}
People.prototype={//原型对象
getName:function(){
console.log(this.name);
},
getAge:function(){
console.log(this.age);
},
setAge:function(age){
this.age=age;
}
}
var tom=new People("tom",12);
tom.getName();//tom
tom.setAge(13);
tom.getAge();//13
当调用构造函数创建一个实例时,实例内部将包含一个内部指针__proto__指向构造函数的prototype,这个连接存在于实例和构造函数的prototype之间,而不是实例与构造函数。
function Person(name){ //构造函数
this.name=name;
}
Person.prototype.printName=function() //原型对象
{
console.log(this.name);
}
var person1=new Person('tom');//实例化对象
console.log(person1.__proto__);//Person{}
console.log(person1.constructor);//function Person (name)
console.log(Person.prototype);//指向原型对象Person
Person的实例person1中包含了name属性,同时自动生成一个__proto__属性,该属性指向Person的prototype,可以访问到prototype内定义的printName方法。
每个js函数都有prototype属性,这个属性引用了一个对象,这个对象就是原型。原型对象初始化的时候是空的,我们可以在这里面定义任何属性和方法,这些方法和属性都将被该构造函数所创建的对象继承。
__proto__是普通对象的隐士属性,在new的时候会指向prototype所指的对象,而prototy则是属于构造函数的属性。
实例就是通过构造函数创建的。实例一创造出来就具有constructor属性(指向构造函数)和__proto__属性(指向原型对象)。
构造函数中有一个prototype属性,这个属性是一个指针,指向它的原型对象。
原型对象内部也有一个指针(constructor属性)指向构造函数:
Person.prototype.constructor = Person;
实例可以访问原型对象上定义的属性和方法。
这里的perosn1就是实例,prototype是它的原型对象。
function Base(){
this.id="base";
}
var obj=new Base();
new 操作符具体干了什么呢?其实就干了三件事。
var obj={};//创建一个空对象
obj._proto_=Base.prototype;//将它的__proto__属性指向原型对象
Base.call(obj);//将上下文环境设为该对象
之前创建的对象并不会因为后来构造函数的改变而改变。
function A() {}
A.prototype.x = 10;
var a = new A();
console.log(a.x); // 10
// 设置A为null - 显示引用构造函数
A = null;
// 但如果constructor属性没有改变的话,
// 依然可以通过它创建对象
var b = new a.constructor();
console.log(b.x); // 10
// 隐式的引用也删除掉
delete a.constructor.prototype.constructor;
delete b.constructor.prototype.constructor;
// 通过A的构造函数再也不能创建对象了
// 但这2个对象依然有自己的原型
console.log(a.x); // 10
console.log(b.x); // 10
function A(){
this.x=1;
}
var a=new A();
a.x;//a
原型链:当从一个对象那里调取属性或方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象那里找。如果prototype没有,就会去prototype关联的前辈prototype那里找,如果再没有则继续查找Prototype.Prototype引用的对象,以此类推,直到prototype为undefined(Object的Prototype就是undefined)从而形成了所谓的原型链。
在ECMAScript中,实现继承的方法就是依靠原型链实现的。
var a={
x:11,
func:function(){
console.log(1);
}
}
var b={
y:12,
__proto__:a
};
var c={
z:13,
__proto__:b
};
c.func();//1
c.x;//11
c.y;//12
c.z;//13
在上面的例子中,c的原型是b,b的原型是a,c中没有func方法,就到它的原型b中找,b也没有func方法,于是就到b的原型a中找,找啊找终于找到了func,于是就调用这个方法。这就形成了一条链,叫做原型链。