[关闭]
@greenfavo 2015-12-14T20:46:21.000000Z 字数 3003 阅读 674

js原型与原型链

博客


javascript没有像java这种面向对象语言的类继承模型,而是基于原型的继承模型。对于原型模式,我们可以利用JavaScript特有的原型继承特性去创建对象的方式,也就是创建的一个对象作为另外一个对象的prototype属性值。原型对象本身就是有效地利用了每个构造器创建的对象,例如,如果一个构造函数的原型包含了一个name属性,那通过这个构造函数创建的对象都会有这个属性。

原型

js有6种数据类型:Number,String,Boolean,Object,null,undefined,每个对象都有一个__proto__属性指向它的原型,于是该对象就获得了它原型对象上的一些属性和方法。如果没有为这个对象显式地设置原型对象,它的原型就是默认的原型对象。这是因为类型在使用方法时会转换为相应的对象。比如:

  1. var a=1;
  2. a.__proto__;//Number
  3. var str="hello";
  4. str.__proto__;//String
  5. var obj={};
  6. obj.__proto__;//Object

如果为一个对象设置了原型,那该对象就具有了原型对象的属性和方法。

  1. var obj={
  2. x:1,
  3. y:2,
  4. add:function(){
  5. return this.x+this.y;
  6. }
  7. };
  8. var obj2={};
  9. obj2.__proto__=obj;
  10. obj2.add();//3
  11. obj2.add=function(m,n){
  12. return m+n+1;
  13. }
  14. obj2.add(1,3);//5

通过这样的方式obj2就获得了obj的属性和方法,这就实现了基于原型的继承。当然obj2也可以覆盖obj中的同名方法和属性。

构造函数

构造函数是一种特殊的函数,一般首字母大写,用new关键字可以创建并初始化一个对象。

  1. function People(name,age){//构造函数
  2. this.name=name;
  3. this.age=age;
  4. }
  5. People.prototype={//原型对象
  6. getName:function(){
  7. console.log(this.name);
  8. },
  9. getAge:function(){
  10. console.log(this.age);
  11. },
  12. setAge:function(age){
  13. this.age=age;
  14. }
  15. }
  16. var tom=new People("tom",12);
  17. tom.getName();//tom
  18. tom.setAge(13);
  19. tom.getAge();//13

当调用构造函数创建一个实例时,实例内部将包含一个内部指针__proto__指向构造函数的prototype,这个连接存在于实例和构造函数的prototype之间,而不是实例与构造函数。

  1. function Person(name){ //构造函数
  2. this.name=name;
  3. }
  4. Person.prototype.printName=function() //原型对象
  5. {
  6. console.log(this.name);
  7. }
  8. var person1=new Person('tom');//实例化对象
  9. console.log(person1.__proto__);//Person{}
  10. console.log(person1.constructor);//function Person (name)
  11. console.log(Person.prototype);//指向原型对象Person

Person的实例person1中包含了name属性,同时自动生成一个__proto__属性,该属性指向Person的prototype,可以访问到prototype内定义的printName方法。

每个js函数都有prototype属性,这个属性引用了一个对象,这个对象就是原型。原型对象初始化的时候是空的,我们可以在这里面定义任何属性和方法,这些方法和属性都将被该构造函数所创建的对象继承。

prototype与__proto__区别

__proto__是普通对象的隐士属性,在new的时候会指向prototype所指的对象,而prototy则是属于构造函数的属性。

构造函数,实例和原型对象的区别

实例就是通过构造函数创建的。实例一创造出来就具有constructor属性(指向构造函数)和__proto__属性(指向原型对象)。

构造函数中有一个prototype属性,这个属性是一个指针,指向它的原型对象。

原型对象内部也有一个指针(constructor属性)指向构造函数:

  1. Person.prototype.constructor = Person;

实例可以访问原型对象上定义的属性和方法。
这里的perosn1就是实例,prototype是它的原型对象。

new操作符

  1. function Base(){
  2. this.id="base";
  3. }
  4. var obj=new Base();

new 操作符具体干了什么呢?其实就干了三件事。

  1. var obj={};//创建一个空对象
  2. obj._proto_=Base.prototype;//将它的__proto__属性指向原型对象
  3. Base.call(obj);//将上下文环境设为该对象

对象独立于构造函数

之前创建的对象并不会因为后来构造函数的改变而改变。

  1. function A() {}
  2. A.prototype.x = 10;
  3. var a = new A();
  4. console.log(a.x); // 10
  5. // 设置A为null - 显示引用构造函数
  6. A = null;
  7. // 但如果constructor属性没有改变的话,
  8. // 依然可以通过它创建对象
  9. var b = new a.constructor();
  10. console.log(b.x); // 10
  11. // 隐式的引用也删除掉
  12. delete a.constructor.prototype.constructor;
  13. delete b.constructor.prototype.constructor;
  14. // 通过A的构造函数再也不能创建对象了
  15. // 但这2个对象依然有自己的原型
  16. console.log(a.x); // 10
  17. console.log(b.x); // 10
  18. function A(){
  19. this.x=1;
  20. }
  21. var a=new A();
  22. a.x;//a

原型链

原型链:当从一个对象那里调取属性或方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象那里找。如果prototype没有,就会去prototype关联的前辈prototype那里找,如果再没有则继续查找Prototype.Prototype引用的对象,以此类推,直到prototype为undefined(Object的Prototype就是undefined)从而形成了所谓的原型链。
在ECMAScript中,实现继承的方法就是依靠原型链实现的。

  1. var a={
  2. x:11,
  3. func:function(){
  4. console.log(1);
  5. }
  6. }
  7. var b={
  8. y:12,
  9. __proto__:a
  10. };
  11. var c={
  12. z:13,
  13. __proto__:b
  14. };
  15. c.func();//1
  16. c.x;//11
  17. c.y;//12
  18. c.z;//13

在上面的例子中,c的原型是b,b的原型是a,c中没有func方法,就到它的原型b中找,b也没有func方法,于是就到b的原型a中找,找啊找终于找到了func,于是就调用这个方法。这就形成了一条链,叫做原型链。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注