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>
效果:
JavaScript 面向对象(二) —— 案例篇的更多相关文章
- JavaScript 面向对象(三) —— 高级篇
JavaScript 面向对象(一) —— 基础篇 JavaScript 面向对象(二) —— 案例篇 一.json方式的面向对象 首先要知道,js中出现的东西都能够放到json中.关于json数据格 ...
- JavaScript 面向对象(一) —— 基础篇
学好JS的面向对象,能很大程度上提高代码的重用率,像jQuery,easyui等,这篇博客主要从细节上一步步讲JS中如何有效地创建对象,也可以看到常见的创建对象的方式,最后也会附上一些JS面向对象的案 ...
- Javascript面向对象二
Javascript面向对象二 可以通过指定原型属性来对所有的对象指定属性, Object.prototype.name="zhangsan"; Object.prototype. ...
- javascript面向对象系列第一篇——构造函数和原型对象
× 目录 [1]构造函数 [2]原型对象 [3]总结 前面的话 一般地,javascript使用构造函数和原型对象来进行面向对象编程,它们的表现与其他面向对象编程语言中的类相似又不同.本文将详细介绍如 ...
- JavaScript面向对象(二)——成员属性、静态属性、原型属性与JS原型链
前 言 JRedu 上一篇博客中,我们介绍了JS中的面向对象,并详细的解释了this的指向问题.本篇博客,我们继续来学习JS的面向对象.来深入理解一下JavaScript OOP中的成员属性/方 ...
- javascript面向对象系列第二篇——创建对象的5种模式
× 目录 [1]字面量 [2]工厂模式 [3]构造函数[4]原型模式[5]组合模式 前面的话 如何创建对象,或者说如何更优雅的创建对象,一直是一个津津乐道的话题.本文将从最简单的创建对象的方式入手,逐 ...
- javascript面向对象(二):构造函数的继承
本文来自阮一峰 这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例. 今天要介绍的是,对象之间的"继承"的五种方法. 比如,现在有 ...
- 一篇文章图文并茂地带你轻松学完 JavaScript 设计模式(二)
JavaScript 设计模式(二) 本篇文章是 JavaScript 设计模式的第二篇文章,如果没有看过我上篇文章的读者,可以先看完 上篇文章 后再看这篇文章,当然两篇文章并没有过多的依赖性. 5. ...
- Javascript面向对象特性实现封装、继承、接口详细案例——进级高手篇
Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...
随机推荐
- oncontextmenu事件
oncontextmenu的作用是阻止浏览器默认的鼠标右键行为. 阻止弹出右键自带菜单 document.oncontextmenu=function(){ console.log('你点击了右键') ...
- androi 多线程
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=&q ...
- Subtitute
报表导出到Excel时,日期已经被格式化,有时候日期格式带星期几,比方说"2016-12-30 星期五",或者是"2016/12/30 星期五". 我不想要星期 ...
- CentOS 7 Rescure
之前从来没想过会在Linux系统中使用这个东西-- 今天系统无法启动了!!! 一.开机进度条卡住了.查看一下字符卡在哪里了? Starting MySQL Community Server... 就是 ...
- Xmemcached的FAQ和性能调整建议
转载 http://www.blogjava.net/killme2008/archive/2014/02/13/325564.html 一.XMemcached是什么?经常碰到的一个问题是很多朋友对 ...
- JavaScript数组类型
特点 动态长度 一个数组里面的元素可以是不同类型 数组的length属性不是只读属性,可通过length延长数组也可以删减数组的长度 定义数组两种方法 //方法一: var names = new A ...
- 禁止从别的应用启动Activity
在Manifest.xml中设置android:exported="false"即可,如: <activity android:name=".MyAty" ...
- 增量处理属性之记录模式(Record Modes)
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- Snipaste
http://files.cnblogs.com/files/hwd13/Snipast.zip
- memset函数
函数介绍 void *memset(void *s, int ch, size_t n); 函数解释:将s中前n个字节 (typedef unsigned int size_t )用 ch 替换并返回 ...