OO --- JS

来源:互联网 发布:数控外圆锥度编程 编辑:程序博客网 时间:2024/04/28 21:29

每一个函数都包含了一个prototype属性,这个属性指向了一个prototype对象. 注意不要搞混了. 

构造函数: 
new操作符用来生成一个新的对象.new后面必须要跟上一个函数,也就是我们常说的构造函数.构造函数的工作原理又是怎样的呢? 
先看一个例子: 

Js代码 
function Person(name,sex){   
   this.name =name;   
   this.sex =sex;   
}   
var per = newPerson("sdcyst","male");   
alert("name:"+per.name+"_sex:"+per.sex);//name:sdcyst_sex:male  

function Person(name,sex){ 
   this.name = name; 
   this.sex = sex; 

var per = newPerson("sdcyst","male"); 
alert("name:"+per.name+"_sex:"+per.sex);//name:sdcyst_sex:male 

下面说明一下这个工作的步骤: 
开始创建了一个函数(不是方法,只是一个普通的函数),注意用到了this关键字.以前我们提到过this关键字表示调用该方法的对象,也就 
是说通过对象调用"方法"的时候,this关键字会指向该对象(不使用对象直接调用该函数则this指向整个的script域,或者函数所在的域,在此我们不做详细的讨论).当我们使用new操作符时,javascript会先创建一个空的对象,然后这个对象被new后面的方法(函数)的this关键字引用!然后在方法中通过操作this,就给这个新创建的对象相应的赋予了属性.最后返回这个经过处理的对象.这样上面的例子就很清楚:先创建一个空对象,然后调用Person方法对其进行赋值,最后返回该对象,我们就得到了一个per对象. 

prototype(原型)--在这里会反复提到"原型对象"和"原型属性",注意区分这两个概念. 
在javascript中,每个对象都有一个prototype属性,这个属性指向了一个prototype对象. 
上面我们提到了用new来创建一个对象的过程,事实上在这个过程中,当创建了空对象后,new会接着操作刚生成的这个对象的prototype属性. 每个方法都有一个prototype属性(因为方法本身也是对象),new操作符生成的新对象的prototype属性值和构造方法的prototype属性值是一致的.构造方法的prototype属性指向了一个prototype对象,这个prototype对象初始只有一个属性constructor,而这个constructor属性又指向了prototype属性所在的方
有点晕,看下面的图: 

 


这样,当用构造函数创建一个新的对象时,它会获取构造函数的prototype属性所指向的prototype对象的所有属性.对构造函数对应的prototype对象所做的任何操作都会反应到它所生成的对象身上,所有的这些对象共享构造函数对应的prototype对象的属性(包括方法). 
看个具体的例子吧: 

Js代码 
function Person(name,sex){ //构造函数   
   this.name =name;   
   this.sex =sex;   
}   
Person.prototype.age =12;  //为prototype属性对应的prototype对象的属性赋值   
Person.prototype.print =function() {//添加方法   
   alert(this.name+"_"+this.sex+"_"+this.age);   
};   
  
var p1 = newPerson("name1","male");   
var p2 = newPerson("name2","male");   
p1.print(); //name1_male_12   
p2.print(); //name2_male_12   
  
Person.prototype.age =18; //改变prototype对象的属性值,注意是操作构造函数的prototype属性   
p1.print(); //name1_male_18   
p2.print(); //name2_male_18  

function Person(name,sex){  //构造函数 
   this.name = name; 
   this.sex = sex; 

Person.prototype.age =12;  //为prototype属性对应的prototype对象的属性赋值 
Person.prototype.print =function() { //添加方法 
   alert(this.name+"_"+this.sex+"_"+this.age); 
}; 

var p1 = newPerson("name1","male"); 
var p2 = newPerson("name2","male"); 
p1.print(); //name1_male_12 
p2.print(); //name2_male_12 

Person.prototype.age =18; //改变prototype对象的属性值,注意是操作构造函数的prototype属性 
p1.print(); //name1_male_18 
p2.print(); //name2_male_18 

到目前为止,我们已经模拟出了简单的类的实现,我们有了构造函数,有了类属性,有了类方法,可以创建"实例". 
在下面的文章中,我们就用"类"这个名字来代替构造方法,但是,这仅仅是模拟,并不是真正的面向对象的"类". 
在下一步的介绍之前,我们先来看看改变对象的prototype属性和设置prototype属性的注意事项: 
给出一种不是很恰当的解释,或许有助于我们理解:当我们new了一个对象之后,这个对象就会获得构造函数的prototype属性(包括函数和变量),可以认为是构造函数(类)继承了它的prototype属性对应的prototype对象的函数和变量,也就是说, prototype对象模拟了一个超类的效果.听着比较拗口,我们直接看个实例吧: 

Js代码 
function Person(name,sex){ //Person类的构造函数   
   this.name =name;   
   this.sex =sex;   
}   
Person.prototype.age =12;  //为Person类的prototype属性对应的prototype对象的属性赋值,   
                            //相当于为Person类的父类添加属性   
Person.prototype.print =function() {//为Person类的父类添加方法   
   alert(this.name+"_"+this.sex+"_"+this.age);   
};   
  
var p1 = newPerson("name1","male");//p1的age属性继承子Person类的父类(即prototype对象)   
var p2 = newPerson("name2","male");   
  
p1.print(); //name1_male_12   
p2.print(); //name2_male_12   
  
p1.age = 34;//改变p1实例的age属性   
p1.print(); //name1_male_34   
p2.print(); //name2_male_12   
  
Person.prototype.age =22; //改变Person类的超类的age属性   
p1.print(); //name1_male_34(p1的age属性并没有随着prototype属性的改变而改变)   
p2.print(); //name2_male_22(p2的age属性发生了改变)   
  
p1.print = function(){ //改变p1对象的print方法   
   alert("i amp1");   
}   
  
p1.print(); //i amp1(p1的方法发生了改变)   
p2.print(); //name2_male_22(p2的方法并没有改变)   
  
Person.prototype.print =function() {//改变Person超类的print方法   
   alert("new printmethod!");   
}   
  
p1.print(); //i amp1(p1的print方法仍旧是自己的方法)   
p2.print(); //new printmethod!(p2的print方法随着超类方法的改变而改变)  

function Person(name,sex){  //Person类的构造函数 
   this.name = name; 
   this.sex = sex; 

Person.prototype.age =12;  //为Person类的prototype属性对应的prototype对象的属性赋值, 
                            //相当于为Person类的父类添加属性 
Person.prototype.print =function() { //为Person类的父类添加方法 
   alert(this.name+"_"+this.sex+"_"+this.age); 
}; 

var p1 = newPerson("name1","male");//p1的age属性继承子Person类的父类(即prototype对象) 
var p2 = newPerson("name2","male"); 

p1.print(); //name1_male_12 
p2.print(); //name2_male_12 

p1.age = 34;//改变p1实例的age属性 
p1.print(); //name1_male_34 
p2.print(); //name2_male_12 

Person.prototype.age =22; //改变Person类的超类的age属性 
p1.print(); //name1_male_34(p1的age属性并没有随着prototype属性的改变而改变) 
p2.print(); //name2_male_22(p2的age属性发生了改变) 

p1.print = function(){  //改变p1对象的print方法 
   alert("i am p1"); 


p1.print(); //i am p1(p1的方法发生了改变) 
p2.print(); //name2_male_22(p2的方法并没有改变) 

Person.prototype.print =function() { //改变Person超类的print方法 
   alert("new print method!"); 


p1.print(); //i am p1(p1的print方法仍旧是自己的方法) 
p2.print(); //new printmethod!(p2的print方法随着超类方法的改变而改变) 

看过一篇文章介绍说javascript中对象的prototype属性相当于java中的static变量,可以被这个类下的所有对象 
共用.而上面的例子似乎表明实际情况并不是这样: 
在JS中,当我们用new操作符创建了一个类的实例对象后,它的方法和属性确实继承了类的prototype属性,类的prototype属性 
中定义的方法和属性,确实可以被这些实例对象直接引用.但是,当我们对这些实例对象的属性和方法重新赋值或定义后,那么 
实例对象的属性或方法就不再指向类的prototype属性中定义的属性和方法,此时,即使再对类的prototype属性中相应的方法或 
属性做修改,也不会反应在实例对象身上.这就解释了上面的例子: 
一开始,用new操作符生成了两个对象p1,p2,他们的age属性和print方法都来自(继承于)Person类的prototype属性.然后,我们 
修改了p1的age属性,后面对Person类的prototype属性中的age重新赋值(Person.prototype.age= 22),p1的age属性并不会 
随之改变,但是p2的age属性却随之发生了变化,因为p2的age属性还是引自Person类的prototype属性.同样的情况在后面的 
print方法中也体现了出来. 

类变量/类方法/实例变量/实例方法
Js代码 
function Person(name,age) { //定义方法   
    this.name =name;   
    this.age =age;   
}   
var o = new Object();  //空对象   
alert(o.name + "_" + o.age);//undefined_undefined   
Person.call(o,"sdcyst",18);//相当于调用:o.Person("sdcyst",18)   
alert(o.name + "_" + o.age);//sdcyst_18   
Person.apply(o,["name",89]);//apply方法作用同call,不同之处在于传递参数的形式是用数组来传递   
alert(o.name + "_" + o.age);//name_89  

Js代码 
function Circle(radius){   
    this.radius=radius;   
}   
Circle.prototype.area = function(){   
       return 3.14 * this.radius *this.radius;   
   }   
var c = newCircle(1);   
alert(c.area()); //3.14  

让我们用prototype属性来模拟一下类的继承:
首先定义一个Circle类作为父类,然后定义子类 PositionCircle. 

Js代码 
function Circle(radius) { //定义父类Circle   
    this.radius=radius;   
}   
Circle.prototype.area = function() {//定义父类的方法area计算面积   
    returnthis.radius * this.radius *3.14;   
}   
  
function PositionCircle(x,y,radius) {//定义类PositionCircle   
    this.x =x;                   //属性横坐标   
    this.y =y;                   //属性纵坐标   
   Circle.call(this,radius);     //调用父类的方法,相当于调用this.Circle(radius),设置PositionCircle类的   
                                  //radius属性   
}   
PositionCircle.prototype = new Circle();//设置PositionCircle的父类为Circle类   
var pc = newPositionCircle(1,2,1);  
alert(pc.area()); //3.14   
                  //PositionCircle类的area方法继承自Circle类,而Circle类的   
                  //area方法又继承自它的prototype属性对应的prototype对象   
alert(pc.radius); //1 PositionCircle类的radius属性继承自Circle类   
  
  
alert(pc.constructor);//Circle       

  
PositionCircle.prototype.constructor =PositionCircle   
alert(pc.constructor); //PositionCircle  

作用域、闭包、模拟私有属性 
Js代码 
var sco = "global"; //全局变量   
function t(){    
    var sco ="local"; //函数内部的局部变量   
   alert(sco);        //local优先调用局部变量   
}   
t();            //local   
alert(sco);      //global 不能使用函数内的局部变量
  
注意一点,在javascript中没有块级别的作用域,也就是说在java或c/c++中我们可以用"{}"来包围一个块,从而在其中定义块内的局部变量,在"{}"块外部,这些变量不再起作用, 同时,也可以在for循环等控制语句中定义局部的变量,但在javascript中没有此项特性: 

Js代码 
function f(props){   
   for(var i=0; i<10; i++){}   
   alert(i);        //10 虽然i定义在for循环的控制语句中,但在函数   
                     //的其他位置仍旧可以访问该变量.   
   if(props == "local"){   
       var sco ="local";   
      alert(sco); 
   }
   alert(sco);      //同样,函数仍可引用if语句内定义的变量   
}
f("local");     //10  local  local
--------------------------------------------------------
var sco ="global"; 
function print1() { 
   alert(sco);  //global 

function print2() { 
    var sco ="local"; 
   alert(sco);  //local 

function print3() { 
   alert(sco);  //undefined 
    var sco ="local"; 
   alert(sco);  local 


print1();  //global 
print2();  //local 
print3();  //undefined  local前面两个函数都很容易理解,关键是第三个:第一个alert语句并没有把全局变量"global"显示出来, 
而是undefined,这是因为在print3函数中,我们定义了sco局部变量(不管位置在何处),那么全局的 
sco属性在函数内部将不起作用,所以第一个alert中sco其实是局部sco变量,相当于: 
functionprint3() { 
    varsco; 
   alert(sco); 
    sco ="local"; 
   alert(sco); 
}从这个例子我们得出,在函数内部定义局部变量时,最好是在开头就把所需的变量定义好,以免出错。 

函数的作用域在定义函数的时候已经确定了,例如: 

Js代码 
varscope = "global"  //定义全局变量   
function print(){   
   alert(scope);   
}   
function change(){   
   var scope = "local"; //定义局部变量   
   print();             //虽然是在change函数的作用域内调用print函数,   
                         //但是print函数执行时仍旧按照它定义时的作用域起作用   
}   
change();   //golbal  

…………
原创粉丝点击