JavaScript中的面向对象

面相对象的概念

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中的继承?

  1. 上面首先定义了一个Animal类,然后通过prototype属性为Animal添加了一个方法introduce()
  2. 然后创建了一个Dog类作为Animal的子类,该类除了继承父类的两个属性,还有两个自己独有的属性kind和yell
  3. 然后将Dog类的prototype属性设置为Animal对象(代码中 2 的位置),这样就表明Dog类的原型是Animal,这样Dog就继承了Animal的属性和方法,如introduce()方法
  4. 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语法的编程语言非常多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值