当前位置: 代码迷 >> Web前端 >> 犀牛书札记:(11)Classes, Constructors and Prototypes
  详细解决方案

犀牛书札记:(11)Classes, Constructors and Prototypes

热度:492   发布时间:2012-09-04 14:19:30.0
犀牛书笔记:(11)Classes, Constructors and Prototypes

在JS中创建对象往往使用new xxx()的方法,其中xxx被称为构造函数,实际过程是由new操作符创建一个空对象然后由后面的xxx方法对该对象进行初始化。在xxx方法中,可以使用this关键字引用到。通过定义构造方法,就定义了一个类。在这种语境下,构造方法的命名通常不同于一般的方法命名(动宾)而用类命名的方式。构造函数通常没有返回值,如果有返回值,返回值将会代替之前用new生成的对象

?

如果构造函数返回元数据,对前面初始化的对象没有影响,被赋值的变量还是可以引用到新生成的对象。只有在返回对象时,才会代替返回对象。

?

function Shape(){
       this.x = 100;
       this.y = 100;
       return new String("this is not the object you are expecting");
}

function Shape1(){
      this.x = 100;
      this.y = 100;
      return 3;
}

function test(){
      var temp = new Shape();
      alert(temp.x);//return undefined.

      var temp1 = new Shape1();
      alert(temp1.x);//return 100
}

?

当函数被当作方法使用时,函数内的this关键字引用的是作为方法所有者的对象

?

?

function Rectangle(w, h){
     this.width = w;
     this.height = h;
     this.area = function(){
           return this.width * this.height;
     }
}

?

?使用普通属性作为对象方法的效率是很低的。

?

每一个JS对象都包括一个内部引用指向另一个对象,成为prototype对象

如果一个对象是某个对象的PROTOTYPE,那么所有这个对象的属性都将会被把它作为prototype的对象继承

?

被构造方法初始化的对象的prototype就是构造方法的prototype对象

?

初始的prototype对象只有一个属性--constructor,指回这个prototype关联的构造器方法

任何prototype对象的属性将被作为该构造器生成的对象的属性(这就是为什么所有对象都有一个constructor属性的原因)

?

prototype的这种特性使得它成为设置方法,和共有常数的理想位置

?

另外一个用途是,可以通过prototype生成一个子类:

?

function heir(p){
    function f(){};
    f.prototype = p;
    return new f();
}
?

这种继承关系是在查找一个属性时自动发生的,这些被继承的属性并不是在创建新对象时复制到新对象中的。这种设计节约了很多内存空间,另一个特性是,即使在一个继承对象创建之后,对原prototype对象新增的方法见依然可以在被创建对象中得到体现。

?

继承的属性和普通属性一样,都可以用for/in语句遍历到,只有通过hasOwnProperty()方法才能够识别是不是prototype的属性。

?

?

function Rectangle(w,h){
    this.width = w;
    this.height = h;
}

Rectangle.prototype.area = function(){return this.width*this.height;};

....
var temp = new Rectangle(100,100);
alert(temp.hasOwnProperty("area"));//false;
alert("area" in temp);//true;
?

当读取某个对象的某个属性时,JS先检查该对象是否有该属性,再检查该对象的prototype对象是否有该属性。

当写入某个对象的某个属性时,JS将不使用prototype属性。


这就是说,继承只有在读取对象属性时才发生

如果一个对象o的Prototype有一个属性k,那么o.k =4;将会先在o中寻找是否有一个属性叫k,如果没有就创建一个,并不会到prototype中去寻找

?

一般的built-in对象比如String,Date,都可以修改其prototype,但是有些对象,比如浏览器相关的对象,就不具备这种能力

?

?

修改built-in对象的一大作用就是,对某些浏览器没有实现的方法和功能,可以通过修改prototype对象来自己实现。

?

在JS中模拟类

每一个在constructor中创建并初始化的属性都是instance property.

创建一个instance method,就是通过设置constructor的prototype实现的

和JAVA重要的不同是,在JAVA类中,this关键字是可以隐藏的,而JS中,必须显式的使用this关键字

定义一个属于constructor的属性来模拟class property.

function Shape(){
      this.x = 100;
      this.y = 100;
}

Shape.UNIT = 200;
....

var temp = new Shape();
alert(temp.UNIT);//returns undefined.
alert(Shape.UNIT);//returns 200;

?同样也可以定义一个属于constructor的方法来模拟class method.

?

?

以下是完整的定义了一个类Circle

function Circle(radius){
   this.r= radius;//instance property;
}

Circle.PI = 3.14;//class property;

Circle.prototype.area = function() { return Circle.PI * this.r * this.r;}
//instance method;

//class method
Circle.max = function(a,b){
       if ( a.r > b.r ) return a;
      else return b;
}
?

private member

要实现封装,就需要用到闭包,但要这样做,访问方法只能存在每个对象实例中,不能从Prototype对象继承

function ImmutableRectangle(w,h){
    this.getWidth = function() {return w;}
    this.getHeight = function() {return h;}
}

ImmutableRectangle.prototype.area = function(){
    return this.getWidth() * this.getHeight();
};
?

通用对象方法:

  1. toString()
    Circle.prototype.toString = function(){.....}
  2. valueOf()
    在需要将该对象转换成元数据类型时调用。
  3. 比较方法(== < >。。。)
    JS中的比较符比较的是引用而非内容,所以有必要实现自己的equals()方法。如果试图使用<.>这种符号,js将试图调用该对象的valueOf()方法,将对象转换成元对象再比较。

?

父类和子类:

JS中Object是所有类的父类

对一个对象属性的搜索路径是,先搜索该对象本身,再搜索该对象构造器的prototype,再搜索object的prototype

?

继承Rectangle类:

  1. 定义一个子类构造器PositionedRectangle
    function PositionedRectangle(w,h,x,y){
        Rectangle.call(this,w,h);//调用父类构造器
        this.x = x;
        this.y = y;
    }
    ?
  2. 继承父类prototype
    function heir(p){
         function f(){}'
         f.prototype = p;
         return new f();
    }
    
    PositionedRectangle.prototype = heir(Rectangle.prototype);
    ?

调用父类被覆盖的方法:

Rectangle.prototype.toString.call(this);

之所以用call,是因为无法确定toString指向的对象。

?

?

JS扩展的另一种方式(不使用继承)

直接把属性和方法拷贝到新的类

可以直接通过拷贝/borrow的方式实现多重继承:

function Colored(c){ this.color = c;}
Colored.prototype.getColor = function(){ return this.color;}

function ColoredRectangle(x,y,w,h,c){
     Rectangle.call(this,x,y,w,h);
     Colored.call(this,c);
}

ColoredRectangle.prototype = heir(Rectangle.prototype);
ColoredRectangle.prototype.constructor = ColoredRectangle;

borrowMethods(Colored, ColoredRectangle);

?注意这里prototype.constructor需要重新设置,否则将是父类的构造器.

?

?

?

typeof null是object

typeof undefined是undefined.

typeof function是function

用instanceof来判断到底是哪种对象

?

如果需要测试某个对象属于具体哪个类,可以使用if( d.constructor == xxxx)

?

对于built-in对象,toString()方法可以反映对象的类型信息

?

DUCK Typing

if it walks like a duck and quacks like a duck, it's a duck!

如果一个对象拥有一个类的所有方法/属性,那么就说它是那个类的一个实例

?

?

?

?

?

?

?

?

?

?

?

?

  相关解决方案