看案例前可以先看看基础篇: JavaScript 面向对象(一) —— 基础篇
案例——面向对象的选项卡:把面向过程的程序一步步改成面向对象的形式,使其能够更加的通用(但是通用的东西,一般会比较臃肿)。
下面是一个简单的选项卡,也是我们常见的面向过程的创建形式。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <style>
5 #tabBox input {
6 background: #F6F3F3;
7 border: 1px solid #FF0000;
8 }
9 #tabBox .active {
10 background: #E9D4D4;
11 }
12 #tabBox div {
13 width:300px;
14 height:250px;
15 display:none;
16 padding: 10px;
17 background: #E9D4D4;
18 border: 1px solid #FF0000;
19 }
20 </style>
21 <meta charset="utf-8" />
22 <title>选项卡</title>
23 <script>
24 window.onload=function(){
25 var tabBox = document.getElementById('tabBox');
26 var tabBtn = tabBox.getElementsByTagName('input');
27 var tabDiv = tabBox.getElementsByTagName('div');
28
29 for(var i=0;i<tabBtn.length;i++){
30 tabBtn[i].index = i;
31 tabBtn[i].onclick = function (){
32 for(var j=0;j<tabBtn.length;j++){
33 tabBtn[j].className='';
34 tabDiv[j].style.display='none';
35 }
36 this.className='active';
37 tabDiv[this.index].style.display='block';
38 };
39 }
40 };
41 </script>
42 </head>
43
44 <body>
45 <div id="tabBox">
46 <input type="button" value="游戏" class="active" />
47 <input type="button" value="旅行" />
48 <input type="button" value="音乐" />
49 <div style="display:block;">GTA5、孤岛惊魂</div>
50 <div>澳大利亚、西藏</div>
51 <div>暗里着迷、一生有你</div>
52 </div>
53 </body>
54 </html>
效果:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
下面来一步步改成面向对象的形式。
1.首先将嵌套的函数拿到window.onload外面,不能有函数嵌套,可以有全局变量。如下:所有的改写最终效果都不变。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <style>
5 #tabBox input {
6 background: #F6F3F3;
7 border: 1px solid #FF0000;
8 }
9 #tabBox .active {
10 background: #E9D4D4;
11 }
12 #tabBox div {
13 width:300px;
14 height:250px;
15 display:none;
16 padding: 10px;
17 background: #E9D4D4;
18 border: 1px solid #FF0000;
19 }
20 </style>
21 <meta charset="utf-8" />
22 <title>选项卡</title>
23 <script>
24 //将在嵌套函数里的变量提取到全局中
25 var tabBtn = null;
26 var tabDiv = null;
27
28 window.onload = function(){
29 var tabBox = document.getElementById('tabBox');
30 tabBtn = tabBox.getElementsByTagName('input');
31 tabDiv = tabBox.getElementsByTagName('div');
32
33 for(var i=0;i<tabBtn.length;i++){
34 tabBtn[i].index = i;
35 //此处调用函数即可
36 tabBtn[i].onclick = clickBtn;
37 }
38 };
39
40 //将嵌套函数提取到全局中
41 function clickBtn(){
42 for(var j=0;j<tabBtn.length;j++){
43 tabBtn[j].className='';
44 tabDiv[j].style.display='none';
45 }
46 this.className='active';
47 tabDiv[this.index].style.display='block';
48 };
49
50 </script>
51 </head>
52
53 <body>
54 <div id="tabBox">
55 <input type="button" value="游戏" class="active" />
56 <input type="button" value="旅行" />
57 <input type="button" value="音乐" />
58 <div style="display:block;">GTA5、孤岛惊魂</div>
59 <div>澳大利亚、西藏</div>
60 <div>暗里着迷、一生有你</div>
61 </div>
62 </body>
63 </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按钮里有这两个属性吗?没有,所以会出错!
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <style>
5 #tabBox input {
6 background: #F6F3F3;
7 border: 1px solid #FF0000;
8 }
9 #tabBox .active {
10 background: #E9D4D4;
11 }
12 #tabBox div {
13 width:300px;
14 height:250px;
15 display:none;
16 padding: 10px;
17 background: #E9D4D4;
18 border: 1px solid #FF0000;
19 }
20 </style>
21 <meta charset="utf-8" />
22 <title>选项卡</title>
23 <script>
24
25 window.onload = function(){
26 var tab = new Tab("tabBox");
27 }
28
29 /**
30 * 将之前window.onload里的代码提到一个构造函数里
31 * [可以将这个Tab构造函数想象成一个Tab类]
32 * @param {Object} id:选项卡id以参数的形式传入
33 */
34 function Tab(id){
35 var tabBox = document.getElementById(id);
36 //将之前的全局变量变为对象的属性
37 this.tabBtn = tabBox.getElementsByTagName('input');
38 this.tabDiv = tabBox.getElementsByTagName('div');
39
40 for(var i=0;i<this.tabBtn.length;i++){
41 this.tabBtn[i].index = i;
42
43 //此处这种方式调用函数,已经将clickBtn的所属变成this.tabBtn[i]
44 this.tabBtn[i].onclick = this.clickBtn;
45 }
46 };
47 //将之前的全局函数添加到构造函数的原型里,作为对象的一个方法
48 Tab.prototype.clickBtn = function(){
49 alert(this); //HTMLInputElement
50 for(var j=0;j<this.tabBtn.length;j++){
51 this.tabBtn[j].className='';
52 this.tabDiv[j].style.display='none';
53 }
54 this.className='active';
55 this.tabDiv[this.index].style.display='block';
56 };
57
58 </script>
59 </head>
60
61 <body>
62 <div id="tabBox">
63 <input type="button" value="游戏" class="active" />
64 <input type="button" value="旅行" />
65 <input type="button" value="音乐" />
66 <div style="display:block;">GTA5、孤岛惊魂</div>
67 <div>澳大利亚、西藏</div>
68 <div>暗里着迷、一生有你</div>
69 </div>
70 </body>
71 </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对象,那么对象中有这两个属性吗?没有,还会出错!
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <style>
5 #tabBox input {
6 background: #F6F3F3;
7 border: 1px solid #FF0000;
8 }
9 #tabBox .active {
10 background: #E9D4D4;
11 }
12 #tabBox div {
13 width:300px;
14 height:250px;
15 display:none;
16 padding: 10px;
17 background: #E9D4D4;
18 border: 1px solid #FF0000;
19 }
20 </style>
21 <meta charset="utf-8" />
22 <title>选项卡</title>
23 <script>
24
25 window.onload = function(){
26 var tab = new Tab("tabBox");
27 }
28
29 /**
30 * 选项卡
31 * @param {Object} id:选项卡id
32 */
33 function Tab(id){
34 var tabBox = document.getElementById(id);
35
36 this.tabBtn = tabBox.getElementsByTagName('input');
37 this.tabDiv = tabBox.getElementsByTagName('div');
38
39 for(var i=0;i<this.tabBtn.length;i++){
40 this.tabBtn[i].index = i;
41 //将this保存成一个变量,就可以在下面代码中调用对象的方法了
42 var _this = this;
43 //此处这种方式调用函数,就不会改变clickBtn方法的所属关系
44 this.tabBtn[i].onclick = function(){
45 //注意此处不能直接使用this,this指向this.tabBtn[i]
46 _this.clickBtn();
47 };
48 }
49 };
50 //点击选项卡按钮
51 Tab.prototype.clickBtn = function(){
52 alert(this); //Object
53 for(var j=0;j<this.tabBtn.length;j++){
54 this.tabBtn[j].className='';
55 this.tabDiv[j].style.display='none';
56 }
57 this.className='active';
58 this.tabDiv[this.index].style.display='block';
59 };
60
61 </script>
62 </head>
63
64 <body>
65 <div id="tabBox">
66 <input type="button" value="游戏" class="active" />
67 <input type="button" value="旅行" />
68 <input type="button" value="音乐" />
69 <div style="display:block;">GTA5、孤岛惊魂</div>
70 <div>澳大利亚、西藏</div>
71 <div>暗里着迷、一生有你</div>
72 </div>
73 </body>
74 </html>
4. 以参数的形式将点击的按钮传入clickBtn中,看第44行代码,以及54,55行代码
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <style>
5 #tabBox input {
6 background: #F6F3F3;
7 border: 1px solid #FF0000;
8 }
9 #tabBox .active {
10 background: #E9D4D4;
11 }
12 #tabBox div {
13 width:300px;
14 height:250px;
15 display:none;
16 padding: 10px;
17 background: #E9D4D4;
18 border: 1px solid #FF0000;
19 }
20 </style>
21 <meta charset="utf-8" />
22 <title>选项卡</title>
23 <script>
24
25 window.onload = function(){
26 var tab = new Tab("tabBox");
27 }
28
29 /**
30 * 选项卡
31 * @param {Object} id:选项卡id
32 */
33 function Tab(id){
34 var tabBox = document.getElementById(id);
35
36 this.tabBtn = tabBox.getElementsByTagName('input');
37 this.tabDiv = tabBox.getElementsByTagName('div');
38
39 for(var i=0;i<this.tabBtn.length;i++){
40 this.tabBtn[i].index = i;
41 var _this = this;
42 this.tabBtn[i].onclick = function(){
43 //注意参数this代表的是this.tabBtn[i],即input按钮
44 _this.clickBtn(this);
45 };
46 }
47 };
48 //将点击的按钮以参数的形式传入
49 Tab.prototype.clickBtn = function(btn){
50 for(var j=0;j<this.tabBtn.length;j++){
51 this.tabBtn[j].className='';
52 this.tabDiv[j].style.display='none';
53 }
54 btn.className='active';
55 this.tabDiv[btn.index].style.display='block';
56 };
57
58 </script>
59 </head>
60
61 <body>
62 <div id="tabBox">
63 <input type="button" value="游戏" class="active" />
64 <input type="button" value="旅行" />
65 <input type="button" value="音乐" />
66 <div style="display:block;">GTA5、孤岛惊魂</div>
67 <div>澳大利亚、西藏</div>
68 <div>暗里着迷、一生有你</div>
69 </div>
70 </body>
71 </html>
5.最终版 —— 将代码提取到一个单独的js文件中,在用的时候引入即可。一般花大时间去写一个面向对象的程序,就是为了能够复用,以及方便的使用。
tab.js
1 /**
2 * 选项卡
3 * @param {Object} id 选项卡id
4 */
5 function Tab(id){
6 var tabBox = document.getElementById(id);
7 this.tabBtn = tabBox.getElementsByTagName('input');
8 this.tabDiv = tabBox.getElementsByTagName('div');
9
10 for(var i=0;i<this.tabBtn.length;i++){
11 this.tabBtn[i].index = i;
12 var _this = this;
13 this.tabBtn[i].onclick = function(){
14 _this.clickBtn(this);
15 };
16 }
17 };
18 /**
19 * 为Tab原型添加点击选项卡方法
20 * @param {Object} btn 点击的按钮
21 */
22 Tab.prototype.clickBtn = function(btn){
23 for(var j=0;j<this.tabBtn.length;j++){
24 this.tabBtn[j].className='';
25 this.tabDiv[j].style.display='none';
26 }
27 btn.className='active';
28 this.tabDiv[btn.index].style.display='block';
29 };
使用:tab.html 可以看到使用的时候,就可以很简单的创建两个选项卡出来了。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <style>
5 .tab input {
6 background: #F6F3F3;
7 border: 1px solid #FF0000;
8 }
9 .tab .active {
10 background: #E9D4D4;
11 }
12 .tab div {
13 width:300px;
14 height:250px;
15 display:none;
16 padding: 10px;
17 background: #E9D4D4;
18 border: 1px solid #FF0000;
19 }
20 </style>
21 <meta charset="utf-8" />
22 <title>选项卡</title>
23 <!-- 引入tab.js -->
24 <script type="text/javascript" src="../js/tab.js" ></script>
25 <script>
26
27 window.onload = function(){
28 var tab1 = new Tab("tabBox1");
29
30 var tab2 = new Tab("tabBox2");
31 }
32
33 </script>
34 </head>
35
36 <body>
37 <div class="tab" id="tabBox1">
38 <input type="button" value="游戏" class="active" />
39 <input type="button" value="旅行" />
40 <input type="button" value="音乐" />
41 <div style="display:block;">GTA5、孤岛惊魂</div>
42 <div>澳大利亚、西藏</div>
43 <div>暗里着迷、一生有你</div>
44 </div>
45 <br />
46 <div class="tab" id="tabBox2">
47 <input type="button" value="技术" class="active" />
48 <input type="button" value="工具" />
49 <input type="button" value="网站" />
50 <div style="display:block;">Java、Spring</div>
51 <div>Eclipse、HBuilder</div>
52 <div>博客园、CSD</div>
53 </div>
54 </body>
55 </html>
效果:
看的有点晕吧,一定要好好理解JS面向对象中的this。js中面向对象大部分时候出问题就是出在this的处理上,需要注意。
面向过程:简洁、美观、容易维护;
面向对象:容易出错、混乱、难以维护;面向对象相对面向过程来说,写代码的时候麻烦些,但是用起来很方便,面向过程则相反。
开发面向对象的程序需要有一个好的面向对象的思维,即将具体对象抽象成一个构造函数的过程。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
再来简单总结一下JS面向对象中的this,this一般会在两种情况下出问题,一是使用定时器、二是事件,从上面的例子中也可以看出来。注意下面的说法是在构造函数里哦,其它情况下,this指向的是调用者。
可以看到效果没有将姓名显示出来,其实看到这里原因应该很清楚了,就是第14行代码中this.name,此处的this指向谁?指向window,因为setInterval是属于window的。
1 <!DOCTYPE html>
2 <html>
3 <meta charset="UTF-8" />
4 <head>
5 <script>
6
7 function Person(name){
8 this.name = name;
9 //定时器
10 setInterval(this.showName, 3000);
11 }
12 Person.prototype.showName = function(){
13 alert(this); //window
14 alert("姓名:"+this.name);
15 }
16
17 var p1 = new Person("jiangzhou");
18
19 </script>
20 </head>
21 </html>
效果:
解决办法:上面例子中已经列出来了,就是用一个function将要执行的代码包起来,使其所属关系不会发生变化,注意function里调用方法时使用的是外部变量'_this'。事件的处理在上面的例子中已经说明了。
1 <!DOCTYPE html>
2 <html>
3 <meta charset="UTF-8" />
4 <head>
5 <script>
6
7 function Person(name){
8 this.name = name;
9
10 var _this = this;
11
12 setInterval(function(){
13 _this.showName();
14 }, 3000);
15 }
16 Person.prototype.showName = function(){
17 alert(this); //[Object Object]
18 alert("姓名:"+this.name); //姓名:jianghzou
19 }
20
21 var p1 = new Person("jiangzhou");
22
23 </script>
24 </head>
25 </html>
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
再附上一个案例 —— 拖拽
原始的面向过程代码:
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <style>
6 #box {
7 width: 100px;
8 height: 100px;
9 background: blue;
10 position: absolute;
11 }
12 </style>
13 <title>拖拽</title>
14 <script>
15 var oBox=null;
16 var disX=0;
17 var disY=0;
18
19 window.onload=function(){
20 oBox=document.getElementById('box');
21
22 oBox.onmousedown=fnDown;
23 };
24 //鼠标按下事件
25 function fnDown(ev){
26 var oEvent = ev||event;
27 disX = oEvent.clientX - oBox.offsetLeft;
28 disY = oEvent.clientY - oBox.offsetTop;
29
30 document.onmousemove = fnMove;
31 document.onmouseup = fnUp;
32 }
33 //鼠标移动事件
34 function fnMove(ev){
35 var oEvent=ev||event;
36
37 oBox.style.left = oEvent.clientX - disX + 'px';
38 oBox.style.top = oEvent.clientY - disY + 'px';
39 }
40 //鼠标抬起事件
41 function fnUp(){
42 document.onmousemove = null;
43 document.onmouseup = null;
44 }
45 </script>
46 </head>
47
48 <body>
49 <div id="box"></div>
50 </body>
51 </html>
下面是面向对象的代码
drag.js
1 /**
2 * 拖拽
3 * @param {Object} id div的id
4 */
5 function Drag(id){
6 this.oBox = document.getElementById(id);
7 this.disX = 0;
8 this.disY = 0;
9
10 var _this = this;
11
12 this.oBox.onmousedown = function(){
13 _this.fnDown();
14 }
15 }
16 //鼠标按下
17 Drag.prototype.fnDown = function(ev){
18 var oEvent = ev || event;
19
20 this.disX = oEvent.clientX - this.oBox.offsetLeft;
21 this.disY = oEvent.clientY - this.oBox.offsetTop;
22
23 var _this = this;
24
25 document.onmousemove = function(){
26 _this.fnMove();
27 };
28 document.onmouseup = function(){
29 _this.fnUp();
30 };
31 }
32 //鼠标移动
33 Drag.prototype.fnMove = function(ev){
34 var oEvent= ev || event;
35
36 this.oBox.style.left = oEvent.clientX - this.disX + 'px';
37 this.oBox.style.top = oEvent.clientY - this.disY + 'px';
38 }
39 //鼠标抬起
40 Drag.prototype.fnUp = function(){
41 document.onmousemove = null;
42 document.onmouseup = null;
43 }
drag.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <style>
6 div {
7 position: absolute;
8 }
9 </style>
10 <title>拖拽</title>
11 <script type="text/javascript" src="../js/drag.js" ></script>
12 <script>
13 window.onload = function(){
14 var drag1 = new Drag("box1");
15
16 var drag1 = new Drag("box2");
17 };
18 </script>
19 </head>
20
21 <body>
22 <div id="box1" style="background: red;data-width: 200px;data-height: 200px;"></div>
23
24 <div id="box2" style="background: blue;data-width: 100px;data-height: 300px;"></div>
25 </body>
26 </html>
效果: