面向对象(封装、继承、多态)
当我们创建对象时,一般有2种写法。1.字面量直接写 2.通过构造函数进行声明
字面量:
var student={
name:"tom",
age:16,
study:function(){
alert("study");
}
}
构造函数
function Student(name,age){ //构造函数名一般首字母大写
this.name=name;
this.age=age;
this.study=function(){
alert("study");
}
}
构造函数通过new语句来创建实例 var student1=new Student(“tom”,16);
构造函数也是有缺点的:构造函数每new一个实例,里面的函数就会在新的实例中重新创建。因为js中函数就是对象,所以相当于构造函数中的方法又在不断的实例化对象。
因此每个实例上的同名函数并不是同一个。
在这种情况下,我们将里面的函数拿出来并放在外面,如下:
function Student(name,age){
this.name=name;
this.age=age;
this.study=study;
}
function study(){
alert("study");
}
这样不同实例中的同名函数就会是同一个函数。
但是将函数放在外面,就会被任意的调用,没有任何的限制。
这时就需要用到原型对象(prototype):
外面的属性、方法会通过原型属性被放入原型对象中 Student.prototype.study=function(){alert("study");}
而原型中的方法、属性也只能被对应对象的实例调用。这就同样起到了封装的作用,并且封装的更好,因为对象实例可以访问原型中的属性、方法,但是无法更改他们。如果在实例中定义一个和原型属性同名的属性,那么这个属性也只是覆盖了原型属性(这算是一种多态),但是原型属性并没有改变,其他实例仍然同样的调用。
function Student(name){
this.name=name;
}
Student.prototype.age=16;
Student.prototype.study=function(){alert("study");}
var tom=new Student("tom");
var jim=new Student("jim");
tom.age=18;
alert(tom.age); //18 这是实例自身定义的age属性
alert(jim.age); //16 这是来自原型中age属性
(原因涉及到原型链)
继承:
构造函数中的属性、方法通过apply、call来进行继承。
原型中的属性、方法要通过原型继承
function Student(name){
this.name=name;
}
Student.prototype.age=16;
Student.prototype.study=function(){alert("study");}
function Worker(){ //继承Student的属性、方法
Student.apply(this,arguments);
this.job="XXX"; //再定义自身的属性
}
Worker.prototype=Student.prototype; //继承原型的属性、方法
Worker.prototype.showjob=function(){ //定义自身的原型属性、方法
alert(this.job);
}