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

看案例前可以先看看基础篇: JavaScript 面向对象(一) —— 基础篇

案例——面向对象的选项卡:把面向过程的程序一步步改成面向对象的形式,使其能够更加的通用(但是通用的东西,一般会比较臃肿)。

下面是一个简单的选项卡,也是我们常见的面向过程的创建形式。

<!DOCTYPE html>
<html>
<head>
    <style>
        #tabBox input {
            background: #F6F3F3;
            border: 1px solid #FF0000;
        }
        #tabBox .active {
            background: #E9D4D4;
        }
        #tabBox div {
            width:300px; 
            height:250px; 
            display:none;
            padding: 10px;
            background: #E9D4D4;
            border: 1px solid #FF0000;
        }
    </style>
    <meta charset="utf-8" />
    <title>选项卡</title>
    <script>
        window.onload=function(){
            var tabBox = document.getElementById('tabBox');
            var tabBtn = tabBox.getElementsByTagName('input');
            var tabDiv = tabBox.getElementsByTagName('div');
            
            for(var i=0;i<tabBtn.length;i++){
                tabBtn[i].index = i;
                tabBtn[i].onclick = function (){
                    for(var j=0;j<tabBtn.length;j++){
                        tabBtn[j].className='';
                        tabDiv[j].style.display='none';
                    }
                    this.className='active';
                    tabDiv[this.index].style.display='block';
                };
            }
        };
    </script>
</head>

<body>
    <div id="tabBox">
        <input type="button" value="游戏" class="active" />
        <input type="button" value="旅行" />
        <input type="button" value="音乐" />
        <div style="display:block;">GTA5、孤岛惊魂</div>
        <div>澳大利亚、西藏</div>
        <div>暗里着迷、一生有你</div>
    </div>
</body>
</html>

效果:

 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 下面来一步步改成面向对象的形式。

 1.首先将嵌套的函数拿到window.onload外面,不能有函数嵌套,可以有全局变量。如下:所有的改写最终效果都不变。

<!DOCTYPE html>
<html>
<head>
    <style>
        #tabBox input {
            background: #F6F3F3;
            border: 1px solid #FF0000;
        }
        #tabBox .active {
            background: #E9D4D4;
        }
        #tabBox div {
            width:300px; 
            height:250px; 
            display:none;
            padding: 10px;
            background: #E9D4D4;
            border: 1px solid #FF0000;
        }
    </style>
    <meta charset="utf-8" />
    <title>选项卡</title>
    <script>
        //将在嵌套函数里的变量提取到全局中
        var tabBtn = null;
        var tabDiv = null;
        
        window.onload = function(){
            var tabBox = document.getElementById('tabBox');
            tabBtn = tabBox.getElementsByTagName('input');
            tabDiv = tabBox.getElementsByTagName('div');
            
            for(var i=0;i<tabBtn.length;i++){
                tabBtn[i].index = i;
                //此处调用函数即可
                tabBtn[i].onclick = clickBtn;
            }
        };
        
        //将嵌套函数提取到全局中
        function clickBtn(){
            for(var j=0;j<tabBtn.length;j++){
                tabBtn[j].className='';
                tabDiv[j].style.display='none';
            }
            this.className='active';
            tabDiv[this.index].style.display='block';
        };
        
    </script>
</head>

<body>
    <div id="tabBox">
        <input type="button" value="游戏" class="active" />
        <input type="button" value="旅行" />
        <input type="button" value="音乐" />
        <div style="display:block;">GTA5、孤岛惊魂</div>
        <div>澳大利亚、西藏</div>
        <div>暗里着迷、一生有你</div>
    </div>
</body>
</html>

2.将全局的变量变为对象的属性,全局的函数变为对象的方法;将window.onload里的代码提取到一个构造函数里面,在window.onload里创建对象即可;(下面的代码执行起来是有问题的)。

这里必须注意:在构造函数Tab里的this跟之前this所代表的是不同的(此处是通过new来创建对象的);在上面的示例中,this指的是调用者;在构造函数里,this指向的是var tab = new Tab() ,即tab这个对象,注意是对象。

说一下这段代码的问题:我们在Tab的原型上添加clickBtn方法后,clickBtn方法里的this本应该是指向var tab = new Tab()的,但是我们在第44行将clickBtn添加给了this.tabBtn[i],即input按钮,clickBtn的所属由Tab对象变成了input按钮(第49行)。

