JavaScript 面向对象(三) —— 高级篇

JavaScript 面向对象(一) —— 基础篇

JavaScript 面向对象(二) —— 案例篇

一、json方式的面向对象

首先要知道,js中出现的东西都能够放到json中。关于json数据格式这里推荐一篇博客:JSON 数据格式

先看下json创建的简单对象:相比基础篇中的构造函数、原型等的创建方式,json方式简单方便;但是缺点很明显,如果想创建多个对象,那么会产生大量重复代码,不可取。

JSON方式适用于只创建一个对象的情况,代码简介又优雅。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script>
            var person = {
                name: "jiangzhou",
                age: 22,
                showName: function(){
                    alert(this); //[Object Object]
                    alert("姓名:"+this.name);
                },
                showAge: function(){
                    alert("年龄:"+this.age);
                }
            };
            person.showName();
            person.showAge();
            
        </script>
    </head>
</html>

JSON在JS面向对象的应用中,主要的一个作用就是命名空间:如果有大量常用的js函数,利用json,我们可以将同一类函数放在一个“类”里,类似于java那样,这样我们就能很好的管理和查找使用这些js函数,看下面的例子就很好理解了。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script>
            //仿java.lang包
            var lang = {};
            
            /**
             * 仿java.lang.Math类
             */
            lang.Math = {
                /**
                 * 求绝对值
                 * @param {Object} a
                 */
                abs: function(a){
                    return a > 0 ? a : -a;
                },
                /**
                 * 求最大值
                 * @param {Object} a
                 * @param {Object} b
                 */
                max: function(a, b){
                    return a > b ? a : b;
                },
                /**
                 * PI
                 */
                PI: 3.1415926
            }
            
            /**
             * 仿java.lang.String类
             */
            lang.String = {
                /**
                 * 求字符串长度
                 * @param {Object} str
                 */
                length: function(str){
                    return str.length;
                },
                /**
                 * 将字符串转为小写
                 * @param {Object} str
                 */
                toLowerCase: function(str){
                    return str.toLowerCase();
                },
                /**
                 * 将字符串转为大写
                 * @param {Object} str
                 */
                toUpperCase: function(str){
                    return str.toUpperCase();
                }
            }
            
            //调用
            alert(lang.Math.abs(-19)); //19
            alert(lang.Math.PI);
            alert(lang.String.toUpperCase("abcdefg")); //ABCDEFG
            
        </script>
    </head>
</html>

二、面向对象的继承

先举个简单的例子来说一下JS中的继承,Student <extends> Person;

在js中,通过call来调用父类的构造方法继承父类的属性(第33行),通过原型来继承父类的方法(第39行)。注意:先调用父类构造函数,再添加自己的属性;先继承父类的方法,再添加自己的方法。

这里解释下为什么调用Person.call(this, name, sex)就相当于是在调用父类的构造方法:先问一下这个call中的this是谁?这里指向对象student吧。

所以,在子构造函数中调用Person.call()时,那么构造函数Person里的两行代码this.name=name, this.sex=sex中this就是代表student了,所以这两行代码相当于是在为student添加name和sex属性。

但是,下面的通过原型来继承父类的方法,即Student.prototype = Person.prototype,是有问题的,这种方式将影响父类(继承是不能影响父类的),此时Person的原型中有了个showMajor方法(第50行),为什么呢?先思考下,下面解释。

<!DOCTYPE html>
<html>
    <meta charset="UTF-8" />
    <head>
        <script>
            
            /**
             * Person 父类 人
             * @param {Object} name 姓名
             * @param {Object} sex 性别
             */
            function Person(name, sex){
                this.name = name;
                this.sex = sex;
            }
            Person.prototype.showName = function(){
                alert("姓名:"+this.name); 
            }
            Person.prototype.showSex = function(){
                alert("性别:"+this.sex); 
            }
            
        /*-----------------------------------------------------*/
            
            /**
             * Student   学生 继承 人
             * @param {Object} name
             * @param {Object} sex
             * @param {Object} major 学生特有属性:专业
             */
            function Student(name, sex, major){
                //调用父类的构造函数
                Person.call(this, name, sex);
                
                //添加自己的属性
                this.major = major;
            }
            //继承父类原型中的方法
            Student.prototype = Person.prototype;
            //添加自己特有的方法
            Student.prototype.showMajor = function(){
                alert("专业:"+this.major);
            }
            
            var student = new Student("bojiangzhou", "男", "信息管理");
            student.showName();
            student.showSex();
            student.showMajor();
            
            alert(Person.prototype.showMajor);
        </script>
    </head>
</html>

第50行弹出的信息:

为了解释为什么通过Student.prototype = Person.prototype来继承父类的方法会影响父类,下面举一个数组的例子,一看就知道怎么回事了。

为什么arr1和arr2弹出来的一样呢?第15、16行显示arr1和arr2是一个对象。对象!应该很清楚了吧,arr1和arr2都是指向这个数组对象的一个引用,所以改变arr2时,arr1也变了。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <script>
        var arr1 = [1,2,3,4,5];
        var arr2 = arr1;
        
        arr2.push(6);
        
        alert(arr1); //弹出1,2,3,4,5,6
        alert(arr2); //弹出1,2,3,4,5,6
        
        alert(typeof arr1); //object
        alert(typeof arr2); //object
    </script>
</html>

其实我们主要是想获得arr1数组的一个副本,怎么做才能不改变arr1呢,看下面:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <script>
        var arr1 = [1,2,3,4,5];
        var arr2 = [];
        
        //复制arr1的数据即可
        for(var i=0;i<arr1.length;i++){
            arr2[i]=arr1[i];
        }
        
        arr2.push(6);
        
        alert(arr1); //弹出1,2,3,4,5
        alert(arr2); //弹出1,2,3,4,5,6
        
    </script>
