什么是面向对象编程
-
用对象的思想去写代码,就是面向对象编程
过程式写法
面向对象写法
-
我们一直都在使用对象
数组Array 时间Date
面向对象编程(OOP)的特点
抽象:抓住核心问题
封装:只能通过对象来访问方法
继承:从已有对象上继承出新的对象
多态:多对象的不同形态
对象的组成
方法(行为、操作) —— 对象下面的函数:过程、动态的
属性 —— 对象下面的变量:状态、静态的
创建第一个面向对象程序
//var obj = {};
var obj = new Object(); //创建了一个空的对象
obj.name = '小明'; //属性
obj.showName = function(){ //方法
alert(this.name);
}
obj.showName();
-
为对象添加属性和方法
Object对象
this指向
创建两个对象:重复代码过多
//var obj = {};
var obj = new Object(); //创建了一个空的对象
obj.name = '小明'; //属性
obj.showName = function(){ //方法
alert(this.name);
}
obj.showName();
var obj2 = new Object();
obj2.name = '小强';
obj.showName = function(){
alert(this.name);
}
obj2.showName();
工厂方式
面向对象中的封装函数
//工厂方式:封装函数
function createPerson(name){
//1. 原料
var obj = new Object();
//2. 加工
obj.name = name;
obj.showName = function(){
alert(this.name);
};
//3. 出厂
return obj;
}
var p1 = createPerson('小明');
p1.showName();
var p2 = createPerson('小强');
p2.showName();
改成与系统对象类似的写法
1、首字母大写
2、New关键字提取
3、this指向为新创建的对象
/* 当new去调用一个函数:这个时候函数中的this就是创建出来的对象,而且函数的返回值直接就是this啦。(这叫做隐式返回) */
// new后面调用的函数:构造函数
function CreatePerson(name){
this.name = name;
this.showName = function(){
alert(this.name);
};
// return obj; 隐式返回,所以这一行不用写了
}
var p1 = new CreatePerson('小明');
p1.showName();
var p2 = new CreatePerson('小强');
p2.showName();
构造函数
用来创建对象的函数,叫做构造函数
存在的问题
1、对象的引用
2.浪费内存
/* 当new去调用一个函数:这个时候函数中的this就是创建出来的对象,而且函数的返回值直接就是this啦。(这叫做隐式返回) */
// new后面调用的函数:构造函数
function CreatePerson(name){
this.name = name;
this.showName = function(){
alert(this.name);
};
// return obj; 隐式返回,所以这一行不用写了
}
var p1 = new CreatePerson('小明');
p1.showName();
var p2 = new CreatePerson('小强');
p2.showName();
//alert(p1.showName == p2.showName); //false
/*
var a = [1, 2, 3];
var b = [1, 2, 3];
alert(a == b); //false;
*/
/*
var a = 5;
var b = a;
b += a;
alert(b); //8
alert(a); //5 基本类型:赋值的时候只是值的复制
*/
/*
var a = [1, 2, 3];
var b = a;
b.push(4);
alert(b); //[1, 2, 3, 4]
alert(a); //[1, 2, 3, 4] 对象类型:赋值不仅是值的复制,而且也是引用的传递
*/
/*
var a = [1, 2, 3];
var b = a;
b = [1, 2, 3, 4];
alert(b); //[1, 2, 3, 4]
alert(a); //[1, 2, 3] 只要赋值就会在内存中重新生成,所以a,b互补影响
*/
/*
var a = 5;
var b = 5;
alert(a == b); //true 基本类型的比较:只要值相同就可以
*/
/*
var a = [1, 2, 3];
var b = [1, 2, 3];
alert(a == b); //false 对象类型:值和引用都相同才行
*/
/*
var a = [1, 2, 3];
var b = a;
alert(a == b); //true
*/
原型 —— prototype
概念
去改写对象下面公用的方法或者属性,让公用的方法或者属性在内存中仅存在一份(好处:提高性能)
学习原型
类比:原型就是CSS中的class(普通方法就是CSS中的style)
普通方法的优先级比原型要高
原型可以复用,普通方法不可以复用
var arr = [];
arr.number = 10;
Array.prototype.number = 20;
alert(arr.number); //10 普通方法的优先级高于原型
//原型:prototype:要写在构造函数下面
var arr = [1, 2, 3, 4, 5];
var arr2 = [2, 2, 2, 2, 2];
Array.prototype.sum = function(){
var result = 0;
for(var i=0; i<this.length; i++){
result += this[i];
}
return result;
}
/*
arr.sum = function(){
var result = 0;
for(var i=0; i<this.length; i++){
result += this[i];
}
return result;
}
alert(arr.sum()); //15
*/
alert(arr.sum());
alert(arr2.sum());
通过原型改写工厂方式原则:
相同的属性和方法可以加在原型上
混合的编程模式
function CreatePerson(name){
this.name = name; //变化的,不能公用的属性不能写在原型上
}
CreatePerson.prototype.showName = function(){
alert(this.name);
}
var p1 = new CreatePerson('小明');
var p2 = new CreatePerson('小强');
alert(p1.showName == p2.showName); //true
混合的编程模式
//面向对象的写法
function 构造函数(){
this.属性
}
构造函数.原型.方法 = function(){};
//面向对象的使用
var 对象1 = new 构造函数();
对象1.方法();
总结面向对象写法:构造函数加属性,原型加方法
面向对象的选项卡
原则
先写出普通的写法,然后改成面向对象写法
-
普通方法变型
尽量不要出现函数嵌套函数
可以有全局变量
把onload中不是赋值的语句放到单独的函数中
-
改成面向对象
全局变量就是属性
函数就是方法
onload中创建对象
改this指向问题,要尽量让面向对象中的this指向对象
1、先写出普通方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象的选项卡</title>
<style>
#div1 div { width: 200px; height: 200px; border: 1px solid #000; display: none; }
.active { background: red; }
</style>
<script>
window.onload = function(){
var oParent = document.getElementById('div1');
var aInput = oParent.getElementsByTagName('input');
var aDiv = oParent.getElementsByTagName('div');
for(var i=0; i<aInput.length; i++){
aInput[i].index = i;
aInput[i].onclick = function(){
for(var i=0; i<aInput.length; i++){
aInput[i].className = '';
aDiv[i].style.display = 'none';
}
this.className = 'active';
aDiv[this.index].style.display = 'block';
}
}
}
</script>
</head>
<body>
<div id="div1">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">11111</div>
<div>22222</div>
<div>33333</div>
</div>
</body>
</html>
2、普通方法变型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象的选项卡</title>
<style>
#div1 div { width: 200px; height: 200px; border: 1px solid #000; display: none; }
.active { background: red; }
</style>
<script>
var oParent = null;
var aInput = null;
var aDiv = null;
window.onload = function(){
oParent = document.getElementById('div1');
aInput = oParent.getElementsByTagName('input');
aDiv = oParent.getElementsByTagName('div');
init();
};
function init(){ //初始化的函数方法
for(var i=0; i<aInput.length; i++){
aInput[i].index = i;
aInput[i].onclick = change;
}
}
function change(){
for(var i=0; i<aInput.length; i++){
aInput[i].className = '';
aDiv[i].style.display = 'none';
}
this.className = 'active';
aDiv[this.index].style.display = 'block';
}
/*
- 普通方法变型
- 尽量不要出现函数嵌套函数
- 可以有全局变量
- 把onload中不是赋值的语句放到单独的函数中
*/
</script>
</head>
<body>
<div id="div1">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">11111</div>
<div>22222</div>
<div>33333</div>
</div>
</body>
</html>
关于this的指向
oDiv.onclick = function(){
this: oDiv
};
---
oDiv.onclick = show;
function show(){
this: oDiv
}
---
oDiv.onclick = function(){
show();
};
function show(){
this: window
}
改写成面向对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象bianxie</title>
<style>
#div1 div { width: 200px; height: 200px; border: 1px solid #000; display: none; }
.active { background: red; }
</style>
<script>
var oParent = null;
var aInput = null;
var aDiv = null;
window.onload = function(){
var t1 = new Tab();
t1.init();
};
function Tab(){
this.oParent = document.getElementById('div1');
this.aInput = this.oParent.getElementsByTagName('input');
this.aDiv = this.oParent.getElementsByTagName('div');
}
Tab.prototype.init = function(){
var This = this;
for(var i=0; i<this.aInput.length; i++){
this.aInput[i].index = i;
this.aInput[i].onclick = function(){
This.change(this);
};
}
}
Tab.prototype.change = function(obj){
for(var i=0; i<this.aInput.length; i++){
this.aInput[i].className = '';
this.aDiv[i].style.display = 'none';
}
obj.className = 'active';
this.aDiv[obj.index].style.display = 'block';
}
/*
- 改成面向对象
- 全局变量就是属性
- 函数就是方法
- onload中创建对象
- 改this指向问题:注意事件或者是定时器里面的this。要尽量保持面向对象中的this指向对象
*/
</script>
</head>
<body>
<div id="div1">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">11111</div>
<div>22222</div>
<div>33333</div>
</div>
</body>
</html>
面向对象的复用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象bianxie</title>
<style>
#div1 div, #div2 div { width: 200px; height: 200px; border: 1px solid #000; display: none; }
.active { background: red; }
</style>
<script>
var oParent = null;
var aInput = null;
var aDiv = null;
window.onload = function(){
var t1 = new Tab('div1');
t1.init();
var t2 = new Tab('div2');
t2.init();
t2.autoplay();
};
function Tab(id){
this.oParent = document.getElementById(id);
this.aInput = this.oParent.getElementsByTagName('input');
this.aDiv = this.oParent.getElementsByTagName('div');
this.iNow = 0;
}
Tab.prototype.init = function(){
var This = this;
for(var i=0; i<this.aInput.length; i++){
this.aInput[i].index = i;
this.aInput[i].onclick = function(){
This.change(this);
};
}
}
Tab.prototype.change = function(obj){
for(var i=0; i<this.aInput.length; i++){
this.aInput[i].className = '';
this.aDiv[i].style.display = 'none';
}
obj.className = 'active';
this.aDiv[obj.index].style.display = 'block';
}
Tab.prototype.autoplay = function(){
var This = this;
setInterval(function(){
if(This.iNow == This.aInput.length - 1){
This.iNow = 0;
} else {
This.iNow ++;
}
for(var i=0; i<This.aInput.length; i++){
This.aInput[i].className = '';
This.aDiv[i].style.display = 'none';
}
This.aInput[This.iNow].className = 'active';
This.aDiv[This.iNow].style.display = 'block';
}, 2000)
}
</script>
</head>
<body>
<div id="div1">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">11111</div>
<div>22222</div>
<div>33333</div>
</div>
<div id="div2">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">11111</div>
<div>22222</div>
<div>33333</div>
</div>
</body>
</html>
面向对象的拖拽
注意
Event对象(event对象一定要写到事件函数里面)
事件函数中用来阻止默认行为的return false也要写到事件函数里面
!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象编写拖拽</title>
<style>
#div1 { width: 100px; height: 100px; background: red; position: absolute; }
</style>
<script>
/* 普通拖拽
window.onload = function(){
var oDiv = document.getElementById('div1');
var disX = 0;
var disY = 0;
oDiv.onmousedown = function(ev){
var ev = ev || window.event;
disX = ev.clientX - oDiv.offsetLeft;
disY = ev.clientY - oDiv.offsetTop;
document.onmousemove = function(ev){
var ev = ev || window.event;
oDiv.style.left = ev.clientX - disX + 'px';
oDiv.style.top = ev.clientY - disY + 'px';
}
document.onmouseup = function(){
document.onmousemove = null;
document.onmouseup = null;
}
return false;
}
}
*/
/* 第一步:普通方法变型
//先变型
var oDiv = null;
var disX = 0;
var disY = 0;
window.onload = function(){
oDiv = document.getElementById('div1');
init();
}
function init(){
oDiv.onmousedown = fnDown;
}
function fnDown(ev){
var ev = ev || window.event;
disX = ev.clientX - oDiv.offsetLeft;
disY = ev.clientY - oDiv.offsetTop;
document.onmousemove = fnMove;
document.onmouseup = fnUp;
return false;
}
function fnMove(ev){
var ev = ev || window.event;
oDiv.style.left = ev.clientX - disX + 'px';
oDiv.style.top = ev.clientY - disY + 'px';
}
function fnUp(){
document.onmousemove = null;
document.onmouseup = null;
}
*/
//改成面向对象
window.onload = function(){
var d1 = new Drag('div1');
d1.init();
}
function Drag(id){
this.oDiv = document.getElementById(id);
this.disX = 0;
this.dixY = 0;
}
Drag.prototype.init = function(){
var This = this;
this.oDiv.onmousedown = function(ev){
var ev = ev || window.event;
This.fnDown(ev);
return false;
};
}
Drag.prototype.fnDown = function(ev) {
var ev = ev || window.event;
var This = this;
this.disX = ev.clientX - this.oDiv.offsetLeft;
this.disY = ev.clientY - this.oDiv.offsetTop;
document.onmousemove = function(ev){
var ev = ev || window.event;
This.fnMove(ev);
};
document.onmouseup = this.fnUp;
}
Drag.prototype.fnMove = function(ev){
this.oDiv.style.left = ev.clientX - this.disX + 'px';
this.oDiv.style.top = ev.clientY - this.disY + 'px';
}
Drag.prototype.fnUp = function(){
document.onmousemove = null;
document.onmouseup = null;
}
</script>
</head>
<body>
<div id="div1"></div>
</body>
</html>
本课练习
为数组对象添加求和,最大值
为字符串对象添加判断是不是最后一个字母
面向对象的选项卡
给选项卡添加一个自动播放的方法
任意学过的效果改写成面向对象
面向对象的面试题
高级面向对象
包装对象
JS基于原型的程序
String Number Boolean
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
</style>
<script>
/*
function Aaa(){
this.name='小明';
}
Aaa.prototype.showName=function(){
alert(this.name);
}
var a1 = new Aaa();
a1.showName();
var arr = new Array();
arr.push();
arr.sort();
// 在js源码中:系统对象也是基于原型的程序
function Array(){
this.length = 0;
}
Array.prototype.push = function(){};
Array.prototype.sort = function(){};
*/
//尽量不要去修改或添加系统对象下面的方法和属性
var arr = [1, 2, 3];
//Arr.prototype.push = function(){}; //加上这句话,就修改了源码中的push方法,那么后面那一句中的4、5、6就添加不进去了
Array.prototype.push = function(){ //自己写一个数组的push方法
//this : 1, 2, 3
//arguments: 4, 5, 6
for(var i=0; i<arguments.length; i++){
this[this.length] = arguments[i];
}
return this.length;
}
arr.push(4, 5, 6);
alert(arr);
</script>
</head>
<body>
</body>
</html>
给基本类型添加方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
</style>
<script>
/*
var str = 'hello';
alert( typeof str); //string
str.charAt(0); //字符串为什么下面会有方法呢?
str.indexOf('e');
*/
//包装对象:基本类型都有自己对应的包装对象(String, Number, Boolean)(null和undefined没有包装对象)
/*
var str = new String('hello');
alert(typeof str); //object
alert(str.charAt(1)); //弹出e
String.prototype.charAt = function(){}; //基本类型的方法都是放在它们的包装对象上
*/
/*
var str = 'hello'; //str是字符串
str.charAt(0); //基本类型找到对应的包装对象类型,然后包装对象把所有的属性和方法给了基本类型,然后包装对象消失
*/
//给基本类型添加对象的时候,就是把方法添加到基本类型对应的包装对象下面
var str = 'hello';
String.prototype.lastValue = function(){
return this.charAt(this.length-1);
};
alert(str.lastValue()); //o
var str1 = 'hello';
str1.number = 10; //在str1的包装对象上创建了一个number,然后包装对象就消失了
alert(str1.number); //undefined 再去调用这句话的时候,此时又重新创建了一个对象,这个对象与刚才那个对象不是同一个
</script>
</head>
<body>
</body>
</html>
原型链
实例对象与原型之间的连接,叫做原型链
proto (隐式连接)
Object对象类型是原型链的最外层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象中的原型链</title>
<style></style>
<script>
//原型链:实例对象与原型之间的连接叫作原型链
//_proto_ (隐式连接)
//原型链的最外层是Object.prototype
function Aaa(){
}
Aaa.prototype.num = 10;
var a1 = new Aaa();
alert(a1.num); //10 a1现在它自己下面找,找不到这个num,然后又通过原型链到了Aaa.prototype,到它下面去找,最终找到了num
function Bbb(){
this.num = 20;
}
Bbb.prototype.num = 10;
var b1 = new Bbb();
alert(b1.num); //20 简单来讲,实例上的方法的优先级高于原型上的方法;本质上来讲,是从b1下面找num直接就找到了,于是就不需要再进一步通过原型链去找到Bbb.prototype上的num了。
function Ccc(){};
var c1 = new Ccc();
Object.prototype.num = 30; //弹出30 c1下找不到num;顺着原型链找到Ccc也找不到num,继续顺着原型链往上找,找到Object.prototype,找到了num
alert(c1.num);
</script>
</head>
<body>
</body>
</html>
面向对象的一些属性和方法
hasOwnProperty()看是不是对象自身下面的属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象中的原型链</title>
<style></style>
<script>
//hasOwnProperty: 看是不是对象自身下面的属性
var arr = [];
arr.num = 10;
Array.prototype.num2 = 20;
alert(arr.hasOwnProperty('num')); //true
alert(arr.hasOwnProperty('num2')); //false
alert(arr.hasOwnProperty == Object.prototype.hasOwnProperty); //true hasOwnProperty其实是Object.prototype下面的方法
</script>
</head>
<body>
</body>
</html>
constructor查看对象的构造函数
每个原型都会自动添加constructor属性
for in 的时候,有些属性是找不到的
避免修改constructor属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象中的原型链</title>
<style></style>
<script>
//constructor: 查看对象的构造函数
function Aaa(){
}
var a1 = new Aaa();
alert(a1.constructor); //function Aaa
var arr = [];
alert(arr.constructor == Array); //true
/*
在写完一个构造函数之后
function Aaa(){}
程序会自动添加一句话:
Aaa.prototype.constructor = Aaa; //每一个函数都会有的,都是自动生成的
这时候new一个Aaa
var a1 = new Aaa();
alert(a1.constructor); //弹出Aaa
如果手动写一句:
Aaa.prototype.constructor = Array;
那么再去弹a1.constructor的话,弹出的就是Array了。
而hasOwnProperty这个属性是添加在Object.prototype下面的,所以每个对象下面去查这个hasOwnProperty也都会有。但是hasOwnProperty这个方法不是在具体对象下面的,而都是沿着原型链找到Object.prototype身上找到的。跟constructor是不一样的。
*/
function Bbb(){
}
Bbb.prototype.name = '小明';
Bbb.prototype.age = 20;
var b1 = new Bbb();
alert(b1.constructor); //function Bbb()
//下面这种写法就无意识地改变了c1的constructor,因为json直接赋值给了Ccc.prototype,而不是向上面那段代码是添加操作。那么原本系统自动生成的那句话Ccc.prototype.constructor = Ccc这句话就被覆盖掉了。然后通过c1.constructor去找的话,其实找的就是这个json所对应的constructor了。
function Ccc(){
}
Ccc.prototype = {
name: '小明',
age: 20
}
var c1 = new Ccc();
alert(c1.constructor); //function Object
//为了避免上述问题,应该注意,用json简写的时候,要把constructor修正过来,如下:
function Ddd(){
}
Ddd.prototype = {
constructor: Ddd, //通过这一句来修正一下
name: '小明',
age: 20
}
var d1 = new Ddd();
alert(d1.constructor); //Ddd
</script>
</head>
<body>
</body>
</html>
for in循环,有些属性找不到
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面向对象中的原型链</title>
<style></style>
<script>
function Aaa(){
}
Aaa.prototype.name = 10;
Aaa.prototype.constructor = Aaa;
for (var attr in Aaa.prototype){
alert(attr); //只能找到自己添加的,系统自动生成的比如constructor,for in循环是找不到的
}
</script>
</head>
<body>
</body>
</html>
instanceof运算符
对象与构造函数在原型链上是否有关系
可以用来作类型判断
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>instanceof()</title>
<style></style>
<script>
// instanceof: 对象与构造函数在原型链上是否有关系
function Aaa(){
}
var a1 = new Aaa();
alert(a1 instanceof Aaa); //true 看a1是否与Aaa在同一个原型链上
alert(a1 instanceof Array); //false
alert(a1 instanceof Object); //true
</script>
</head>
<body>
</body>
</html>
toString()
object上的方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>toString()</title>
<style></style>
<script>
// toString():
// 位置:系统对象下面都是自带的;自己写的对象都是通过原型链找Object下面的
// 作用:把对象转成字符串
var arr = [];
alert(arr.toString); //找得到
alert(arr.toString == Object.prototype.toString); //false 系统对象下面的toString不在Object原型上
function Aaa(){
}
var a1 = new Aaa();
alert(a1.toString); //找得到
alert(a1.toString == Object.prototype.toString); //true 构造函数生成的对象的toString方法在Object原型上
var arr = [1, 2, 3];
alert(typeof arr.toString()); //string
alert(arr.toString()); //"1, 2, 3"
//因为知道toString在哪儿了,所以可以手动修改toString方法
Array.prototype.toString = function(){
return this.join('+');
}
alert(arr.toString()); //"1+2+3"
var num = 255;
alert(num.toString(16)); //"ff" 将255转成16进制
//利用toString进行类型判断(用constructor和instanceof也都可以进行类型判断)。推荐toString来判断例如数组的类型
var arr = []
var date = new Date;
var json = {}
var reg = new RegExp;
var n = null;
alert(Object.prototype.toString.call(arr)); //[object Array]
alert(Object.prototype.toString.call(date)); //[object Date]
alert(Object.prototype.toString.call(json)); //[object Object]
alert(Object.prototype.toString.call(reg)); //[object RegExp]
alert(Object.prototype.toString.call(n)); //[object Null]
//判断类型时直接进行比较就可以了,例如判断arr是否是数组:
alert(Object.prototype.toString.call(arr) == '[object Array]'); //true
//举例说明用instanceof和constructor来判断数组失效,但是toString依然有效的例子
window.onload = function(){
var oF = document.createElement('iframe');
document.body.appendChild(oF);
var ifArray = window.frames[0].Array; //ifArray就是iframe里面的数组
var arr = new ifArray(); //ifArray就是iframe里面的数组 这时候跨页面了
alert(arr.constructor == Array); //false constructor判断iframe下面的数组失效
alert(arr instanceof Array); //false 判断失效
alert(Object.prototype.toString.call(arr) == '[object Array]'); //true 判断依然有效
}
</script>
</head>
<body>
</body>
</html>
对象的继承
什么是继承
在原有对象的基础上,略作修改,得到一个新的对象
不影响原有对象的功能
如何添加继承
属性:call
方法:for in
例子:继承的拖拽
!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>继承</title>
<style></style>
<script>
// 继承:子类不影响父类;子类可以继承父类的一些功能(实现代码复用)
function CreatePerson(name, sex){ //父类
this.name = name;
this.sex = sex;
}
CreatePerson.prototype.showName = function(){
alert(this.name);
}
var p1 = new CreatePerson('小明', '男');
p1.showName();
//属性的继承:调用父类的构造函数 用call改this指向
//方法的继承:用for in循环
function CreateStar(name, sex, job){ //子类
// this.name = name;
// this.sex = sex;
CreatePerson.call(this, name, sex); //调用父类构造函数 需要修改指向
this.job = job;
}
/*
CreateStar.prototype = CreatePerson.prototype; //将父类的原型赋给子类,那么子类就获得了父类下所有的属性值,实现了方法的继承 但是这里有对象的引用问题,造成互相干涉
例如:
CreateStar.prototype.showJob = function(){}
上面子类的原型添加的方法,那么父类CreatePerson.prototype下面也有了showJob的方法
*/
//方法的继承应该用for in循环,将父类的所有属性拷贝给子类,这叫作“拷贝继承”。jQuery也是采用拷贝继承extend
function extend(obj1, obj2){
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
extend(CreateStar.prototype, CreatePerson.prototype )
var p2 = new CreateStar('黄晓明', '男', '演员')
p2.showName();
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>继承</title>
<style></style>
<script>
var a = {
name: '小明'
};
//var b = a;
// var b = {};
// for(var attr in a){
// b[attr] = a[attr];
// }
// b.name = "小强";
// alert(a.name);
//将对象属性的拷贝封装成一个函数
function extend(obj1, obj2){
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
var b = {};
extend(b, a);
b.name = '小强';
alert(b.name);
alert(a.name);
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>继承</title>
<style></style>
<script>
var a = [1, 2, 3];
var b = a;
// b.push(4); //修改 a、b之间引用链条存在
// alert(a); //1, 2, 3, 4 a被b影响了
b = [1, 2, 3, 4]; //重新赋值,内存中这个b又重新生成了。a和b之间这个引用的链条已经断开,a、b没有关系
alert(a); //1, 2, 3 a未被影响
//在for in循环中,子类原型的方法也直接等于了父类原型的方法,因为方法是函数,也是个对象,为什么这种“对象 = 对象”没有互相干涉呢?这是因为函数有些特殊,不能被修改,只能被重新赋值
</script>
</head>
<body>
</body>
</html>
继承的拖拽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {width: 100px; height: 100px; background: red; position: absolute;}
#div2 {width: 100px; height: 100px; background: yellow; position: absolute; left: 100px;}
</style>
<script src="jquery-1.11.1.js"></script>
<script>
window.onload = function(){
var d1 = new Drag('div1');
d1.init();
var d2 = new ChildDrag('div2');
d2.init();
}
//父类的面向对象的拖拽开始
function Drag(id){ //父类
this.obj = document.getElementById(id);
this.disX = 0;
this.disY = 0;
}
Drag.prototype.init = function(){
var This = this;
this.obj.onmousedown = function(ev){
var ev = ev || window.event;
This.fnDown(ev);
document.onmousemove = function(ev){
var ev = ev || window.event;
This.fnMove(ev);
}
document.onmouseup = function(){
This.fnUp();
}
return false;
}
}
Drag.prototype.fnDown = function(ev){
this.disX = ev.clientX - this.obj.offsetLeft;
this.disY = ev.clientY - this.obj.offsetTop;
}
Drag.prototype.fnMove = function(ev){
this.obj.style.left = ev.clientX - this.disX + 'px';
this.obj.style.top = ev.clientY - this.disY + 'px';
}
Drag.prototype.fnUp = function(){
document.onmousemove = null;
document.onmouseup = null;
}
//父类的面向对象的拖拽结束
//子类继承父类开始
function ChildDrag(id){ //子类
Drag.call(this, id);
}
extend(ChildDrag.prototype, Drag.prototype);
//通过改变ChildDrap原型下的fnMove,给子类添加了限制拖拽时,元素超出可视区左右边界的功能
ChildDrag.prototype.fnMove = function(ev){
var L = ev.clientX - this.disX;
var T = ev.clientY - this.disY;
if(L < 0){
L = 0;
} else if (L > document.documentElement.clientWidth - this.obj.offsetWidth){
L = document.documentElement.clientWidth - this.obj.offsetWidth;
}
this.obj.style.left = L + 'px';
this.obj.style.top = T + 'px';
}
function extend(obj1, obj2){
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
//子类继承父类结束
</script>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
</body>
</html>
继承的其他形式
1、类式继承
利用构造函数(类)继承的方式
2、原型继承
借助原型来实现对象继承对象
类式继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
</style>
<script>
//类:JS是没有类的概念的,可以把JS中的构造函数看做是类
//要做属性和方法继承的时候,要分开继承
function Aaa(){ //父类
this.name = '小明';
this.arr = [1, 2, 3];
}
Aaa.prototype.showName = function(){
alert(this.name);
}
function Bbb(){ //子类
}
Bbb.prototype = new Aaa(); //这句话就是类式继承
//只写上面一句话是有问题的,修改了constructor指向。
//Bbb.prototype.constructor = Bbb; //修正指向问题
var b1 = new Bbb();
b1.showName();
alert(b1.name);
alert(b1.constructor); //弹出的并不是Bbb,而是Aaa。只有写了上面修正指向的那句话,这里才会变成Bbb
//真正规范的类式继承要用下面几句话:
// function Bbb(){ //这里只继承了属性
// Aaa.call(this)
// }
//var F = function(){}; //创建一个空的构造函数
//F.prototype = Aaa.prototype;
//Bbb.prototype = new F(); //这里只继承了方法
//Bbb.prototype.constructor = Bbb;
b1.arr.push(4);
var b2 = new Bbb();
alert(b2.arr); //[1, 2, 3, 4] 这里看到上面的b1.arr.push(4)影响到了这里 要避免这种问题,应该用上面的类式继承的规范写法才行
</script>
</head>
<body>
</body>
</html>
原型继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>原型继承</title>
<style>
</style>
<script>
var a = {
name: '小明'
}
var b = cloneObj(a); //让b继承a
b.name = '小强'; //这一句不会更改a的name,因为这一句在b的下面又新建了一个属性name,值为“小强”
alert(b.name); //小强
alert(a.name); //小明
function cloneObj(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
</script>
</head>
<body>
</body>
</html>
拷贝继承:通用型的 有new或无new的时候都可以
类式继承:适合通过new构造函数形式
原型继承:比较适合无new的对象
组件开发
1、对象的多种表现形式
提高对象的复用性
如何配置参数和默认参数
例子:拖拽
例子:弹窗
2、什么是组件?
对面向对象的深入应用(UI组件、功能组件)
将配置参数、方法、事件、三者进行分离
3、创建自定义事件
有利于多人协作开发代码
如何去挂载自定义事件与事件函数
4、例子:基于JQ的选项卡的组件开发模式
trigger() extend()等方法的使用
5、本课练习
组件开发的练习
http://tangram.baidu.com/magic/
如何配置参数和默认参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件开发中的参数问题</title>
<script>
//用json来解决参数数量问题
function show(opt){
}
show({
id: 'div1',
toDown: function(){},
toUp: function(){}
});
//用下面的方法解决参数顺序问题
var a = { //配置参数
//name: '小明'
name: '小明'
}
var b = { //默认参数
name: '小强'
}
extend(b, a); //当有配置的时候,走配置,没有配置的时候,走默认
alert(b.name);
function extend(obj1, obj2){
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
</script>
</head>
<body>
</body>
</html>
拖拽的组件开发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
#div1 {width: 100px; height: 100px; background: red; position: absolute;}
#div2 {width: 100px; height: 100px; background: yellow; position: absolute; left: 100px;}
#div3 {width: 100px; height: 100px; background: blue; position: absolute; left: 200px;}
#div4 {width: 100px; height: 100px; background: green; position: absolute; left: 300px;}
</style>
<script src="jquery-1.11.1.js"></script>
<script>
/*
组件开发:像兄弟之间的关系(代码复用的一种形式)
*/
window.onload = function(){
var d1 = new Drag();
d1.init('div1');
var d2 = new Drag();
d2.init('div2', { //配置参数
toDown: function(){
document.title = 'hello';
}
});
var d3 = new Drag();
d3.init('div3', { //配置参数
toDown: function(){
document.title = '妙味';
},
toUp: function(){
document.title = '课堂';
}
});
var d4 = new Drag();
d4.init('div4', { //配置参数
toUp: function(){
document.title = 'byebye';
}
});
}
function Drag(){
this.obj = null;
this.disX = 0;
this.disY = 0;
this.settings = { //默认参数
//id不应该属于配置参数当中,它属于必填项
toDown: function(){},
toUp: function(){}
}
}
Drag.prototype.init = function(id, opt){
var This = this;
this.obj = document.getElementById(id);
extend(this.settings, opt); //用配置覆盖默认
this.obj.onmousedown = function(ev){
var ev = ev || window.event;
This.fnDown(ev);
This.settings.toDown();
document.onmousemove = function(ev){
var ev = ev || window.event;
This.fnMove(ev);
}
document.onmouseup = function(){
This.fnUp();
This.settings.toUp();
}
return false;
}
}
Drag.prototype.fnDown = function(ev){
this.disX = ev.clientX - this.obj.offsetLeft;
this.disY = ev.clientY - this.obj.offsetTop;
}
Drag.prototype.fnMove = function(ev){
this.obj.style.left = ev.clientX - this.disX + 'px';
this.obj.style.top = ev.clientY - this.disY + 'px';
}
Drag.prototype.fnUp = function(){
document.onmousemove = null;
document.onmouseup = null;
}
function extend(obj1, obj2){
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
</script>
</head>
<body>
<div id="div1"></div> <!-- 红色方块是老大 -->
<div id="div2"></div> <!-- 黄色方块是老二 黄色的按下之后title有一个变化 -->
<div id="div3"></div> <!-- 老三 按下title变化,抬起title变化 -->
<div id="div4"></div> <!-- 老四 鼠标抬起时title有变化 -->
</body>
</html>
弹窗的组件开发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>弹窗的组件开发</title>
<style>
* {margin : 0; padding; 0}
.login {background: white; border: 1px #000 solid; position: absolute; left: 0; top: 0; z-index: 2;}
.title {height: 30px; background: gray; color: white;}
.title .close {float: right;}
#mask {width: 500px; height: 500px; background: black; filter: alpha(oapcity=50); opacity: 0.5; position: absolute; left: 0; top: 0; z-index: 1;}
</style>
<script>
window.onload = function(){
var aInput = document.getElementsByTagName('input');
aInput[0].onclick = function(){
var d1 = new Dialog();
d1.init({//配置参数
iNow: 0,
title: '登陆'
});
}
aInput[1].onclick = function(){
var d2 = new Dialog();
d2.init({//配置参数
iNow: 1,
w : 100,
h : 400,
dir: 'right',
title: '公告'
});
}
aInput[2].onclick = function(){
var d3 = new Dialog();
d3.init({//配置参数
iNow: 2,
mask: true
});
}
}
function Dialog(){
this.oLogin = null;
this.settings = { //默认参数
w: 300,
h: 300,
dir: 'center',
title: '',
mask: false
}
}
Dialog.prototype.json = {}; //防止添加多个弹窗
Dialog.prototype.init = function(opt){
extend(this.settings, opt);
if(this.json[opt.iNow] == undefined){
this.json[opt.iNow] = true;
}
if(this.json[opt.iNow]){ //防止添加多个弹窗
this.create();
this.fnClose();
if(this.settings.mask){
this.createMask();
}
this.json[opt.iNow] = false;
}
}
Dialog.prototype.create = function(){
this.oLogin = document.createElement('div');
this.oLogin.className = 'login';
this.oLogin.innerHTML = '<div class="title"><span>' + this.settings.title + '</span><span class="close">X</span></div><div class="content"></div>';
document.body.appendChild(this.oLogin);
this.setData();
}
Dialog.prototype.setData = function(){
this.oLogin.style.width = this.settings.w + 'px';
this.oLogin.style.height = this.settings.h + 'px';
if(this.settings.dir == 'center'){
this.oLogin.style.left = (viewWidth() - this.oLogin.offsetWidth)/2 + 'px';
this.oLogin.style.top = (viewHeight() - this.oLogin.offsetHeight)/2 + 'px';
} else if (this.settings.dir == 'right'){
this.oLogin.style.left = (viewWidth() - this.oLogin.offsetWidth) + 'px';
this.oLogin.style.top = (viewHeight() - this.oLogin.offsetHeight) + 'px';
}
}
Dialog.prototype.fnClose = function(){
var oClose = this.oLogin.getElementsByTagName('span')[1];
var This = this;
oClose.onclick = function(){
document.body.removeChild(This.oLogin);
if(This.settings.mask){
document.body.removeChild(This.oMask);
}
This.json[This.settings.iNow] = true;
}
}
Dialog.prototype.createMask = function(){
var oMask = document.createElement('div');
oMask.id = 'mask';
document.body.appendChild(oMask);
this.oMask = oMask;
oMask.style.width = viewWidth() + 'px';
oMask.style.height = viewHeight() + 'px';
}
function extend(obj1, obj2){
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
function viewWidth(){
return document.documentElement.clientWidth;
}
function viewHeight(){
return document.documentElement.clientHeight;
}
</script>
</head>
<body>
<input type="button" value="1">
<input type="button" value="2">
<input type="button" value="3">
<!-- <div class="login">
<div class="title">
<span>标题</span><span class="close">X</span>
</div>
<div class="content"></div>
</div> -->
<!-- <div id="mask"></div> -->
</body>
</html>
理解自定义事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义事件</title>
<script>
// 自定义事件:主要是跟函数有关系,让函数能够具备事件的某些特性
// 三个人都针对show来添加功能。都添加到一个地方会乱,于是三个人分开来写
/*
function show(){
alert(1); //第一个人写
}
function show(){
alert(2); //第二个人写
}
function show(){
alert(3); //第三个人写
}
show(); //只能执行第三个人写的,因为函数不能修改只能覆盖
*/
//看看是否能让函数具备事件的特性,多人写的可以绑定上去,就解决了多人协作的问题。原理就如以下情形。当然以下代码不是添加自定义事件的代码,只是为了方便理解:
/*
window.addEventListener('show', function(){
alert(1); //第一个人写的
}, false);
window.addEventListener('show', function(){
alert(2); //第二个人写的
}, false);
window.addEventListener('show', function(){
alert(3); //第三个人写的
}, false);
show(); //主动触发自定义事件
*/
</script>
</head>
<body>
</body>
</html>
用原生js实现自定义事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义事件</title>
<script>
//自定义事件重点在于将函数和元素以及事件名称建立关系,在执行某个函数的时候,主动触发某个事件名称下面相关的所有函数
window.onload = function(){
var oDiv = document.getElementById('div1');
var oSpan = document.getElementById('span1');
bindEvent(oDiv, 'click', function(){
alert(1);
})
bindEvent(oDiv, 'click', function(){
alert(2);
})
bindEvent(oSpan, 'show', function(){ //这里的show就是个自定义事件
alert(3);
})
bindEvent(oSpan, 'show', function(){ //这里的show就是个自定义事件
alert(4);
})
bindEvent(oSpan, 'hide', function(){ //这里的show就是个自定义事件
alert(5);
})
fireEvent(oSpan, 'show'); //主动触发,弹出3, 4
fireEvent(oDiv, 'click'); //主动触发,弹出1, 2
}
function bindEvent(obj, events, fn){
//fn: 看作一本书 《西游记》
//obj: 相当于图书馆的楼层 文学楼
//events: 相当于书架 古典文学书架
obj.listeners = obj.listeners || {}; //先找到楼层,没有楼层就创建楼层
obj.listeners[events] = obj.listeners[events] || []; //再找到书架,没有书架就创建书架
obj.listeners[events].push(fn); //把fn这本书放到书架上
/* 通过以上的方式,将obj,events和fn建立了关系*/
if(obj.addEventListener){
obj.addEventListener(events, fn, false);
} else {
obj.attachEvent('on' + events, fn);
}
}
function fireEvent(obj, events){ //主动触发自定义事件
for(var i=0; i<obj.listeners[events].length; i++){
obj.listeners[events][i]();
}
}
</script>
</head>
<body>
<div id="div1">div</div>
<span id="span1">span</span>
</body>
配置、方法、自定义事件分离
——正规组件的写法——基于拖拽的组件进行的修改
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义事件的拖拽组件</title>
<style>
#div1 {width: 100px; height: 100px; background: red; position: absolute;}
#div2 {width: 100px; height: 100px; background: yellow; position: absolute; left: 100px;}
#div3 {width: 100px; height: 100px; background: blue; position: absolute; left: 200px;}
#div4 {width: 100px; height: 100px; background: green; position: absolute; left: 300px;}
</style>
<script src="jquery-1.11.1.js"></script>
<script>
window.onload = function(){
var d1 = new Drag();
d1.init('div1');
var d2 = new Drag();
d2.init('div2');
bindEvent(d2, 'toDown', function(){
document.title = 'hello';
});
bindEvent(d2, 'toDown', function(){ //新来的一个同事扩展d2的toDown事件的时候,更容易
document.body.style.background = 'black';
})
var d3 = new Drag();
d3.init('div3');
bindEvent(d3, 'toDown', function(){
document.title = '妙味';
})
bindEvent(d3, 'toUp', function(){
document.title = '课堂';
})
var d4 = new Drag();
d4.init('div4');
bindEvent(d4, 'toUp', function(){
document.title = 'byebye';
})
}
function Drag(){
this.obj = null;
this.disX = 0;
this.disY = 0;
this.settings = {
}
}
Drag.prototype.init = function(id, opt){
var This = this;
this.obj = document.getElementById(id);
extend(this.settings, opt);
this.obj.onmousedown = function(ev){
var ev = ev || window.event;
This.fnDown(ev);
fireEvent(This, 'toDown');
document.onmousemove = function(ev){
var ev = ev || window.event;
This.fnMove(ev);
}
document.onmouseup = function(){
This.fnUp();
fireEvent(This, 'toUp');
}
return false;
}
}
Drag.prototype.fnDown = function(ev){
this.disX = ev.clientX - this.obj.offsetLeft;
this.disY = ev.clientY - this.obj.offsetTop;
}
Drag.prototype.fnMove = function(ev){
this.obj.style.left = ev.clientX - this.disX + 'px';
this.obj.style.top = ev.clientY - this.disY + 'px';
}
Drag.prototype.fnUp = function(){
document.onmousemove = null;
document.onmouseup = null;
}
function extend(obj1, obj2){
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
function bindEvent(obj, events, fn){
obj.listeners = obj.listeners || {};
obj.listeners[events] = obj.listeners[events] || [];
obj.listeners[events].push(fn);
if(obj.nodeType){ //如果传进来的是DOM元素的话,走着下面;如果传进来的不是DOM,是对象的话,就不走下面,只走上面了
if(obj.addEventListener){
obj.addEventListener(events, fn, false);
} else {
obj.attachEvent('on' + events, fn);
}
}
}
function fireEvent(obj, events){
if(obj.listeners && obj.listeners[events]){
for(var i=0; i<obj.listeners[events].length; i++){
obj.listeners[events][i]();
}
}
}
</script>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
<div id="div4"></div>
</body>
</html>
用JQ实现选项卡的组件开发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>基于JQ的选项卡组件开发</title>
<style>
html, body {margin: 0; padding: 0;}
#div1 div, #div2 div, #div3 div, #div4 div {width: 200px; height: 200px; border: 1px solid #000; display: none;}
#div1 .active, #div2 .active, #div3 .active, #div4 .active {background: red;}
</style>
<script src="jquery-1.11.1.js"></script>
<script>
/*
title: 基于JQ的选项卡组件
Options:
- event
- delay
Methods:
- nowSel() - 设置开始的tab数
- getContent() - 获取当前内容
Events:
- beforeClick 点击前触发
- afterClick 点击后触发
*/
// JQ中的主动触发:trigger()
$(function(){
var t1 = new Tab();
t1.init('div1', {});
var t2 = new Tab();
t2.init('div2', {
event: 'mouseover'
});
var t3 = new Tab();
t3.init('div3', {
event: 'mouseover',
delay: 200
});
var t4 = new Tab();
t4.init('div4', {});
t4.nowSel(2);
$('#input1').click(function(){
alert(t4.getContent());
})
$(t4).on('beforeClick', function(){
alert(t4.getContent());
})
$(t4).on('afterClick', function(){
alert(t4.getContent());
})
})
function Tab(){
this.oParent = null;
this.aInput = null;
this.aDiv = null;
this.iNow = 0;
this.settings = { //默认参数
event: 'click',
delay: 0
}
}
Tab.prototype.init = function(oParent, opt){
$.extend(this.settings, opt);
this.oParent = $('#' + oParent);
this.aInput = this.oParent.find('input');
this.aDiv = this.oParent.find('div');
this.change();
}
Tab.prototype.change = function(){
var This = this;
var timer = null;
this.aInput.on(this.settings.event, function(){
if(This.settings.event == 'mouseover' && This.settings.delay){
var _this = this;
timer = setTimeout(function(){
show(_this);
}, This.settings.delay)
} else {
show(this);
}
}).mouseout(function(){
clearTimeout(timer);
});
function show(obj){
$(This).trigger('beforeClick');
This.aInput.attr('class', '');
This.aDiv.css('display', 'none');
$(obj).attr('class', 'active');
This.aDiv.eq($(obj).index()).css('display', 'block');
This.iNow = $(obj).index();
$(This).trigger('afterClick');
}
Tab.prototype.nowSel = function(index){
this.aInput.attr('class', '');
this.aDiv.css('display', 'none');
this.aInput.eq(index).attr('class', 'active');
this.aDiv.eq(index).css('display', 'block');
this.iNow = index;
}
Tab.prototype.getContent = function(){
return this.aDiv.eq(this.iNow).html();
}
}
</script>
</head>
<body>
<div id="div1">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">111111</div>
<div>222222</div>
<div>333333</div>
</div>
<div id="div2">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">111111</div>
<div>222222</div>
<div>333333</div>
</div>
<div id="div3">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">111111</div>
<div>222222</div>
<div>333333</div>
</div>
<div id="div4">
<input type="button" value="1" class="active">
<input type="button" value="2">
<input type="button" value="3">
<div style="display: block">111111</div>
<div>222222</div>
<div>333333</div>
</div>
<input type="button" value="点击" id="input1">
</body>
</html>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。