第十五节 JS面向对象实例及高级

时间:2021-04-11 19:19:00

实例:面向对象的选项卡

  把面向过程的程序,改写成面向对象的形式

    原则:不能有函数套函数,但可以有全局变量

    过程:

      onload —— 改写成 构造函数,其中window.onload的功能是在页面加载时“初始换整个程序”,类似于构造函数——初始化整个对象

      全局变量 —— 改写成 属性  

      函数 —— 改写成 方法

    改错:this、时间、闭包、传参

  对象与闭包:通过闭包传递this

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象选项卡</title>
<style>
#div1 button{background: white;}
#div1 button.active{background: yellow;}
#div1 div{
width: 200px;
height: 200px;
background: #cccccc;
display: none;
}
</style>
<script>
// window.onload = function () {
// var oDiv = document.getElementById('div1');
// var aBtn = oDiv.getElementsByTagName('button');
// var aDiv = oDiv.getElementsByTagName('div');
//
// for (var i = 0; i < aBtn.length; i++) {
// aBtn[i].index = i;
//
// aBtn[i].onclick = function () {
// for (var i = 0; i < aBtn.length; i++) {
// aBtn[i].className = '';
// aDiv[i].style.display = 'none';
// };
// this.className = 'active';
// aDiv[this.index].style.display = 'block';
// }
// }
// }; // //此时我们要把上述语句改写为面向对象的代码
// var aBtn = null; //定义全局变量
// var aDiv = null; //定义全局变量
//
// window.onload = function () {
// var oDiv = document.getElementById('div1');
// aBtn = oDiv.getElementsByTagName('button');
// aDiv = oDiv.getElementsByTagName('div');
//
// for (var i = 0; i < aBtn.length; i++) {
// aBtn[i].index = i;
// aBtn[i].onclick = fnClick; //函数没有嵌套,只是调用
// }
// };
//
// function fnClick() {
// for (var i = 0; i < aBtn.length; i++) {
// aBtn[i].className = '';
// aDiv[i].style.display = 'none';
// }
// this.className = 'active'; //此处this表示当前被点击的那个按钮
// aDiv[this.index].style.display = 'block';
// } //在进行一次改写
window.onload = function () {
new TabSwitch('div1');
}; function TabSwitch(id) {
var _this = this; var oDiv = document.getElementById(id); this.aBtn = oDiv.getElementsByTagName('button');
this.aDiv = oDiv.getElementsByTagName('div'); for (var i = 0; i < this.aBtn.length; i++) {
this.aBtn[i].index = i;
// this.aBtn[i].onclick = this.fnClick; //此时fnClick已经不是一个函数了,而是一个方法
this.aBtn[i].onclick = function () {
_this.fnClick(this);
}; }
} TabSwitch.prototype.fnClick = function (oBtn) {
// alert(this); //[object HTMLButtonElement] for (var i = 0; i < this.aBtn.length; i++) {
this.aBtn[i].className = '';
this.aDiv[i].style.display = 'none';
}
oBtn.className = 'active'; //此时this代表当前的对象
this.aDiv[oBtn.index].style.display = 'block';
};
</script>
</head>
<body>
<div id="div1">
<button class="active">aaa</button>
<button>bbb</button>
<button>ccc</button>
<div style="display: block">111</div>
<div>222</div>
<div>333</div>
</div>
</body>
</html>

Json方式的面向对象

  把方法包在一个Json里

<script>
// var json = {
// a:12,
// b:5,
// c:'abc',
// d:function () {alert('a');}
// };
// alert(json.a); //12
// json.d(); //a // var json = {
// a:12,
// show:function () {alert(this);} //此时this即指json
// };
// json.show(); //[object Object] //改写67.html,较67.html更加简单,但是缺点是不适合多个对象,比如再创建一个其他用户时,又需要在创建一个Json对象
var json = {
name:'haha',
qq:'123456789',
showName:function (){
alert('他的名字叫做:'+this.name);
},
showQQ:function () {
alert('他的QQ号为:'+this.qq);
}
};
json.showName();
json.showQQ();
</script>

    有人称作——命名空间

<script>
var json = {}; //json中包含另外3个json json.common = {};
json.fx = {};
json.site = {}; //在不同的json里面写同一个名字的函数,并且不会相互冲突
json.common.getUser = function () {
alert('a');
};
json.fx.getUser = function () {
alert('b');
};
json.site.getUser = function () {
alert('c');
}; json.common.getUser();
json.fx.getUser();
json.site.getUser();
</script>

    在公司里,把同一类方法,包在一起,既不会相互冲突,又方便查找

引用 :用“=”处理,和复制相似,但是两者指向的是同一个事物,而不是两个

 <script>
