用对象的思想去写代码,就是面向对象编程css
过程式写法html
面向对象写法node
咱们一直都在使用对象jquery
数组Array 时间Date面试
抽象:抓住核心问题编程
封装:只能经过对象来访问方法json
继承:从已有对象上继承出新的对象数组
多态:多对象的不一样形态app
方法(行为、操做) —— 对象下面的函数:过程、动态的ide
属性 —— 对象下面的变量:状态、静态的
//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();
改为与系统对象相似的写法
一、首字母大写
二、New关键字提取
三、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();
构造函数
用来建立对象的函数,叫作构造函数
存在的问题
一、对象的引用
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 */
概念
去改写对象下面公用的方法或者属性,让公用的方法或者属性在内存中仅存在一份(好处:提升性能)
学习原型
类比:原型就是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指向对象
一、先写出普通方法
<!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>
二、普通方法变型
<!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方法,那么后面那一句中的四、五、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>
<!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属性
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>
对象与构造函数在原型链上是否有关系
能够用来做类型判断
<!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>
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>
一、类式继承
利用构造函数(类)继承的方式
二、原型继承
借助原型来实现对象继承对象
类式继承
<!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的对象
一、对象的多种表现形式
提升对象的复用性
如何配置参数和默认参数
例子:拖拽
例子:弹窗
二、什么是组件?
对面向对象的深刻应用(UI组件、功能组件)
将配置参数、方法、事件、三者进行分离
三、建立自定义事件
有利于多人协做开发代码
如何去挂载自定义事件与事件函数
四、例子:基于JQ的选项卡的组件开发模式
trigger() extend()等方法的使用
五、本课练习
组件开发的练习
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>
<!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>
<!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>