前面一章节简单介绍了js 里面创建对象的几种方式,其中每种都是有利有弊;但是最为安全有效的还是最后一个混合构造函数方式;
回顾一下这种方式的简单套路
1,利用构造函数加属性;2,利用原型加方法(事件函数);
现在我们来把一个普通的面向过程的函数改成面向对象的形式;用一个最简单的tab来说明
<style media="screen">
button.act{
background:red;
}
.box{
width:400px;
height:300px;
color:white;
}
.box div{
display:none;
height:100%;
}
.box div.act{
display:block;
}
</style>
<div id="wrap">
<div class="nav">
<button type="button" class="act" name="button">选项一</button>
<button type="button" name="button">选项二</button>
<button type="button" name="button">选项三</button>
</div>
<div class="box">
<div class="box-i act" style="background:black">选项一div</div>
<div class="box-i" style="background:teal">选项二div</div>
<div class="box-i" style="background:blue">选项三div</div>
</div>
</div>
<script>
window.onload=function () {
var op=document.getElementById('wrap');
var aBtn=op.getElementsByTagName('button');
var aBox=op.getElementsByClassName('box-i');
for (var i = 0; i < aBtn.length; i++) {
aBtn[i].index=i;
aBox[i].index=i;
aBtn[i].onclick=function(){
for (var j = 0; j < aBtn.length; j++) {
aBtn[j].classList.remove('act');
aBox[j].classList.remove('act');
}
this.classList.add('act');
aBox[this.index].classList.add('act');
}
}
}
</script>
没啥样式,结构也没啥,js部分也很简单(贼恶心);
现在我们来把这东西改成对象形式的,在改成面向对象的时候我们需要明确几点;
1,不能有函数嵌套,但是可以有全局变量;
2,把普通的函数改成构造函数
3,函数里面的变量变成属性
4,函数里面的方法改为对象的方法
明确了上面的几点来动手吧,先从简单的动手,把原来函数的变量改成属性和改成构造函数吧,从window.onload里面把这东西拿出去
为此我们需要一个构造函数来做
function swtab(id){
this.oparent=document.getElementById(id);
this.aBtn=this.oparent.getElementsByTagName('button');
this.aBox=this.oparent.getElementsByClassName('box-i');
for (var i = 0; i < this.aBtn.length; i++) {
this.aBox[i].index=i;
this.aBtn[i].index=i;
this.aBtn[i].onclick=function(){
for (var j = 0; j < this.aBtn.length; j++) {
this.aBtn[j].classList.remove('act');
this.aBox[j].classList.remove('act');
}
this.classList.add('act');
this.aBox[this.index].classList.add('act');
}
}
}
上面做了2件事,第一把原本代码块用一个函数抽了出去,并且 把原来的变量改成了这个构造函数的属性;(变成了this.),也就是以前所有的变量都要使用this. 的形式了;
现在我们要用第4点把构造函数里面的嵌套函数也抽出去,别忘了构造函数加属性,原型加方法原则
function swtab(id){
this.oparent=document.getElementById(id);
this.aBtn=this.oparent.getElementsByTagName('button');
this.aBox=this.oparent.getElementsByClassName('box-i');
for (var i = 0; i < this.aBtn.length; i++) {
this.aBox[i].index=i;
this.aBtn[i].index=i;
this.aBtn[i].onclick=this.fnClick;
}
}
swtab.prototype.fnClick= function(){
for(var j=0;j<this.aBtn.length;j++){
this.aBtn[j].classList.remove('act');
this.aBox[j].classList.remove('act');
}
this.classList.add('act');
this.aBox[this.index].classList.add('act');
}
现在没了函数嵌套,而是把click 函数挂在swtab的原型上;现在我们运行
在swtab.prototype.fnClick函数里面的for循环会得到 Uncaught TypeError: Cannot read property 'length' of undefined
这一句this.aBtn.length 引起了报错,为什么呢?在面向对象的时候,很多时候都是this引起的,要时刻警惕this到底指向哪儿去了,我们去原型方法(swtab.prototype.fnClick)里面console.log(this);
当我们点击了按钮之后 控制台输出了 <button type="button" name="button">选项二</button>
,也就说现在this 指向了点击的那个按钮,仔细想一想并没有错,虽然这个fnClick 是属于swtab 实例对象的,但是这个时候调用他的并不是swtab 实例对象,而是被点击的那个按钮,一个按钮自然是没有length 属性了;
现在问题来了,我要怎么保持这个this不变,哪怕是按钮点击调用也保持不变;没错,这时候你应该想起了 var _this=this
这种奇怪的写法;对,我们使用闭包保持对this 的引用;使用闭包关键就是函数套函数,内层使用外层的变量
现在我们对调用函数在改变一下
var _this=this;
this.aBtn[i].onclick=function(){
_this.fnClick();
};
现在我们在看一下 fnClick 函数里面的this指去哪儿
swtab {oparent: div#wrap, aBtn: HTMLCollection(3), aBox: HTMLCollection(3)}
可以看到,现在这个this 被保持引用了swtab 实例对象了,也没有报length
的错误了;但是我们却得到了一个新的错误 Uncaught TypeError: Cannot read property 'add' of undefined
看来是fnClick 函数最后两句报错了; 很显而易见的错,我们一开始就是把this 定为当前点击的按钮,但是现在这个函数里面的this 被闭包锁定了swtab 实例对象,当然classList 属性不存在了;
这就难受了,这个时候fnClick函数里面this同时存在2个状态,一个要指向实例对象,另一个是要指向当前点击按钮...如果我们能够从外部获取到当前点击的按钮那就好办了,所以我们可以在外部传入当前点击的按钮...
现在在改造一下
var _this=this;
this.aBtn[i].onclick=function(){
_this.fnClick(this);
};
swtab.prototype.fnClick= function(cur){
var _btn=cur;
for(var j=0;j<this.aBtn.length;j++){
this.aBtn[j].classList.remove('act');
this.aBox[j].classList.remove('act');
}
_btn.classList.add('act');
this.aBox[_btn.index].classList.add('act');
}
现在在试一下,函数正常运行;
最后完成的代码
window.onload=function (argument) {
new swtab('wrap');
}
function swtab(id){
this.oparent=document.getElementById(id);
this.aBtn=this.oparent.getElementsByTagName('button');
this.aBox=this.oparent.getElementsByClassName('box-i');
var _this=this;
for (var i = 0; i < this.aBtn.length; i++) {
this.aBox[i].index=i;
this.aBtn[i].index=i;
this.aBtn[i].onclick=function(){
_this.fnClick(this);
}
}
}
swtab.prototype.fnClick= function(cur){
var _btn=cur;
for(var j=0;j<this.aBtn.length;j++){
this.aBtn[j].classList.remove('act');
this.aBox[j].classList.remove('act');
}
_btn.classList.add('act');
this.aBox[_btn.index].classList.add('act');
}
现在总结一下,上面的东西看起来简单,但是里面的东西却是不少,既有对象的混合创建,又有闭包保证this 引用,还有this传递,从中也可看到,出的问题基本都是在这个this 的指向上,所以这个this 确实及其重要
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。