// var arr1 = [1,2,3];
// var arr2 = arr1;
//
// arr2.push(4);
//
// alert(arr2); //返回:1,2,3,4
// alert(arr1); //返回:1,2,3,4
//原因是上面“arr2 = arr1;”语句知识把两个变量都指向了同一个数组,并不是指向两个不同的数组,此时该语句叫做“引用”,若想使其不相同,如下:
var arr1 = [1,2,3];
var arr2 = []; for (var i = 0; i<arr1.length; i++){
arr2.push(arr1[i]);
}
arr2.push(4); alert(arr2); //返回:1,2,3,4
alert(arr1); //返回:1,2,3
</script>

  对象的继承

    什么是继承:在原有的基础上,略作修改,得到一个新的类,并且不影响原有类的功能

 <script>
function A() { //A的属性都写在函数内,构造函数
this.abc = 12;
} A.prototype.show = function () { //A的方法都现在原型上
alert(this.abc);
}; function B() {
A.call(this); //此时this表示new B() 即新建的B对象 用call来继承父级的属性
} // //继承属性
// var obj = new B();
// alert(obj.abc); //返回12 当B方法内没有定义“A.call(this);”时,返回的是undefined // //继承父级的属性我们可以用call,但是继承父级的方法我们用什么呢?如下:
// B.prototype = A.prototype;
// var obj = new B();
// obj.show(); //返回12 //但是问题在于当B存在自己的方法的时候时,如:
B.prototype = A.prototype; B.prototype.fn = function () {
alert('abc');
}; var objB = new B();
var objA = new B(); objA.fn(); //返回 abc 按理说不应该弹出该结果,因为B继承A;但是这样一来A B就互相继承了
</script>

拖拽和继承

  面向对象的拖拽——改写原有拖拽

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象的拖拽</title>
<style>
#div1{
width: 200px;
height: 200px;
background: red;
position: absolute;
}
</style>
<script>
// //面向过程的拖拽:
// window.onload = function () {
// var oDiv = document.getElementById('div1');
//
// oDiv.onmousedown = function (ev) {
// var oEvent = ev||event;
//
// var disX = oEvent.clientX-oDiv.offsetLeft;
// var disY = oEvent.clientY-oDiv.offsetTop;
//
// document.onmousemove = function (ev) {
// var oEvent = ev||event;
//
// oDiv.style.left = oEvent.clientX-disX+'px';
// oDiv.style.top = oEvent.clientY-disY+'px';
// };
// document.onmouseup = function () {
// document.onmousemove = null;
// document.onmouseup = null;
// };
// };
// }; //面向对象的拖拽:
//第一步:把所有的函数嵌套去掉
//第二步:把所有在多个函数中用到的变量,改为全局变量
// // var oDiv = null;
// var disX = 0;
// var disY = 0;
//
// function Drag(id) {
// oDiv = document.getElementById(id);
// oDiv.onmousedown = this.fnDown;
// };
//
// function fnDown(ev) {
// var oEvent = ev||event;
//
// disX = oEvent.clientX-oDiv.offsetLeft;
// disY = oEvent.clientY-oDiv.offsetTop;
//
// document.onmousemove = fnMove;
// document.onmouseup = fnUp;
// }
// function fnMove(ev) {
// var oEvent = ev||event;
//
// oDiv.style.left = oEvent.clientX-disX+'px';
// oDiv.style.top = oEvent.clientY-disY+'px';
// }
// function fnUp() {
// document.onmousemove = null;
// document.onmouseup = null;
// } //第三步:把“var oDiv = null;”注释掉,把对象的属性用this表示,即把div当做属性,然后合理添加this
// var oDiv = null;
window.onload = function () {
new Drag('div1');
}; function Drag(id) {
var _this = this; this.disX = 0;
this.disY = 0; this.oDiv = document.getElementById(id);
this.oDiv.onmousedown = function (ev) {
_this.fnDown(ev);
};
}; Drag.prototype.fnDown = function (ev) {
var _this = this; var oEvent = ev||event; this.disX = oEvent.clientX-this.oDiv.offsetLeft;
this.disY = oEvent.clientY-this.oDiv.offsetTop; document.onmousemove = function (ev) {
_this.fnMove(ev);
};
document.onmouseup = function (ev) {
_this.fnUp(ev);
};
};
Drag.prototype.fnMove = function (ev) {
var oEvent = ev||event; this.oDiv.style.left = oEvent.clientX-this.disX+'px';
this.oDiv.style.top = oEvent.clientY-this.disY+'px';
};
Drag.prototype.fnUp = function () {
document.onmousemove = null;
document.onmouseup = null;
}; //此时我们就可以把这些拖拽操作的方法,提取出来放到一个js文件中,方便之后需要使用只需 src引入 来使用即可
</script>
</head>
<body>
<div id="div1"></div>
</body>
</html>

  instanceof运算符:查看对象是否是某个类的实例

