关于javascript要不要使用面向对象编程,每个人都会有自己的看法。如果是在后端使用javascript,那出于多方面的原因都会使用面向对象编程。但如果是作为浏览器的脚本语言,不同的人有不同的看法。
有些人觉得javascript更偏向于一门函数式语言。有些人觉得javascript要对dom进行频繁的操作,不适合面向对象编程。但是不同的公司有不同的语言规范,为了代码可读性,也为了更好的合作,有时候就必须进行面向对象编程。
面向对象的特性
面向对象的三大主要特性就是继承、封装、多态。
由于javascript没有真正意义上的class(类),因此在实现面向对象编程的过程中与其他语言有些不一样(比如我们较为熟悉的java)。
封装
我参考了阮一峰的博文,感兴趣的可以直接看原文:Javascript 面向对象编程(一):封装。
在这里还需要先搞清楚几个词语:原型,实例,字面量。
原型
原型是一个对象,其他对象可以通过它实现属性继承。每一个对象都可以成为原型。
可以参考这篇文章:理解JavaScript原型。
实例
在面向对象的编程中,通常把用类创建对象的过程称为实例化,创建出来的就叫实例。当然javascript没有类,却有类似类的存在。
字面量
在JavaScript中,可以通过类的实例化来创建对象,也可以使用 对象字面量 直接创建对象。
比如:
var people={
name:"xiaoming",
age:20
}
就是用对象字面量直接创建了people这个对象。
封装
如上所示,如果用字面量的方法进行封装
var people={
name:"",
age=""
根据people这个作为原型来实例化两个实例
var people1={};
people1.name="xiaoming";
people1.age="22";
var people2={};
people2.name="xiaofang";
people2.age="23";
可以看出这样的方法既浪费代码,实例与原型之间也看不出有什么联系。
接下来我们用构造函数的方法:
function people(name,age){
this.name=name;
this.age=age;
}
var people1=new people("小明",22);
var people2=new people("小芳",23);
alert(people1.name);//小明
alert(people2.age);//23
用了构造函数,感觉一切都是那么完美了,但是问题还是存在。
function people(name,age){
this.name=name;
this.age=age;
height=180;
sex="男";
ability=function(){alert("花钱")};
}
var people1=new people("小明",22);
var people2=new people("小芳",23);
alert(people1.name);//小明
alert(people2.age);//23
alert(people1.sex);//男
alert(people2.height);//180
可以看出,每次我新建一个实例,这些一样的属性会被重新创建一遍,一次次地开辟新的内存空间,浪费可想而知。
于是乎,javascript发明了一个prototype的属性。
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。
function people(name,age){
this.name=name;
this.age=age;
}
people.prototype.height=180;
people.prototype.sex="男";
var people1=new people("小明",22);
var people2=new people("小芳",23);
alert(people1.name);//小明
alert(people2.age);//23
alert(people1.height);//180
alert(people2.sex);//男
这样子一些一样的属性都是继承而来,而不是重新创建的。
当然具体用哪个还是看情况的。
继承
构造函数继承
function people(){};
people.prototype.say=function(){
alert("hello");
}
function student(){};
student.prototype=new people();
这样就实现了继承。可以创建实例进行试验。
var s=new student();
s.say();//hello
可以看到实现了继承。
那如果继承的元素拥有好饿被继承的元素一样的属性呢?
function people(){};
people.prototype.say=function(){
alert("hello");
}
function student(){};
student.prototype=new people();
student.protoype.say=function(){
alert("world");
var s=new student();
s.say();//world
可以看到实现的是继承者的自有属性。这里其实是重写了父类的方法。
那要使用父类的方法呢?
student.prototype=new people();
var supersay=student.prototype.say;
student.prototype.say=function(){
supersay.call(this);
alert("world");
}
var s=new student();
s.say();//hello world
带参数的也是一样。
function people(name){
this._name=name;
};
people.prototype.say=function(){
alert("hello"+this._name);
}
function student(name){
this._name=name;
};
student.prototype=new people();
var supersay=student.prototype.say;
student.prototype.say=function(){
supersay.call(this);
alert("world"+this._name);
}
var s=new student("xiaoming");
s.say();//world
非构造函数继承
如果不使用构造函数的话,要实现继承可以使用对象的传递。
function person(){
var _this={};
_this.sayhello=function(){
alert("hello");
}
return _this;
}
function student(){
var _this=person();
return _this;
}
var s=student();
s.sayhello();
这里的student的_this完全继承了person这个对象,因此拥有person里的属性和方法。
这个方式也可以进行父类的重写。
function person(){
var _this={};
_this.sayhello=function(){
alert("hello");
}
return _this;
}
function student(){
var _this=person();
_this.sayhello=function(){
alert("hi");
}
return _this;
}
var s=student();
s.sayhello();
要使用父类的方法也是一样的,用call或apply。
function person(){
var _this={};
_this.sayhello=function(){
alert("hello");
}
return _this;
}
function student(){
var _this=person();
var supersay=_this.sayhello;
_this.sayhello=function(){
supersay.call(this);
alert("hi");
}
return _this;
}
var s=student();
s.sayhello();
传参和闭包与构造函数的继承是一样的。
信息的封装(闭包)
闭包的形式:
(function(){
var n="xiaofang";
function people(name){
this._name=name;
};
people.prototype.say=function(){
alert("hello"+this._name+n);
}
}())
即用个大括号把内容包起来,这个时候定义的n就不能被外界取到。比如:
(function(){
var n="xiaofang";
function people(name){
this._name=name;
};
people.prototype.say=function(){
alert("hello"+this._name+n);
}
}())
function student(name){
this._name=name;
};
student.prototype=new people();
var supersay=student.prototype.say;
student.prototype.say=function(){
supersay.call(this);
alert("world"+this._name);
}
var s=new student("xiaoming");
s.say();//n值无法取到
(function(){
var n="xiaofang";
function people(name){
this._name=name;
};
people.prototype.say=function(){
alert("hello"+this._name+n);
}
window.people=people;
}())
把主体赋给顶级的window,就可以执行。