javascript面向对象和设计模式
2013-08-22 17:37 臭小子1983 阅读(878) 评论(0) 编辑 收藏 举报一、注释写法
1 /* 2 * @author: 作者 3 * @action: 组件的作用 4 * @date: 创建日期 5 * 6 * Options : events delay // 属性 7 * Methods : currentShow getContent // 方法 8 * Events : beforeClick afterClick // 自定义事件或系统事件 9 */
一、面向对象
一、面向对象的一些重要的方面
1、对象引用 2、作用域 3、闭包 4、上下文
引用:引用就是指向对象实际位置的指针,多个变量可以引用一个对象
1 <script> 2 var obj = new Object(); // 创建一个新类 3 var objRef = obj; // 将引用地址赋给这个变量 4 obj.one = true; // 创建类的属性 5 alert(obj.one === objRef.one); // 返回true 6 </script>
作用域:是JS中一个难处理的特性,在JS里作用域由函数所约束,而不由块约束(如for,if,while语句)
1 <script> 2 // 例1: 3 var foo = "test"; 4 if(true){ 5 var foo = "new test"; // 这里仍然是全局变量 6 } 7 alert(foo === "new test"); // 所以返回true 8 9 // 例2: 10 var foos = "test"; 11 function test(){ 12 var foos = "new test"; // 这里是局布变量 13 } 14 alert(foos === "new test"); // 所以为false 15 </script>
全局变量都是window的属性,定义的函数都是window的方法.
隐式全局变量:
1 <script> 2 var foos = "test"; 3 function test(){ 4 name = "new test"; // 在函数里没有用var定义的变量都为全局变量 5 } 6 alert(name === "new test"); // 返回true 7 </script>
闭包:内层函数可以引用存在于包绕它的函数变量
上下文:是通过变量this工作
1 <script> 2 var obj = { 3 yes: function(){ 4 this.val = true; 5 }, 6 no : function(){ 7 this.val = false; 8 } 9 } 10 // 如果直接调用obj.val 对象obj没有这个属性所以为null 11 // 将运行yes方法后this会指向obj上,这时可以调用obj.val 12 obj.yes(); 13 alert(obj.val); // 返回true 14 </script>
改变上下文
1 <script> 2 function changeColor(colors) { 3 console.log(this); 4 this.style.color = colors; 5 } 6 // 在window对象上调用这个函数,因为window没有style对象 7 // new changeColor("white"); 8 9 // 通过call来改变this指针的指向 10 var box = document.getElementById("box"); 11 changeColor.call(box, "red"); 12 </script>
二、面向对象的基础
1、创建对象和对象属性方法的两个例子
1 <script> 2 // 方法一 3 var obj = new Object(); 4 // 创建对象公用属性和方法 5 obj.name = "哈哈"; 6 obj.sayName = function(){ 7 alert(this.name); 8 } 9 obj.sayName(); 10 11 // 方法二 12 var obj = { 13 name : "哈哈", 14 sayName : function(){ 15 alert(this.name); 16 } 17 } 18 obj.sayName(); 19 </script>
2、公有属性和方法、私有属性和方法
1 <script> 2 // 公有属性、方法 3 function User(name, age){ 4 this.name = name; // 公有属性 5 this.age = age; 6 } 7 User.prototype.getName = function(){ // 公有方法,通过prototype对象 8 return this.name; 9 } 10 User.prototype.getAge = function(){ 11 return this.age; 12 } 13 var user = new User("哈哈", 30); 14 alert(user.getName() == "哈哈"); // 返回true; 15 16 // 私有属性、方法 17 function Class(student, teacher){ 18 this.student = student; 19 this.teacher = teacher; 20 var current = 0; // 私有属性 21 22 function getCurrent(){ 23 return current; 24 } 25 } 26 Class.prototype.getName = function(){ 27 // console.log(current); // 报错,current为私有属性的,返回undefined 28 return this.student; 29 } 30 var className = new Class("aaa", "bbb"); 31 className.getName(); 32 // className.getCurrent(); // 报错,私有方法不能被外界所访问 33 </script>
3、特权方法:是用来访问和改变私有变量的方法
1 <script> 2 function User(name, age){ 3 this.name = name; 4 this.age = age; 5 var year = 2013; 6 this.getYear = function(){ // 特权方法,不能放到prototype中,因为作用域会找不到year 7 return year; 8 } 9 } 10 var user = new User("哈哈", 30); 11 alert(user.getYear()); // 返回2013; 12 </script>
三、命名空间
1 <script> 2 // 创建一个全局的命名空间 3 var YAHOO = {}; 4 5 // 设置一个子的命名空间 6 YAHOO.util = {}; 7 8 YAHOO.util.Event = { 9 addEventListener:function(){ } 10 } 11 12 // 调用方法 13 YAHOO.util.event.addEventListener(); 14 </script>
三、!=、== vs !==、===
在javascript里,null、0、""、false和undefined全部彼此相等(==),因为他们计算的值都为false;
1 <script> 2 console.log(typeof null); // 返回Object 3 console.log(typeof undefined); // 返回undefined 4 5 // 常见问题 6 console.log(null == undefined); // false; 7 console.log("" == undefined); // false; 8 console.log(0 == undefined); // true; 9 console.log(String == undefined); // false; 10 console.log(String == null); // false; 11 console.log(String === Object); // false; 12 </script>
一、富有表现力的javascript
一、在编程时你可以写采用函数式编程风格,也可以写成复杂一点的面象对象编程风格
二、链式调用
如jQuery采用的就是链式调用,$("#name").find("<p>").addClass("active");
1 <div class="box" id="box">14077期任选9场中头奖3894注</div> 2 <input type="button" value="添加" id="add"> 3 <input type="button" value="删除" id="del"> 4 5 <script> 6 function getId(id){ 7 return document.getElementById(id); 8 } 9 10 var oAdd = getId("add"); 11 var oDel = getId("del"); 12 13 function Chain(oId){ 14 this.box = getId(oId); 15 } 16 17 Chain.prototype = { 18 19 // 添加class值 20 addClass: function(claName){ 21 console.log(this.box); 22 var sClass = this.box.getAttribute("class"); 23 this.box.className = sClass+ " " +claName; 24 return this; 25 }, 26 27 // 删除class值 28 removeClass: function(claName){ 29 var sClass = this.box.getAttribute("class"); 30 var delClass = sClass.replace(/"claName"/, ""); 31 this.box.className = delClass; 32 return this; 33 }, 34 35 // css读写样式 36 css: function(pro, val){ 37 var t = this; 38 if(arguments.length == 1){ 39 console.log("参1") 40 var cssValue = t.getStyle(this.box, pro); 41 return cssValue; 42 } 43 if(arguments.length == 2){ 44 console.log("参2"); 45 t.box.style[pro] = val + "px"; 46 } 47 return this; 48 }, 49 50 // 获取样式 51 getStyle: function(obj, attr){ 52 if(obj.currentStyle){ 53 return obj.currentStyle[attr]; 54 } 55 else{ 56 return getComputedStyle(obj, false)[attr] 57 } 58 } 59 60 61 } 62 63 window.$ = function(id){ 64 return new Chain(id); 65 } 66 67 oAdd.onclick = function(){ 68 var getWidth = $("box").addClass("active").css("width"); 69 console.log(getWidth); 70 } 71 72 oDel.onclick = function(){ 73 $("box").addClass("show").removeClass("active").css("width", "20"); 74 } 75 </script>
三、活动的方法
1 一、直接定义函数 2 function start(){ } 3 function end(){ } 4 5 二、创建一个类 6 function Amm(){ } 7 8 Amm.prototype.start = function(){ } 9 Amm.prototype.end = function(){ } 10 11 12 三、传统的程序员更喜欢把他们写到一起 13 function Amm(){ } 14 15 Amm.prototype = { 16 start: function(){ }, 17 end: function(){ } 18 } 19 20 四、链式调用 21 window.$ = function(id){ 22 return new _$(id); 23 } 24 function _$(id){ 25 this.elements = document.getElementById(id); 26 } 27 _$.prototype = { 28 constructor:_$, 29 hide:function(){ 30 console.log('hide'); 31 return this; 32 }, 33 show:function(){ 34 console.log('show'); 35 return this; 36 }, 37 getName:function(callback){ 38 if(callback){ 39 callback.call(this, this.name); 40 } 41 return this; 42 }, 43 setName:function(name){ 44 this.name = name; 45 return this; 46 } 47 } 48 49 $("box").setName("aa").getName(function(name){ 50 console.log(name); 51 }).hide().show();
四、弱类型语言
在js中变量不用声明其类型,不带表变量没有类型,而是变量类型取决于数据的类型,javascript的类型:整型、字符串、布尔,undefined、null
五、函数是一等对象
函数可以存储在变量中,也可以作为参数传给另一个函数,可以作为返回值从其它函数传出,还可以进行构造,
匿名函数,没有名字的函数,自动执行,也可以赋给一个变量
1 // 匿名函数 2 (function(){ 3 var foo = 20; 4 var bar = 10; 5 alert(foo * bar); 6 })(); 7 8 // 可加参数 9 (function(foo, bar){ 10 alert(foo * bar); 11 })(20, 10); 12 14 // 赋给一个变量 15 var sun = (function(foo, bar){ 16 return foo * bar 17 })(20, 10); 18 alert(sun);
匿名函数最有趣的用途是创建“闭包”,闭包是一个受保护变量的空间,如果一个函数调变量并不想让这个变量为全局,也不是局部变量,并且让变量一直保一直保存,不会随着函数的结束而自动消毁,这样就可以使用闭包。
六、继承
继承中不像其它面象对象语言中那么简单,js是使用基于对象的(原型式)继承
七、Javascript中使用设计模式的原因?
1、可维护性:降低模块之间的耦合度,使代码进行重构和换用不同的模块变得容易
2、沟通:
3、性能:某些模式是起优化作用的可以提高运行速度,减少传送到客户端的代码量,
八、检测对象的属性和方法
1、hasOwnProperty:判断是否是对象的属性
var obj = {}; obj.name = "haha"; obj.hasOwnProperty("haha"); // 返回true
2、constructor:查看对象是否是构造函数
1 var type = function(o){ 2 return (o === null) ? "null" : typeof o; 3 } 4 5 var arr = new Array(); 6 7 if(type.constructor == Function){ 8 console.log("function"); 9 } 10 11 if(arr.constructor == Array){ 12 console.log("array"); 13 }
3、instanceof:查看对象与构造函数之间的关系,如果对象继承过其他构造函数的方法,那就也返回真
var arr = []; alert(arr instanceof Array); alert(arr instanceof Object); // 都返回true
二、接口
一、什么是接口
接口提供一种说明一个对象中的哪些方法
接口的好处:能促进代码的重用,接口可以告诉程序员一个类实现了哪些方法
三、封装和信息隐藏
为对象创建私有成员是面向对象的特性之一,目前有几种方法来创建私有、公有、特权方法的对象
封装:通过将一个方法或者属性声明为私用的,可以让对象的实现细节对其他对象保密以降低对象之间的耦合程度,可以保持数据的完整性并对其修改方式加以约束,这样可以是代码更可靠,更易于调试。封装是面向对象的设计的基石。
尽管JavaScript是一门面向对象的语言,可它并不具备将成员声明为公用或私用的任何内部机制,所以我们只能自己想办法实现这种特性。下面还是通过一套完整的代码去分析,介绍什么是私有属性和方法,什么是特权属性和方法,什么是公有属性和方法,什么是公有静态属性和方法。
私有属性和方法:函数有作用域,在函数内用var 关键字声明的变量在外部无法访问,私有属性和方法本质就是你希望在对象外部无法访问的变量。
特权属性和方法:创建属性和方法时使用的this关键字,因为这些方法定义在构造器的作用域中,所以它们可以访问到私有属性和方法;只有那些需要直接访问私有成员的方法才应该被设计为特权方法。
1 function SetObject(name, age, sex){ 2 var _name = name; 3 var _age = age; 4 var _sex = sex; 5 6 this.writeInfo = function(){ 7 console.log("name:" + _.name + " age:" + _.age + " sex:" + _.sex); 8 } 9 } 10 11 SetObject.prototype = { 12 setInfo: function(){ 13 this.writeInfo(); 14 } 15 } 16 17 var createObj = new SetObject("jack", 20, "男"); 18 createObj.setInfo();
共有属性和方法:直接链在prototype上的属性和方法,不可以访问构造器内的私有成员,可以访问特权成员,子类会继承所有的共有方法。
共有静态属性和方法:最好的理解方式就是把它想象成一个命名空间,实际上相当于把构造器作为命名空间来使用。
1 /* -- 封装 -- */ 2 var _packaging =function(){ 3 //私有属性和方法 4 var name ='Darren'; 5 var method1 =function(){ 6 //... 7 } 8 9 //特权属性和方法 10 this.title ='JavaScript Design Patterns' ; 11 this.getName =function(){ 12 return name; 13 } 14 } 15 16 //共有静态属性和方法 17 _packaging._name ='Darren code'; 18 _packaging.alertName =function(){ 19 alert(_packaging._name); 20 } 21 22 //共有属性和方法 23 _packaging.prototype = { 24 init:function(){ 25 //... 26 } 27 }
四、继承
继承一般是在一个基础类上又增加了新的功能,但不想破坏这个基础类中的方法和属性,这样可以创建一个新的超类将基础类继承过来
继承分为两种继承:1、继承属性 使用call来继承父类的属性 2、继承方法,使用for...in的方式将父类的方法继承过来
1、只继承属性:
1 // 构造模式 2 function TabB(name, age){ 3 this.name = name; 4 this.age = age; 5 } 6 TabB.prototype = { 7 sayName : function(){ 8 alert(this.name); 9 }, 10 showAge : function(){ 11 alert(this.age) 12 } 13 } 14 15 // 创建继承类 16 function CopyTabB(name, age, show){ 17 TabB.call(this, name, age); 18 this.show = show; 19 } 20 21 CopyTabB.prototype = { 22 childerName : function(){ 23 console.log(this.name); 24 } 25 } 26 27 var newTagB = new CopyTabB("虎虎", 20, true); 28 newTagB.childerName(); 29 // newTagB.sayName(); // 因为没有继承父类的方法所以就会报错
2、只继承方法:有两种方式
第一种方式:CopyTabB.prototype = new TabB(); // 将子类的原型 = 创建的父类
1 function TabB(name, age){ // 创建父类 2 this.name = name; 3 this.age = age; 4 } 5 TabB.prototype = { 6 sayName : function(){ 7 alert(this.name); 8 } 9 } 10 11 // 创建继承的子类 12 function CopyTabB(name, age, show){ 13 TabB.call(this, name, age); 14 this.show = show; 15 } 16 CopyTabB.prototype = TabB.prototype; 17 // CopyTabB.prototype = new TabB(); 这两种方法都是继承方法不继承属性 18 19 var newTagB = new CopyTabB("虎虎", 20, true); 20 newTagB.sayName();
第二种方式:for...in将对象的方法循环出来
1 // 构造模式 2 function TabB(name, age){ 3 this.name = name; 4 this.age = age; 5 } 6 TabB.prototype = { 7 sayName : function(){ 8 alert(this.name); 9 }, 10 showAge : function(){ 11 alert(this.age) 12 } 13 } 14 15 // 创建继承类 16 function CopyTabB(name, age, show){ 17 TabB.call(this, name, age); 18 this.show = show; 19 } 20 21 extend(TabB.prototype, CopyTabB.prototype); // 将父亲类的所有方法赋给子类 22 23 // 将父类的方法赋给子类 24 function extend(oParent, oChilder){ 25 for(var attr in oParent){ 26 oChilder[attr] = oParent[attr]; 27 } 28 } 29 30 var newTagB = new CopyTabB("虎虎", 20, true); 31 newTagB.sayName();
-----------------------------------------------------------------------------------------------
继承是一个抽象的话题,继承有两种方式:1、类继承 2、原型继承
1、prototype继承
1 // 定义一个超类 2 function PeopleA(name){ 3 this.name = name; 4 } 5 6 // 给超类添加一个方法 7 PeopleA.prototype.getName = function(){ 8 return this.name; 9 } 10 11 12 // 在定义一个超类 13 function PeopleB(name, sex){ 14 PeopleA.call(this, name); 15 this.sex = sex; 16 } 17 18 // 类继承 19 PeopleB.prototype = new PeopleA(); // 将new一个对象赋给prototype 20 // PeopleB.prototype = PeopleA.prototype; // 也可以这样写 21 22 /* 23 * 注:一定要将B类的方法写在,类继承之后,因为方法中还有调A中的属性 24 * */ 25 // 给B超类添加一个方法 26 PeopleB.prototype.writeInfo = function(){ 27 return this.name + this.sex; 28 } 29 30 // 实例化话PeopleB类 31 var createPeopleB = new PeopleB("全家", 20); 32 // console.log(createPeopleB.getName()); // 如果不继承就不能使用PeopleA中的方法 33 console.log(createPeopleB.getName()); 34 console.log(createPeopleB.writeInfo());
2、原型继承
1 <script> 2 // 创建一个超类 3 function Person(name){ 4 this.name = name; 5 } 6 Person.prototype.getName = function(){ 7 return this.name; 8 } 9 10 // 创建一个新类 11 function User(name, passwrod){ 12 Person.call(this, name); 13 this.passwrod = passwrod; 14 }
15 User.prototype = new Person() // User对象继承Person的所有方法
// User.prototype = Person.prototype;
16 User.prototype.getPasswrod = function(){ 17 return this.password; 18 } 19 20 var person = new User("names", 1111); 21 alert(person.getName()); 22 </script>
属性和方法都可以继承
1 function Obj1(){ // 创建一个对象 2 this.name = "siguang"; 3 } 4 Obj1.prototype.sayName = function(){ 5 return this.name; 6 } 7 8 function Obj2(){ // 另一个对象 9 this.age = 20; 10 } 11 12 Obj2.prototype = new Obj1(); 13 14 var newObj = new Obj2(); 15 var sName = newObj.sayName(); 16 console.log(newObj.name); // siguang 17 console.log(sName); // siguang
五、设计模式的类型
一、单例模式
模块模式是为单例模式创建私有变量和特权方法,所谓的单例模式,指的就是只有一个实例的对象,javascript是以对象字面量来创建单例模式
单例模式的用途:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
工厂就是把成员对象的创建工作转交给一个外部对象,好处在于消除对象之间的耦合(何为耦合?就是相互影响)。通过使用工厂方法而不是new关键字及具体类,可以把所有实例化的代码都集中在一个位置,有助于创建模块化的代码,这才是工厂模式的目的和优势。
1、普通的对象字面量单例:都是公有的属性和方法
1 var obj = { 2 name : "My Object", 3 getNameFun : function(){ 4 alert(this.name); 5 } 6 } 7 obj.getNameFun()
2、单例模式,声明的私有变量和方法
1 var singleton = function(){ 2 // 私有变量和私有函数 3 var privateVar = 10; 4 5 // 私有函数 6 function privateFun(){ 7 return false; 8 } 9 10 // 返回对象,可以通过特权方法来访问私有属性和私有方法 11 return { 12 publicVar : 10, 13 publicMethod : function(){ 14 privateVar++; 15 } 16 } 17 } 18 19 singleton.publicMethod();
单体模式之利:把方法和属性组织在一个不会被多次实例化的单体中,描述性的命名空间可以增强代码的自我说明
单体模式之弊:单体模式提供单点访问,导致模块之间的藕和性
二、原型模式(构造函数)
通过每个函数,都有一个prototype的属性来创造其原型方法,所有创建的对象只有一套prototype原型的方法或属性
1 function GetObj(name, old){ 2 this.name = name; 3 this.old = old 4 } 5 6 GetObj.prototype = { 7 init : function(){ 8 alert(this.name + "," + this.old); 9 } 10 } 11 12 var obj = new GetObj("aa", "bb"); 13 obj.init();
普通函数与构造函数的区别
function show(){ // var this = new object(); // 如果是new出来的会自动创建一个 alert(this); } show(); // 这个时候this指向window new show(); // 时候this指向object,如果用new出来的函数系统会自动在内部创建一个var this = new object
三、工厂模式
创建一个函数,在函数内在创建一个对象并将对象返回,而不需要象构造的数一样需要new出来一个对象,因为函数返回的是一个对象所以赋给一个变量就可以直接使用
1 function createPerson(name, age, job){ 2 var o = new Object(); 3 o.name = name; 4 o.age = age; 5 o.job = job; 6 o.sayName = function(){ 7 alert(o.name); 8 } 9 10 return o; 11 } 12 13 var por = createPerson("haha", 20, "人人"); 14 var join = createPerson("heihei", 30, "360"); 15 console.log(por.sayName()); 16 console.log(por.sayName());
1、什么时候使用工厂模式:
1、对象的构建十分复杂 2、需要依赖具体的环境,创建不同的实例 3、处理大量具有相同的属性的小对象
如果功能小就不适合用工厂模式,使得程序复杂
2、工厂模式存在的问题:
对象的方法都是单独存放,构造函数的原型prototype可以解决这种情况.
function createPerson(name, age){ var obj = new Object(); obj.name = name; obj.age = age; obj.showName = function(){ alert(this.name); } obj.showAge = function(){ alert(this.age); } return obj; } var c1 = createPerson("siguang", 30); var c2 = createPerson("lulu", 27); c1.showName(); c2.showName(); alert(c1.showName == c2.showName); // 返回false,说明每次调用都会单独存放这样会占用资源,如果使用构造函数就不会存这种情况
四、桥接模式
在实现API的时候桥接模式非常有用,
桥接模式的作用在于,将抽象与实现分离,以后便二者独立变化
1 addEevent(element, "click", getBeerById(e)) 2 3 function getBeerById(id, callback){ 4 var id = this.id; 5 asyncRequest('GET', 'beer.uri?id=' + id, function(resp){ 6 // Callback response. 7 console.log('Requested Beer: ' + resp.responseText); 8 }) 9 } 10 11 function asyncRequest(type, data, callback){}
// 方法的链式调用
把方法串链起来调用,象Jquery一样,$("#name").find("span").addClass("active");
这种技术分为两部分:1、创建代表HTML元素的工厂(工厂模式) 2、
// 使用单例模式和构造函数的区别
1 <script> 2 // 使用对象直接量(单体模式)和构造函数的区别 3 // 对象直接量 4 var Obj = { 5 getName: "10", 6 7 selectName: function(){ 8 alert(this.getName); 9 this.getName+=10; 10 } 11 } 12 13 $("#btn1").click(function(){ 14 Obj.selectName(); // 每点击一次getName都累加10 15 }) 16 17 $("#btn2").click(function(){ 18 Obj.selectName(); // 每点击一次getName都累加10 19 }) 20 21 22 // 构造函数 23 function Obj(){ 24 this.getName = 10; 25 } 26 27 Obj.prototype.selectName = function(){ 28 alert(this.getName); 29 this.getName+=10; 30 } 31 32 var n1 = new Obj(); 33 var n2 = new Obj(); 34 $("#btn1").click(function(){ 35 n1.selectName(); // 每点击一次n1下的getName都会加10 36 }) 37 38 $("#btn2").click(function(){ 39 n2.selectName(); // 每点击一次n2下的getName都会加10 40 }) 41 </script>
六、模块化
模块方式一
1 var singleton = function(){ 2 // 私有属性 3 var parive = 0; 4 5 // 私有方法 6 function getParive(){ 7 console.log(parive); 8 } 9 10 function getName(){ 11 console.log("siguang"); 12 } 13 14 return { 15 getParive: getParive(), 16 getName : getName() 17 } 18 }() 19 20 singleton.getParive; // 调用
模块方法二:
1 (function(){ 2 var name = "siguang"; 3 4 function Person(val){ 5 this.name = val; 6 } 7 8 Person.prototype.getName = function(){ 9 console.log(this.name); 10 } 11 12 var newObj = new Person(name); 13 newObj.getName(); 14 })()
JS的压缩器
分发js库的一个不可缺少的方面是压缩来节省带宽,有三种类型的压缩器:
1、简单的删除空格和换行和注释,公保留主要的代码
2、删除空白注释,也同将所有的变量名变得改短的压缩
3、删除空白和注释,同时还最小化代码中的所有单词(不仅是变量名)的压缩器
两种不同的库:JSMin和Paker
JSMin:删除无关的非代码 Paker:属于第三类彻底压缩所有单词