clickBtn的所属变成input按钮后,那么clickBtn里的this指向按钮,那再来看clickBtn里的代码,this.tabBtn、this.tabDiv,input按钮里有这两个属性吗?没有,所以会出错!

<!DOCTYPE html>
<html>
<head>
    <style>
        #tabBox input {
            background: #F6F3F3;
            border: 1px solid #FF0000;
        }
        #tabBox .active {
            background: #E9D4D4;
        }
        #tabBox div {
            width:300px; 
            height:250px; 
            display:none;
            padding: 10px;
            background: #E9D4D4;
            border: 1px solid #FF0000;
        }
    </style>
    <meta charset="utf-8" />
    <title>选项卡</title>
    <script>
        
        window.onload = function(){
            var tab = new Tab("tabBox");
        }
    
        /**
         * 将之前window.onload里的代码提到一个构造函数里
         * [可以将这个Tab构造函数想象成一个Tab类]
         * @param {Object} id:选项卡id以参数的形式传入
         */
        function Tab(id){
            var tabBox = document.getElementById(id);
            //将之前的全局变量变为对象的属性
            this.tabBtn = tabBox.getElementsByTagName('input');
            this.tabDiv = tabBox.getElementsByTagName('div');
            
            for(var i=0;i<this.tabBtn.length;i++){
                this.tabBtn[i].index = i;
                
                //此处这种方式调用函数,已经将clickBtn的所属变成this.tabBtn[i]
                this.tabBtn[i].onclick = this.clickBtn;
            }
        };
        //将之前的全局函数添加到构造函数的原型里,作为对象的一个方法
        Tab.prototype.clickBtn = function(){
            alert(this); //HTMLInputElement
            for(var j=0;j<this.tabBtn.length;j++){
                this.tabBtn[j].className='';
                this.tabDiv[j].style.display='none';
            }
            this.className='active';
            this.tabDiv[this.index].style.display='block';
        };
        
    </script>
</head>

<body>
    <div id="tabBox">
        <input type="button" value="游戏" class="active" />
        <input type="button" value="旅行" />
        <input type="button" value="音乐" />
        <div style="display:block;">GTA5、孤岛惊魂</div>
        <div>澳大利亚、西藏</div>
        <div>暗里着迷、一生有你</div>
    </div>
</body>
</html>

将clickBtn赋给input按钮后,方法内的this也指向了input按钮:执行第49行代码的效果

3.将clickBtn的调用放在一个函数里,这样就不会改变clickBtn的所属了。

看第41-47行,注意在function里需要用到外部保存的变量_this。再看第52行,此时弹出的是一个Object,说明clickBtn的所属关系没变,还是Tab对象。

但是还有另一个问题,此时clickBtn里的this指向对象,那么看第57、58行中this.className、this.index,此处的this指的是tab对象,那么对象中有这两个属性吗?没有,还会出错!

<!DOCTYPE html>
<html>
<head>
    <style>
        #tabBox input {
            background: #F6F3F3;
            border: 1px solid #FF0000;
        }
        #tabBox .active {
            background: #E9D4D4;
        }
        #tabBox div {
            width:300px; 
            height:250px; 
            display:none;
            padding: 10px;
            background: #E9D4D4;
            border: 1px solid #FF0000;
        }
    </style>
    <meta charset="utf-8" />
    <title>选项卡</title>
    <script>
        
        window.onload = function(){
            var tab = new Tab("tabBox");
        }
    
        /**
         * 选项卡
         * @param {Object} id:选项卡id
         */
        function Tab(id){
            var tabBox = document.getElementById(id);
            
            this.tabBtn = tabBox.getElementsByTagName('input');
            this.tabDiv = tabBox.getElementsByTagName('div');
            
            for(var i=0;i<this.tabBtn.length;i++){
                this.tabBtn[i].index = i;
                //将this保存成一个变量,就可以在下面代码中调用对象的方法了
                var _this = this;
                //此处这种方式调用函数,就不会改变clickBtn方法的所属关系
                this.tabBtn[i].onclick = function(){
                    //注意此处不能直接使用this,this指向this.tabBtn[i]
                    _this.clickBtn();
                };
            }
        };
        //点击选项卡按钮
        Tab.prototype.clickBtn = function(){
            alert(this); //Object
            for(var j=0;j<this.tabBtn.length;j++){
                this.tabBtn[j].className='';
                this.tabDiv[j].style.display='none';
            }
            this.className='active';
            this.tabDiv[this.index].style.display='block';
        };
        
    </script>