</html>

同样的,我们也可以通过这种方式为继承的子类添加父类原型中的方法,而又不影响父类(38-41行):

<!DOCTYPE html>
<html>
    <meta charset="UTF-8" />
    <head>
        <script>
            
            /**
             * Person 父类 人
             * @param {Object} name 姓名
             * @param {Object} sex 性别
             */
            function Person(name, sex){
                this.name = name;
                this.sex = sex;
            }
            Person.prototype.showName = function(){
                alert("姓名:"+this.name); 
            }
            Person.prototype.showSex = function(){
                alert("性别:"+this.sex); 
            }
            
        /*-----------------------------------------------------*/
            
            /**
             * Student   学生 继承 人
             * @param {Object} name
             * @param {Object} sex
             * @param {Object} major 学生特有属性:专业
             */
            function Student(name, sex, major){
                //调用父类的构造函数
                Person.call(this, name, sex);
                
                //添加自己的属性
                this.major = major;
            }
            //继承父类原型中的方法
            for(var p in Person.prototype){
                Student.prototype[p] = Person.prototype[p];
            }
            
            //添加自己特有的方法
            Student.prototype.showMajor = function(){
                alert("专业:"+this.major);
            }
            
            var student = new Student("bojiangzhou", "男", "信息管理");
            student.showName();
            student.showSex();
            student.showMajor();
            
            alert(Person.prototype.showMajor);
        </script>
    </head>
</html>

第53行弹出信息:Person中没有showMajor方法了。

最后,以案例篇中最后给出的拖拽例子来应用下继承,那个拖拽有一个问题,就是没有控制拖拽出边界的问题。

先贴出之前的拖拽版本:

drag.js:

/**
 * 拖拽
 * @param {Object} id div的id
 */
function Drag(id){
    this.oBox = document.getElementById(id);
    this.disX = 0;
    this.disY = 0;
    
    var _this = this;
    
    this.oBox.onmousedown = function(){
        _this.fnDown();
    }
}
//鼠标按下
Drag.prototype.fnDown = function(ev){
    var oEvent = ev || event;
    
    this.disX = oEvent.clientX - this.oBox.offsetLeft;
    this.disY = oEvent.clientY - this.oBox.offsetTop;
    
    var _this = this;
    
    document.onmousemove = function(){
        _this.fnMove();
    };
    document.onmouseup = function(){
        _this.fnUp();
    };
}
//鼠标移动
Drag.prototype.fnMove = function(ev){
    var oEvent= ev || event;
    
    this.oBox.style.left = oEvent.clientX - this.disX + 'px';
    this.oBox.style.top = oEvent.clientY - this.disY + 'px';
}
//鼠标抬起
Drag.prototype.fnUp = function(){
    document.onmousemove = null;
    document.onmouseup = null;
}

drag.html:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <style>
            div {
                position: absolute;
            }
        </style>
        <title>拖拽</title>
        <script type="text/javascript" src="../js/drag.js" ></script>
        <script>
            window.onload = function(){
                var drag1 = new Drag("box1");
                
                var drag1 = new Drag("box2");
            };
        </script>
    </head>

<body>
    <div id="box1" style="background: red;width: 200px;height: 200px;"></div>
    
    <div id="box2" style="background: blue;width: 100px;height: 300px;"></div>
</body>
</html>

效果:可以看到红色和蓝色的都出边界了,但我们又不想去修改代码,那我们怎么做?学过java的应该都知道可以写一个子类来做一些更加具体的操作,又保留了父类的功能,就是继承。

DragLimit.js:DragLimit继承自Drag,控制了不能出边界

/**
 * 限制边界的拖拽,继承自Drag
 * @param {Object} id
 */
function DragLimit(id){
    Drag.call(this, id);
}
//继承方法
for(var p in Drag.prototype){
    DragLimit.prototype[p] = Drag.prototype[p];
}
/**
 * 覆写父类的鼠标移动方法,控制不能移出边界
 */
DragLimit.prototype.fnMove = function(ev){
    var oEvent= ev || event;
    
    var left = oEvent.clientX - this.disX;
    var top = oEvent.clientY - this.disY;
    
    //控制边界
    if(left < 0){
        left = 0;
    } else if(left > document.documentElement.clientWidth-this.oBox.offsetWidth){
        left = document.documentElement.clientWidth-this.oBox.offsetWidth;
    }
    if(top <= 0){
        top = 0;
    } else if(top > document.documentElement.clientHeight-this.oBox.offsetHeight){
        top = document.documentElement.clientHeight-this.oBox.offsetHeight;
    }
    
    this.oBox.style.left = left + 'px';
    this.oBox.style.top = top + 'px';
}

dragLimit.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <style>
            body {
                padding: 0;
                margin: 0;
            }
            div {
                position: absolute;
            }
        </style>
        <title>拖拽</title>
        <script type="text/javascript" src="../js/drag.js" ></script>
        <script type="text/javascript" src="../js/dragLimit.js" ></script>
        <script>
            window.onload = function(){
                var drag1 = new Drag("box1");
                
                var drag1 = new DragLimit("box2");
            };
        </script>
    </head>

<body>
    <div id="box1" style="background: red;width: 200px;height: 200px;"></div>
    
    <div id="box2" style="background: blue;width: 100px;height: 300px;"></div>
</body>
</html>

效果:蓝色是不能移出边界的。

 三、JS中的对象

js中的对象分为本地对象、内置对象、宿主对象,这里给出W3School文档供参考:ECMAScript 对象类型

作者:bojiangzhou

出处:http://www.cnblogs.com/chiangchou/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

猜你喜欢

转载自blog.csdn.net/qq_35208583/article/details/88863653