@greenfavo
2015-12-14T12:46:21.000000Z
字数 3003
阅读 758
博客
javascript没有像java这种面向对象语言的类继承模型,而是基于原型的继承模型。对于原型模式,我们可以利用JavaScript特有的原型继承特性去创建对象的方式,也就是创建的一个对象作为另外一个对象的prototype属性值。原型对象本身就是有效地利用了每个构造器创建的对象,例如,如果一个构造函数的原型包含了一个name属性,那通过这个构造函数创建的对象都会有这个属性。
js有6种数据类型:Number,String,Boolean,Object,null,undefined,每个对象都有一个__proto__属性指向它的原型,于是该对象就获得了它原型对象上的一些属性和方法。如果没有为这个对象显式地设置原型对象,它的原型就是默认的原型对象。这是因为类型在使用方法时会转换为相应的对象。比如:
var a=1;a.__proto__;//Numbervar str="hello";str.__proto__;//Stringvar obj={};obj.__proto__;//Object
如果为一个对象设置了原型,那该对象就具有了原型对象的属性和方法。
var obj={x:1,y:2,add:function(){return this.x+this.y;}};var obj2={};obj2.__proto__=obj;obj2.add();//3obj2.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();//tomtom.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); // 10console.log(b.x); // 10function 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();//1c.x;//11c.y;//12c.z;//13
在上面的例子中,c的原型是b,b的原型是a,c中没有func方法,就到它的原型b中找,b也没有func方法,于是就到b的原型a中找,找啊找终于找到了func,于是就调用这个方法。这就形成了一条链,叫做原型链。