面相对象的概念
JavaScript并不是面向对象的程序设计语言,他是基于面向对象的弱类型脚本语言,所以面向对象设计的基本特征封装、继承、多态并没有得到完整的实现,而且它也没有如同强类型语言中那么严格的定义标准,不需要严格使用new关键字创建对象。
JavaScript中创建对象
JavaScript中创建函数就相当于创建了对象,返回的对象即是该类的实例,也是Object类的实例,
<script type="text/javascript">
//定义简单函数
function Person (name){
this.name = name ;
}
//使用new关键字,简单创建Person类的实例
var p = new Person('yeeku') ;
//如果p是Person类的实例,则输出静态文本
if (p instanceof Person)
document.writeln("p是Person的实例<br />") ;
//如果P是object类的实例,则输出静态文本
if (p instanceof Object)
document.writeln("p是object的实例");
</script>
可以看到,创建的对象p同属Person类和Object类。
由于JavaScript不允许指定类与类的继承关系,JavaScript没有完善的继承语法,所以开发者定义的类都没有父子关系,只有一个例外,就是他们都是Object类的子类。
对象和关联数组
与强类型语言不同的是,JavaScript对象本质上是一个关联数组,
这表明但需要访问某个JavaScript对象属性时,不仅可以使用obj.propName的形式,也可以采用obj[propName]的形式,事实上再有一些时候,甚至必须使用这种形式
<script type="text/javascript">
function Person(name,age){
//将name、age形参的值分别赋给name、 age实例属性
this.name = name;
this.age = age;
this.info = function(){
alert('info method!');
}
}
var p = new Person('yeeku',30) ;
for (propName in p){
//遍历Person对象的属性
document . writeln('p对象的'+propName+ "属性值为:"
+ p[propName] + "<br />");
}
</script>
这里,实际上是做了一个遍历对象属性的操作,这是必须使用p[propName]形式来访问Person对象的属性,而如果使用p.propName,则会把报错,因为此时程序会吧p.propName当做p实例里的一个属性,但是该属性在p中并不存在。
我们可以这么理解
<script type="text/javascript">
var a = new["a","b","c","d"];
function b(name,age,sex){
this.name =name;
this.age = age;
this.sex = sex;
}
</script>
a是数组,b是关联数组(函数),运用中括号的方法调用数组,a[0]代表a数组中索引为0的值,即"a",a[1]即代表a数组索引值为1的值 “b”。在函数中也是如此,在b[propName]遍历对象属性时,首先遍历b[0],即对象中第一个变量name,b[1]即对象中第二个变量age,在这种形式下的propName其实就类似数组中的索引值,JavaScript通过b[propName]来访问对象中的各个属性值。
继承和prototype
前面说过,JavaScript没有继承语法,所以每一个对象都是Object的子类,对象之间并不存爱直接的父子关系,但JavaScript提供了一些内建类,通过这些内建类可以方便的创建各自的对象。
在用Java或是C++语言习惯创建JavaScript类时,习惯这样写
<script type = "text/javascript">
function Person(name,age){
this.name = name;
this.age = age;
this.info = function(){
document.writeln(this.age +"岁的" + this.name + '正在溜达<br />');
}
}
var p1 = new Person('阿强','10');
p1.info();
</script>
代码在定义Person是=时,也定义了一个Person类,在创建该类的对象时,不仅为对象p1完成了属性的初始化,还为p1提供了一个info方法,这就暴露了一个问题:
- 性能低下:若创建多个对象,那么系统在为这些对象初始化属性时,还位每一个对象都创建了一个info方法,这样虽然每个对象的属性可能不同,但是他们都有一个样的info方法,此时系统会有很多个info函数这其实是做了很多无用功,它会造成系统内存泄露,而其实程序只需要一个info函数就够了。
- 使得info函数中的局部变量产生闭包:闭包会扩大局部变量的作用域,使得局部变量可以存活到函数之外的地方
<script type="text/javascript">
//创建Person函数
function Person (){
// locVal是个局部变量,原本应该该函数结束后立即失效
var locVal = 'ABC' ;
this.info = function(){
//此处会形成闭包
document.writeln ("1ocVal的值为:"+ locVal) ;
return locVal;
}
}
var p = new Person() ;
//调用p对象的info()方法
var val = p.info() ;
//输出val返回值,该返回值就是局部变量locVal
alert(val) ;
</script>
由于在info函数里访问了局部变量,所以形成了闭包使得locVal的作用域变大,这导致在定义val时,可以把Person类里的局部变量locVal带出来,即是离开了info函数,程序依然可以访问到局部变量的值。
prototype属性
为了解决这些问题,JavaScript提供了portotype属性
<script type = "text/javascript">
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.info = function(){
document.writeln(this.age +"岁的" + this.name + '正在溜达<br />');
}
var p1 = new Person('阿强','10');
p1.info();
</script>
这段代码定义info()的方法已经和前文不一样,在JavaScript中,所有函数(类)都有一个prototype属性,如果为JavaScript类的prototype属性增加属性,方法,即可达到对原有类的扩展,我们可以认为,为person类增加了一种属性,这是JavaScript所提供的一种伪继承机制。
所以在上面的写法中,方法info()被提出到了函数体外,所以在创建对象p1时,将只初始化name和age,而通过prototype属性为Person类创建info()方法后,就可以表明Person类带有了info()方法,它的对象们都可以共享这一个info()方法,但是并不会产生闭包——因为他不在Person类当中,创建Person的对象并不会再创建info()方法了。
这种方法虽然解决了上面的问题,但是又带来了新的问题,那就是这种操作实质其实不是继承,因为通过prototype添加新的属性方法时,实质上是将原有的类覆盖了,原有的类将不复存在。
通过prototype对内建类拓展
<script type = "text/javascript">
Array.prototype.indexof = function(obj){
var result = -1;
for(var i = 0;i<this.length ; i++){
if(this[i] == obj){
result = i;
break;
}
}
return result;
}
var arr=[4,5,7,-2,5];
alert (arr.indexof(5));
</script>
上面代码中,通过prototype为JavaScript的内建类Array实施了拓展,增加了indexof()方法,类似String类型里的方法——判断数组中是否包含了某元素
在这里程序第一段就动态的增加了indexof()方法,如果将上面的代码放在JavaScript代码的最方法,则代码中的所有数组都会增加indexof方法,
虽然可以在任何时候为一个类增加属性和方法,但是通常建议在类的定义结束后立即通过prototype属性增加该类所需要的方法,这样可以避免造成一些不必要的混乱,也不会造成内存泄露
JavaScript中的伪继承
结合上面的知识点,其实就可以实现JavaScript中的继承了
<script type = "text/javascript">
function Animal(sex,age){
this.sex = sex;
this.age = age;
}
Animal.prototype.jieshao = function(){
console.log("我的性别是%s,我今年%d岁了!",this.sex,this.age);
}
function Dog(sex,age,kind,yell){
Animal.call(this,sex,age); // (1)
this.kind = kind;
this.yell = yell;
}
Dog.prototype = new Animal(); // (2)
Dog.prototype.sayHello = function(){ // (3)
console.log("我的种类是%s,我的叫声是%s!",this.kind,this.yell)
}
function Cat(sex,age,kind,yell){
Animal.call(this,name,age,kind,yell);
this.kind = kind;
this.yell = yell;
}
var hashiqi = new Dog("公",2,"哈士奇","旺旺旺");
hashiqi.jieshao();
hashiqi.sayHello();
</script>
如何实现JavaScript中的继承?
- 上面首先定义了一个Animal类,然后通过prototype属性为Animal添加了一个方法introduce()
- 然后创建了一个Dog类作为Animal的子类,该类除了继承父类的两个属性,还有两个自己独有的属性kind和yell
- 然后将Dog类的prototype属性设置为Animal对象(代码中 2 的位置),这样就表明Dog类的原型是Animal,这样Dog就继承了Animal的属性和方法,如introduce()方法
- Dog类还通过prototype属性再添加了一个sayHello()方法(代码中 3 的位置)
在完成这些步骤以后,Dog类就有了Animal类的所有属性和方法,Dog类自己还额外添加了两个属性和一个方法,这时候就相当于与Animal类继承给了Dog类,Dog类就相当于Animal类的子类了
注意
在将父类属性给子类属性时,运用了call(用apply也可以)来实现,上面程序(1)处以call作为调用者调用了Animal的构造器来创建子类的属性,这让该对象可以调用Animal对象的属性方法,但是由于这种方式并不是JavaScript真正的继承机制,只是一种伪继承,所以程序使用instanceof判断Dog对象是否为Animal的实例时将返回false
创建对象
JavaScript中创建对象大致有3种方式
- 使用new管家你调用构造器器创建对象
- 使用Object类创建对象
- 使用JSON语法创建对象
使用new关键字调用构造器创建对象
使用new关键字调用构造器创建对象,这是最接近面向对象语言创建对象的方式,new关键字后紧跟函数的方式非常类似于Java中new后紧跟构造器的方式,通过这种方式创建对象简单、直观。JavaScript中所有的函数都可以作为构造器使用,使用new调用函数后总可以返回一个对象。
<script>
function Person(name,age){
this.name = name;
this.age = age;
}
var a = Person();
var b = Person("阿强",5)
</script>
上面是以这种方式创建对象的例子,在前面说过,JavaScript支持传空参数,所以调用时可以不传参,此时该对象内的值就是undefined。
使用Object直接创建对象
var myObj = new Object();
这是空对象,该对象不包含任何属性和方法,因为JavaScript是动态语言,因此可以动态的为该对象增加属性和方法,这种创建方式类似变量的先声明,后赋值的方式
使用JSON语法创建对象
JSON语法提供了一种更简单的方式来创建对象,使用JSON语法可以避免书写函数,也可避免使用new关键字,可以直接创建一个JavaScript对象。
<script>
var person = {
name : "阿珍",
age : 49
}
</script>
这种方法创建队形,总以{开始,以}结束,对象的属性名和属性值以冒号隔开,多个属性之间以逗号隔开,但是定义到最后一个属性后,将不再有逗号,否则会出现报错
使用JSON语法创建JavaScript对象时,属性会不仅可以是普通字符串,也可以是任何基本数据类型,或是函数,数组,甚至还可以是另外一个JSON语法创建的对象
下面定义了一个稍复杂一些的JSON对象
<script type = "text/javascript">
var person = {
name : "wawa",
age : 29,
school :['小学','中学','大学'],
parents: [
{
name : 'baba',
age : 60,
address : '广州'
},
{
name : 'mama',
age : 58,
address : '深圳'
}
]
}
//alert(person.parents);
alert(person.parents[0].age);
</script>
他同时创建了属性,数组,在JSON创建的对象中甚至还又创建了一个对象
JSON实际上已经发展成了一种轻量级。跨语言的数据交换格式,支持JSON语法的编程语言非常多。