</head>

<body>
    <div id="tabBox">
        <input type="button" value="游戏" class="active" />
        <input type="button" value="旅行" />
        <input type="button" value="音乐" />
        <div style="display:block;">GTA5、孤岛惊魂</div>
        <div>澳大利亚、西藏</div>
        <div>暗里着迷、一生有你</div>
    </div>
</body>
</html>

4. 以参数的形式将点击的按钮传入clickBtn中,看第44行代码,以及54,55行代码

<!DOCTYPE html>
<html>
<head>
    <style>
        #tabBox input {
            background: #F6F3F3;
            border: 1px solid #FF0000;
        }
        #tabBox .active {
            background: #E9D4D4;
        }
        #tabBox div {
            width:300px; 
            height:250px; 
            display:none;
            padding: 10px;
            background: #E9D4D4;
            border: 1px solid #FF0000;
        }
    </style>
    <meta charset="utf-8" />
    <title>选项卡</title>
    <script>
        
        window.onload = function(){
            var tab = new Tab("tabBox");
        }
    
        /**
         * 选项卡
         * @param {Object} id:选项卡id
         */
        function Tab(id){
            var tabBox = document.getElementById(id);
            
            this.tabBtn = tabBox.getElementsByTagName('input');
            this.tabDiv = tabBox.getElementsByTagName('div');
            
            for(var i=0;i<this.tabBtn.length;i++){
                this.tabBtn[i].index = i;
                var _this = this;
                this.tabBtn[i].onclick = function(){
                    //注意参数this代表的是this.tabBtn[i],即input按钮
                    _this.clickBtn(this);
                };
            }
        };
        //将点击的按钮以参数的形式传入
        Tab.prototype.clickBtn = function(btn){
            for(var j=0;j<this.tabBtn.length;j++){
                this.tabBtn[j].className='';
                this.tabDiv[j].style.display='none';
            }
            btn.className='active';
            this.tabDiv[btn.index].style.display='block';
        };
        
    </script>
</head>

<body>
    <div id="tabBox">
        <input type="button" value="游戏" class="active" />
        <input type="button" value="旅行" />
        <input type="button" value="音乐" />
        <div style="display:block;">GTA5、孤岛惊魂</div>
        <div>澳大利亚、西藏</div>
        <div>暗里着迷、一生有你</div>
    </div>
</body>
</html>

5.最终版 —— 将代码提取到一个单独的js文件中,在用的时候引入即可。一般花大时间去写一个面向对象的程序,就是为了能够复用,以及方便的使用。

tab.js

/**
 * 选项卡
 * @param {Object} id 选项卡id
 */
function Tab(id){
    var tabBox = document.getElementById(id);
    this.tabBtn = tabBox.getElementsByTagName('input');
    this.tabDiv = tabBox.getElementsByTagName('div');
    
    for(var i=0;i<this.tabBtn.length;i++){
        this.tabBtn[i].index = i;
        var _this = this;
        this.tabBtn[i].onclick = function(){
            _this.clickBtn(this);
        };
    }
};
/**
 * 为Tab原型添加点击选项卡方法
 * @param {Object} btn 点击的按钮
 */
Tab.prototype.clickBtn = function(btn){
    for(var j=0;j<this.tabBtn.length;j++){
        this.tabBtn[j].className='';
        this.tabDiv[j].style.display='none';
    }
    btn.className='active';
    this.tabDiv[btn.index].style.display='block';
};

使用:tab.html 可以看到使用的时候,就可以很简单的创建两个选项卡出来了。

<!DOCTYPE html>
<html>
<head>
    <style>
        .tab input {
            background: #F6F3F3;
            border: 1px solid #FF0000;
        }
        .tab .active {
            background: #E9D4D4;
        }
        .tab div {
            width:300px; 
            height:250px; 
            display:none;
            padding: 10px;
            background: #E9D4D4;
            border: 1px solid #FF0000;
        }
    </style>
    <meta charset="utf-8" />
    <title>选项卡</title>
    <!-- 引入tab.js -->
    <script type="text/javascript" src="../js/tab.js" ></script>
    <script>
        
        window.onload = function(){
            var tab1 = new Tab("tabBox1");
            
            var tab2 = new Tab("tabBox2");
        }
    
    </script>