使用继承

  限制范围的拖拽类

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象的拖拽</title>
<style>
#div1{
width: 200px;
height: 200px;
background: red;
position: absolute;
}
#div2{
width: 200px;
height: 200px;
background: green;
position: absolute;
}
</style>
<script src="74Drag.js"></script>
<script src="74limitDrag.js"></script>
<script>
window.onload = function () {
new Drag('div1');
new LimitDrag('div2');
};
</script>
</head>
<body>
<div id="div1">普通拖拽</div>
<div id="div2">范围受限制的拖拽</div>
</body>
</html>
 function Drag(id) {
var _this = this; this.disX = 0;
this.disY = 0; this.oDiv = document.getElementById(id);
this.oDiv.onmousedown = function (ev) {
_this.fnDown(ev); return false; //拖拽一个物体时,防止另一个物体中的文字被选中
};
}; Drag.prototype.fnDown = function (ev) {
var _this = this; var oEvent = ev||event; this.disX = oEvent.clientX-this.oDiv.offsetLeft;
this.disY = oEvent.clientY-this.oDiv.offsetTop; document.onmousemove = function (ev) {
_this.fnMove(ev);
};
document.onmouseup = function (ev) {
_this.fnUp(ev);
};
};
Drag.prototype.fnMove = function (ev) {
var oEvent = ev||event; this.oDiv.style.left = oEvent.clientX-this.disX+'px';
this.oDiv.style.top = oEvent.clientY-this.disY+'px';
};
Drag.prototype.fnUp = function () {
document.onmousemove = null;
document.onmouseup = null;
};

74Drag.js

 // 面向对象的继承
function LimitDrag(id) {
Drag.call(this, id); //继承属性
} for (var i in Drag.prototype) {
LimitDrag.prototype[i] = Drag.prototype[i];
} //覆盖掉其父级“Drag”的fnMove方法,并重写该方法
LimitDrag.prototype.fnMove = function (ev) {
var oEvent = ev||event; //重新写继承的父级方法
var l = oEvent.clientX-this.disX;
var t = oEvent.clientY-this.disY; if (l < 0) { //重新父级方法,使拖拽左右不可拖出可视区
l = 0;
} else if (l>document.documentElement.clientWidth-this.oDiv.offsetWidth) {
l = document.documentElement.clientWidth-this.oDiv.offsetWidth;
} this.oDiv.style.left = l+'px';
this.oDiv.style.top = t+'px';
};

74limitDrag.js

继承的好处体现在,我把父级的方法继承过来,也可以根据自己需要重写所继承父级方法中不存在的东西

    构造函数伪装

      属性的继承

        原理:欺骗构造函数

      call的使用

 <script>
function show(a, b) {
alert('this是:'+this+'\na是:'+a+'\nb是:'+b);
} // show(12,5); //返回:this是:[object Window] a是:12 b是:5
// show.call(12, 5); //返回:this是:12 a是:5 b是:undefined 此时第一个参数即为this
show.call('haha', 12, 5); //this是:haha a是:12 b是:5
</script>

不使用“引用”的原型继承

 <script>
function A() { //A的属性都写在函数内,构造函数
this.abc = 12;
} A.prototype.show = function () { //A的方法都现在原型上
alert(this.abc);
}; function B() {
A.call(this); //此时this表示new B() 即新建的B对象 用call来继承父级的属性
} // //继承属性
// var obj = new B();
// alert(obj.abc); //返回12 当B方法内没有定义“A.call(this);”时,返回的是undefined // //继承父级的属性我们可以用call,但是继承父级的方法我们用什么呢?如下:
// B.prototype = A.prototype;
// var obj = new B();
// obj.show(); //返回12 // //但是问题在于当B存在自己的方法的时候时,如:
// B.prototype = A.prototype;
//
// B.prototype.fn = function () {
// alert('abc');
// };
//
// var objB = new B();
// var objA = new B();
//
// objA.fn(); //返回 abc 按理说不应该弹出该结果,因为B继承A;但是这样一来A B就互相继承了, // 其实上述也是“引用”惹的祸,解决办法如下:复制,让他们指向不同的物体
for (var i in A.prototype) {
B.prototype[i] = A.prototype[i];
}
B.prototype.fn = function () {
alert('abc');
}; var objB = new B();
var objA = new B(); objA.fn();
</script>

    原型链

      方法的继承

        原理:复制方法

      覆盖原型和方法复制

系统对象

  本地对象(非静态对象)

    什么是本地对象:简单来说,需要进行实例化才能使用系统自带的类,叫做本地对象,常用非静态对象:Object、Function、Array、String、Boolean、Number、Date、RegExp(正则)、Error

  内置对象(静态对象)

    什么是本地对象:简单来说,凡是不需要new就能使用的对象就是内置对象,如Global(虚假对象,因为基本上用不到)、Math

 var obj = new Math();   //错误用法,因为它属于静态对象
Math.ceil(); //正确使用方法

  前两个对象不依赖于JS的执行环境,可以说是JS本身的对象

  宿主对象(JS的运行环境)

    由浏览器提供的对象,其实就是DOM、BOM;

    Node.js运行环境是后台,所以其宿主对象就是后台的一系列对象。