</head>

<body>
    <div class="tab" id="tabBox1">
        <input type="button" value="游戏" class="active" />
        <input type="button" value="旅行" />
        <input type="button" value="音乐" />
        <div style="display:block;">GTA5、孤岛惊魂</div>
        <div>澳大利亚、西藏</div>
        <div>暗里着迷、一生有你</div>
    </div>
    <br />
    <div class="tab" id="tabBox2">
        <input type="button" value="技术" class="active" />
        <input type="button" value="工具" />
        <input type="button" value="网站" />
        <div style="display:block;">Java、Spring</div>
        <div>Eclipse、HBuilder</div>
        <div>博客园、CSD</div>
    </div>
</body>
</html>

效果:

看的有点晕吧,一定要好好理解JS面向对象中的this。js中面向对象大部分时候出问题就是出在this的处理上,需要注意。

面向过程:简洁、美观、容易维护;

面向对象:容易出错、混乱、难以维护;面向对象相对面向过程来说,写代码的时候麻烦些,但是用起来很方便,面向过程则相反。

开发面向对象的程序需要有一个好的面向对象的思维,即将具体对象抽象成一个构造函数的过程。

 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

再来简单总结一下JS面向对象中的this,this一般会在两种情况下出问题,一是使用定时器、二是事件,从上面的例子中也可以看出来。注意下面的说法是在构造函数里哦,其它情况下,this指向的是调用者。

可以看到效果没有将姓名显示出来,其实看到这里原因应该很清楚了,就是第14行代码中this.name,此处的this指向谁?指向window,因为setInterval是属于window的。

<!DOCTYPE html>
<html>
    <meta charset="UTF-8" />
    <head>
        <script>
            
            function Person(name){
                this.name = name;
                //定时器
                setInterval(this.showName, 3000);
            }
            Person.prototype.showName = function(){
                alert(this); //window
                alert("姓名:"+this.name);
            }
            
            var p1 = new Person("jiangzhou");
            
        </script>
    </head>
</html>

 解决办法:上面例子中已经列出来了,就是用一个function将要执行的代码包起来,使其所属关系不会发生变化,注意function里调用方法时使用的是外部变量'_this'。事件的处理在上面的例子中已经说明了。

<!DOCTYPE html>
<html>
    <meta charset="UTF-8" />
    <head>
        <script>
            
            function Person(name){
                this.name = name;
                
                var _this = this;
                
                setInterval(function(){
                    _this.showName();
                }, 3000);
            }
            Person.prototype.showName = function(){
                alert(this); //[Object Object]
                alert("姓名:"+this.name); //姓名:jianghzou
            }
            
            var p1 = new Person("jiangzhou");
            
        </script>
    </head>
</html>

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 再附上一个案例 —— 拖拽

原始的面向过程代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <style>
            #box {
                width: 100px; 
                height: 100px; 
                background: blue; 
                position: absolute;
            }
        </style>
        <title>拖拽</title>
        <script>
            var oBox=null;
            var disX=0;
            var disY=0;
            
            window.onload=function(){
                oBox=document.getElementById('box');
                
                oBox.onmousedown=fnDown;
            };
            //鼠标按下事件
            function fnDown(ev){
                var oEvent = ev||event;
                disX = oEvent.clientX - oBox.offsetLeft;
                disY = oEvent.clientY - oBox.offsetTop;
                
                document.onmousemove = fnMove;
                document.onmouseup = fnUp;
            }
            //鼠标移动事件
            function fnMove(ev){
                var oEvent=ev||event;
                
                oBox.style.left = oEvent.clientX - disX + 'px';
                oBox.style.top = oEvent.clientY - disY + 'px';
            }
            //鼠标抬起事件
            function fnUp(){
                document.onmousemove = null;
                document.onmouseup = null;
            }
        </script>
    </head>

<body>
    <div id="box"></div>
</body>
</html>

下面是面向对象的代码

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>

效果:

作者:bojiangzhou

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

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

猜你喜欢

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