1.须知:
1).本书三种模式:设计模式,编码模式,反模式;其中编码模式是本书主题
反模式:引发问题比解决问题更多的一种方法;
2).对象主要两种类型:原生类型(Native),主机类型(Host)
Native:内置的对象如数组,日期对象等
Host:自定义对象
3)原型(Prototypes),strict模式
Prototypes可以给对象添加成员;
strict模式在ECMAScript5(2009年)增加了strict模式
使用:在函数里直接加上"use strict"就可以了它会对之后代码起到约束的作用
4)JSLint,Console
JSLint:http://jslint.com:在这里可以检查js代码的规范性;也可以再eclipse中添加插件我加的是JSHint
Console:主要有两种方法:log()直接打印;dir()枚举传递过来的对象
Console.log("test",{},{1,2,3}) Console.dir({one:1,two:{three:3}});
//有时console.log()也可以省去
Window.name===window['name'];
Console.log(Window.name===window['name'])是一样的
2.代码优化基本技巧
1)编写可维护代码
1.阅读性好;2.具有一致性;3.预见性好;4.看起来像是一个人编写的5.有文档最好
2)全局变量问题
1.尽量不用全局变量
原因:可能会影响到1)第三方Javascript库;2)来自与广告合作伙伴的脚本
3)来自与第三方用户的跟踪与分析脚本代码4)各种小工具
2.变量释放时的副作用
var定义的全局变量不能删;不用var创建的可以删
原因:不用var创建全局变量本质上是全局对象的属性所有可以删除
Configurable,Enumerable,Writable,Value,Getter,SetterObject的属性中Configurable可用控制对象能不能删
不通过硬编码方式访问全局对象:
var global=({function(){return this}}());
例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//全局变量测试 function testQuanjubl(){ //"use strict";//使用stricts模式 var global_var=1; //这种隐式的全局变量本质上是对象属性可以删;但是var定义的全局变量不能删除 glo_novar=2;//反模式 (function(){ glo_fromfunc=3;//反模式 }()) //全局删除 //console.log(delete global_var); //console.log(delete glo_novar); //console.log(delete glo_fromfunc); //测试删除情况 console.log(typeof global_var); console.log(typeof glo_novar); console.log(typeof glo_fromfunc); //不通过硬编码方式获得windows对象 var glogal=$(function(){ return this; }) console.log(this.glo_novar); }
3).单一var模式
定义变量var模式好处:1)提供一个单一的地址以查找所需要的所有局部变量2)防止出现变量在定义前
3)帮助了牢记声明变量4)更少的代码
例子:
function func(){ var a = 1, b = 2, sum = a + b, myobject = {} }
4)零散变量提升
在函数中定义的变量有这个机制:提升:不管变量定义在什么地方,都会编译到函数头处
所以下面第一个显示undefined:未持有任何数据类型
//测试零散变量 function liansanTest(){ //用这个测试方法时请在开始运行时加一个全局变量myname="global" alert(myname);//"未定义" var myname="local"; alert(myname);//“局部变量” }
5)for循环
须知1:对html容器遍历时DOM操作比较耗时优化
解决办法:for循环时将对象长度缓存起来
for(var i = 0,max = myarray.length; i < max; i++){//对myarray[i]操作}
这样性能优化效果:在Safari 3 中速度提高2倍;在IE7中速度提供170倍
单变量模式:在循环中修改容器(例如新增一个DOM元素),需要修改容器的长度,此时我们可以将变量放到循环外面
function logger(){ var i=0; max, myarray={}; //... for(i=0,max=myarray.length;i<maxi++){ } }
须知3:尽量用i++类似的精简版运算符
须知4:使用最少的变量:逐步减至0循环自动结束:这样也能提高循环的效率
var i, array=[] for(i=myarray.length;i--){ }
6)for-in循环,hasOwnProperty的用法
是针对对象的循环;也可以用在数组上当时这会导致逻辑上的不准
须知1:Object对象添加一个成员后,我们遍历自定义对象如果不用hasOwnPropery函数就会访问到定义的那个成员这种情况不是我们希望的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//hasOwnpery的测试 function hasOwnProperyTest(){ //定义一个man对象 var man={ hands:2, legs:2, heads:1 }; //Object添加一个方法,不用hasOwnpery()就肯定能访问到这个方法 if(typeof Object.prototype.clone==="undefined"){ Object.prototype.clone=function(){}; } //for-in测试没有用hasOwnproperty for(var i in man){ console.log(i,":",man[i]); } //for-in测试用了hasOwnProperty for(var i in man){ if(man.hasOwnProperty(i)){//filter console.log(i," hasOwnproperty:",man[i]); } } //hasOwnProperty另一种用法 for(var i in man){ if(Object.prototype.hasOwnProperty.call(man,i)){ console.log(i," ObjecthasOwnproperty:",man[i]); } } //hasOwnProperty另一种用法 for(var i in man)if(Object.prototype.hasOwnProperty.call(man,i)){ console.log(i," ObjecthasOwnproperty:",man[i]); } }
注意:最好不要添加原型成员
7)switch
1.避免使用隐式转换符==(会自动转换)比较用===
2.避免使用eval():他是个魔鬼,作用式解析字符串当作JavaScript代码来执行;
例如:var property = "name";alert(eval("obj." + property));会式一个对象了
注意:setInterval(),setTimeout(),function(),等在传递参数的时候大多情况也会产生eval类似的隐患
对于eval的代替:JSON.parse(),JSON.org类库,JQuery.parse
eval()与new Function()的区别
功能一样都能解析字符串,但是eval()会影响作用链,在一定要用eval的情况下可以考虑用new Function()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//eavl谨用:解析,会消耗浏览器资源;可能会产生全局变量影响其他全局域 function testEvalAndFunction(){ //eavl()与new Function()区别作用一样但是eavl()会影响到作用域链 console.log(typeof un); console.log(typeof deux); console.log(typeof trois); var jsstring="var un=1;console.log(un);"; eval(jsstring); var jsstring="var deux=2;console.log(deux);"; new Function(jsstring)(); var jsstring="var trois=3;console.log(trois);"; (function(){ eval(jsstring); }()); //开始测试 console.log(un);//只有这个有值了,不安全 console.log(deux); console.log(trois); }
3.parseInt()数值约定
parseInt可以从一个字符串中获取数值,parseInt的第二个参数是进制参数,要尽量用
parseInt("09")因为没有指定第二个参数会返回8进制的数,09不合法;正确写法:parseInt("09",10)以十进制获得结果;
+"09";Number("08")这样会更快,因为parseInt是解析
4.编码规范:
1)编码约束;2)缩进;3)大括号;3)开放的大括号位置;4)空格5)命名的约束6)编写注释;7)编写API文档:可用工具8)正式发布时精简代码
编写好注释才能自动生成API详情请看:第二章36页
3.字面量和构造函数
1) 对象字面量:就是自定义对象
注意:尽量不要使用内置构造函数new Object()构造函数;自定义构造函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//自定义对象 function MyObjectTest(){ //1.对象字面量 //Object构造函数;Object()构造函数的值是动态的 var o=new Object(); console.log(o.constructor===Object); //自定义构造函数 console.log("--------------") var Person = function (name) { //使用对象字面量模式创建了一个新对象 //var this={}; //向this添加属性和方法 this.name=name; this.say=function(){ return "I am "+this.name; }; //return this; } var adam=new Person("Admin"); console.log(adam.say()); console.log("----------------") }
2)字面量数组
使用反模式创建数组:内置构造数组方式:new Array("aaa","bbb","ccc");
字面量方式:var a=["aaa","bbb","ccc"];
new Array()的陷阱:如果只传一个数字参数,指的就是数组的长度但却没有值
Array()有很多灵巧的方法:var white=new Array(256).jion(" ");这样就是重复256的空格了
判断一个对象是否为Array类型:通用判断是否为数组的方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//如果浏览器不支持Array.isArray if(typeof Array.isArray()=== "undefined"){ //没有就新建一个;arg是isArray传递过来的参数 Array.isArray() === function(arg){ return Object.prototype.toString().call(arg)==="[object Array]"; } } //判断对象是否为Array Array.isArray(需要测试的对象);
3)JOSN
JOSN.parse();JOSN.stringify:将对象数组解析;JQuery.parse()
JSON.stringify能将对象,数组解析为是JSON.parse的反方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//json测试解析 function JSONTest(){ var str='{"mystr":"my value"}'; console.log(str); console.log("-------json字符串解析--------"); var jsonStr=JSON.parse(str);//解析方式一 var jsonStr1=jQuery.parseJSON(str);//解析方式二 console.log(jsonStr.mystr); console.log(jsonStr1.mystr); console.log("-------对象,数组反解析为JOSN数组--------"); var dog={ name:"Fido", dob:new Date(), legs:[1,2,3] }; var jsonStr2=JSON.stringify(dog); console.log(jsonStr2); var jsonStr3=JSON.parse(jsonStr2); console.log(jsonStr3.name); }
4)正则表达式
g:全局匹配;m:多行;i:大小写敏感的匹配;
String.replace()能够用到正则
例子:var no_letters="abc123XYZ".replace(/[a-z]/gi,""); console.log(no_letters);
5)基本类型包装器
JavaScript基本类型:数字,字符串,布尔,null,undefined,包装类Number(),String(),Boolean()创建包装对象
包装类中提供了很多方法
6)错误对象
自定义异常用处:用处就是if(true){throw {name,message}}抛出一个异常对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
try{ //发生以此意外,抛出错误 throw{ name: "MyError",//自定义错误类型 message: "oops", extra: "This is rather embarrassing", remady:generricErrorHandler //指定应该处理该错误的函数 }; }catch(e){ //通知用户 alert(e.message); //优美的处理错误 e.remedy(); }
4.函数
1)三种创建函数的方式:
正常模式:function add(num1,num2){return num1+num2}
构造模式:new Function("num1","num2","return num1+num2");
匿名模式:var fun1=new function(num1,num2){num1,num2};
2)每个函数对象都有name属性
3)函数内声明的变量:提升,都会提到最前面
4)回调模式:将函数作为参数再对主函数内部运用函数参数
例子:对函数优化这里是伪代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//没有优化的例子 var findNodes=function(){ //找到dom中所有满足条件的nodes节点 return nodes; } var hide=function(nodes){ //对找到的nodes所有节点都设置一个样式 } hide(findNodes); //思考:上面这种方式对DOM总体查询了2次消耗很大, //我们要是能在一次搜索中完成每个节点样式的设置会更好 //优化后的例子:重构findNodes()接受一个回调函数 var findNodes=function(callback){ var i=10000, nodes=[], founds; //检查回调函数是否可用 if(typeof callback!=="function"){ callback=false; } //用这个回调函数对每个满足要求的节点设置样式 //dosomethings } var hide=function(node){ node.style.display = "none"; } findNodes(hide);
应用:异步事件监听器;超时setTimeout(函数名,500)
5)返回函数:函数也是对象,返回函数对象后,能够根据返回值继续运行
例子:自定义递增
var setup=function(){ var count=0; return function(){ return (count++); }; }; //用发 var next=setup(); next();//返回1; next();//放回2; next();//放回3; //内部原理:调用第一次next()后结果只想next地址的是是function函数了;都是指向同一个地址的,内部私有变量没有初始化就不会重置
6)惰性函数:
在函数内部从新指向函数指针:效果就是第一次调用是是一种结果第二次就不一样了
var scareMe = function(){ alert("Boo!"); scareMe=function(){ alert("Double Boo!"); }; }; //使用自定义函数 scareMe();
缺点:当重定义自身时已经添加到原始数据的任何属性都会丢失,此外如果该函数使用了不同的名字,比如分配给了不同变量,那么重定义部分永远不会发生
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//惰性函数模式深入研究 function dunxinHSMode(){ //先自定义一个惰性函数 var scareMe = function(){ alert("Boo!"); scareMe=function(){ alert("Double Boo!"); }; }; //1.添加一个新的属性 scareMe.property="property"; //2.赋值给另一个不同名称的变量 var prank=scareMe; //3.作为一个方法使用 var spooky={ boo:scareMe } //calling with a new name prank(); prank();//这里惰性就不起作用了两次都是同一个 console.log(prank.property); //作为一个方法来调用 console.log("-------------------"); spooky.boo(); spooky.boo(); console.log(spooky.boo.property); //这里再次使用自定义的函数 scareMe(); scareMe();//这里两次放回的都是惰性结果了因为前面都把第一次运行完了 }
7)即时函数模式(自调用,自执行)
(function(){alert(' ')}()):方式一
末尾的()导致该函数立即执行;外部用()将整个函数包在其中对变量有保护作用
(function(){alert( ' ')})();方式二//jslint推荐用第一种
即时函数参数:
(function(who,when){console.log("I"+who+when)}("xiaoming",new Date()))
8)即使对象模式,类似与即时函数模式,保护全局作用域不受污染对象.init()
init()方法负责初始化工作
注意:这种模式适用与一次性任务,init()结束后也没有该对象的访问,想要有个对象的引用,可用在init函数中添加一个return this
例子:即时函数模式;即时对象模式(用.init())都能立马执行的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//即时函数模式,即时对象模式 function jishiHSTest(){ //即时函数模式 (function(){console.log("nihao ")}()), (function(){console.log("nihao ")})(), console.log("-----------"); //即时对象模式 ({ //配置常量 max600, maxheight:400, //定义方法 getMax:function(){ return this.maxwidth+"x"+this.maxheight; }, //初始化 init: function(){ console.log(this.getMax()); //更多初始化任务s } }).init(); ({ //配置常量 max600, maxheight:400, //定义方法 getMax:function(){ return this.maxwidth+"x"+this.maxheight; }, //初始化 init: function(){ console.log(this.getMax()); //更多初始化任务s } }.init()); }
9)初始化分支:是一种优化模式
当莫个条件在整个生命周期中内不会发生改变时,仅对该条件测试一次很就可以了
10)备忘模式(挺难理解的)
原理:给函数添加一个属性对象(键值对对象),函数传入的参数需要作为键值对的建,每次执行函数时,都会将键值对对象存储值如果对应的建没有的存值的话
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//备忘模式,这个例子 //给函数添加一个属性对象(键值对对象)用来存储结果 //需要有参数才能唯一标识一个缓存 function beiwanMode(){ function myFunc(param){ var result; if(!this._cache){ this._cache = {} } if(this._cache[param]){ console.log("进备忘录"); return this._cache[param]; }else{ result = param * param; this._cache[param] = result; return result; } }; //缓存存储 var zz = myFunc("2"); console.log(zz); var xx = myFunc("2"); console.log(xx); var cc = myFunc("3"); console.log(cc); var ff = myFunc("2"); };
11)当传入参数数量很多,建议用对象封装所有参数
12)函数应用:apply(,);call()两个方法都很厉害
一些纯粹函数编程中语言中函数并不描述为被调用(call和invoked),而是应用(appled)
Function.prototype.apply():有两个参数:第一个参数:很重要,写那个对象函数就作用于哪个对象,第二个一个数组或者多个参数
例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//调用函数的另一种方法apple,call //厉害之处:它的第一个参数设置为什么,函数就作用与那个对象上 //这里例子将原本alien对象中name的的作用域改到了aaa中name了所以结果不同了 function appleTest(){ var alien = { name:'1111', sysHi : function(param){ return "Hello "+this.name + param; } } var aaa = {name:'222'} console.log(alien.sysHi("xiaoping")); console.log(alien.sysHi.apply(aaa,["xiaoping"])); console.log(alien.sysHi.call(aaa,"xiaoping")); }
13)部分应用Curry化:
作用:一个多参函数调用时通过curry化后可以只传部分参数
通用curry化函数
//通用的curry化函数 function schonfinkelize(fn){ var slice = Array.prototype.slice, stored_args = slice.call(arguments,1); return function(){ var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null,args); }; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//Curry测试部分函数应用 function curryTest(){ //通用的curry化函数 function schonfinkelize(fn){ var slice = Array.prototype.slice, stored_args = slice.call(arguments,1); return function(){ var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null,args); }; } //curry应用 function add(a,b,c,d,e){ return a + b + c + d + e; } //可用与任意数量的参数 console.log(schonfinkelize(add,1,2,3)(5,5)); //两步curry化 var addOne = schonfinkelize(add,1); console.log(addOne(10,10,10,10)); var addTwo = schonfinkelize(addOne,2,3); console.log(addTwo(3,4)); }
5.对象创建模式
1)命名空间模式、
好处:有助于减少程序中所需全局变量的数量,同时还有助于避免命名冲突或过长前缀
引入:定义一个全局对象MYAPP,首先要判断程序中是否有,有就不创建没有就再创建,但是当MYAPP.model还有判断是否存在,
所以需要一个函数能一次性处理类似与“MYAPP.model1.model2”创建每个对象子对象子对象
通用namespace处理函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function namespaceTest(){ var MYAPP = MYAPP || {}; MYAPP.namespace = function (ns_string) { var parts = ns_string.split('.'), parent = MYAPP, i; // strip redundant leading global if (parts[0] === "MYAPP") { parts = parts.slice(1); } for (i = 0; i < parts.length; i += 1) { // create a property if it doesn't exist if (typeof parent[parts[i]] === "undefined") { parent[parts[i]] = {}; } parent = parent[parts[i]]; } return parent; }; var module2 = MYAPP.namespace('MYAPP.modules.modules2'); console.log(module2); console.log(MYAPP); }
2)声明依赖关系
var event = YAHOO.util.Event,//这就是声明依赖了
3)私有属性和方法
想要指定私有成员:需要指定用到作用域,用函数创建对象有私有成员在(中定义的成员就是私有成员外面不能访问)
例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//私有成员测试 function siyouCYTest(){ //用函数定义一个对象,可以有私有成员 function Gadget(){ //私有成员 var name = 'ipod'; //公用方法 this.getName = function(){ return name; } } var toy = new Gadget(); console.log(toy.name);//name私有属性不能访问所以结果undefined console.log(toy.getName()); }
私有成员,在调用构造函数的时候每次都会自动创建,浪费了内存,利用对象的原型可以将成员放到原型中,不管新建多少次,这些对象都共享
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//私有成员测试 function siyouCYTest(){ //用函数定义一个对象,可以有私有成员 function Gadget(){ //私有成员 var name = 'ipod'; //公用方法 this.getName = function(){ return name; } } //私有成员放到原型中实现对象资源共享 Gadget.prototype = (function(){ var browser = "Moble webkit"; return { getBrowser:function(){ return browser; } } }()); var toy = new Gadget(); console.log(toy.name);//name私有属性不能访问所以结果undefined console.log(toy.getName()); var toy1=new Gadget(); console.log("----------------"); console.log(toy1.getBrowser()); }
4)私有性失效:有些浏览器有自己的方式还是能访问到私有成员
5)模块模式:提供了结构化的思想有助与帮助我们组织日益增长的代码
组合:命名空间;即时函数;私有特权成员;声明依赖
例子:创建构造函数模块;模块模式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//模块模式测试 function ModeTest(){ //定义namespace方法 var MYAPP = MYAPP || {}; MYAPP.namespace = function (ns_string) { var parts = ns_string.split('.'), parent = MYAPP, i; // strip redundant leading global if (parts[0] === "MYAPP") { parts = parts.slice(1); } for (i = 0; i < parts.length; i += 1) { // create a property if it doesn't exist if (typeof parent[parts[i]] === "undefined") { parent[parts[i]] = {}; } parent = parent[parts[i]]; } return parent; }; //用模块模式创建array工具 MYAPP.utilities.array = (function(){ //私有成员 //依赖 var obj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, //私有属性 array_string = "[object Array]", ops = Object.prototype.toString; //私有方法 //var 变量定义初始化 //公用的API return{ inArray:function(){}, isArray:function(){} } }()) //带构造函数的模块模式,返回的时对象不是函数了 MYAPP.utilities.array1 = (function(){ //私有成员 //依赖 var obj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, Constr; Constr = function(o){ this.element = this.toArray(o); } //公用的API--原型 Constr.prototype ={ constructor : MYAPP.utilities.Array, version : "2.0", toArray:function(obj){ for(var i = 0,a = [],len = obj.length;i < len;i++){ a[i] = obj[i]; } return a; } } //返回要分配给新命名空间的函数 return Constr; }()) }
6)沙盒模式:类似与requireJS
8)静态成员
1.共有静态成员
思路:添加静态成员,主要给对象.一个属性方法就能直接通过名字访问到这个属性方法这就是静态方法;
那它的普通方法,由实例化对象在构造方法内部写或者用原型添加一个方法(利用call调用静态方法变为自己普通方法)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//给对象加一个静态成员 function staticTest(){ //构造函数 var Gadget = function(price){ this.price = price; } //静态方法:类可以直接访问也可以实例化后访问 Gadget.isShiny = function(){ var msg = "you bet"; if(this instanceof Gadget){ msg = msg + ",it cost"+this.price+'|'; } return msg; } //添加普通方法 Gadget.prototype.isShiny = function(){ //用本地的对象调用静态方法 return Gadget.isShiny.call(this); } //测试 console.log(Gadget.isShiny());//添加静态方法 var aa = new Gadget("23"); console.log(aa.isShiny()); }
2.私有静态成员
这个只要将成员把对象用()包起来,内部定义的成员就是私有成员了,再通过对象内部返回的特殊方法对私有静态成员查询,
私有静态成员在相同的构造函数创造的对象中资源共享
例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//私有成员测试 function SiyouCYTest(){ var Gadget = (function(){ var counter =1;//私有成员 //新的构造函数 NewGadget = function(){ counter++; } //特权方法 NewGadget.prototype.getLastId = function(){ return counter; }; return NewGadget; }()) //测试 var ipone = new Gadget(); console.log(ipone.getLastId()); var ipone1 = new Gadget(); console.log(ipone1.getLastId()); }
9)对象常量:大写Math.IP
10)链模式:
11)method模式定义类的方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//用method来定义类的方法用到链 function methodTest(){ //method定义类的方法 var Person = function(name){ this.name = name; }.method('getName',function(){ return this.name; }).method('setName',function(name){ this.name = name; return this; }); //测试这个类 var person = new Person('xiaoping'); console.log(person); }
6.代码复用模式:类似与java中的继承实现代码复用
1)默认模式(设置原型)
原理:子类原型指向父类实例,这样子类就能通过原型找到父类中的成员;
缺点:这里在于同时继承了2个对象的属性:自身属性,原型中的属性;不支持将参数传递到子构造函数中,子构造函数的参数想要传递到父需要反复创建父构造函数
注意:将可复用的成员添加到原型中
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//类式继承模式1:默认模式 function defaultMode(){ //原型继承 var Parent = function(name){ this.name = name || 'Admin'; }; Parent.prototype.say = function(){ console.log(this.name); } var Child = function(){}; function inherit(C,P){ C.prototype = new P(); } //测试 inherit(Child,Parent); var parent = new Parent("xiaoming"); var child = new Child(); child.say(); }
2)借用构造函数模式
原理:子类创建通过调用父类的构造函数:function Child(a,b,c,d){Parent.apply(this,arguments);};实现了将父类复制到子类中从而获得子类成员;解决了默认模式参数传递的问题
缺点:无法从原型中继承任何东西,当父类有成员定义在父类原型中就不能继承此成员;优点:获得父类的真是副本,不存在最对象意外覆盖父对象的风险
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//类式继承模式1:借用构造函数 function jieyonggzhs(){ //父构造函数 function Article(){ this.tags = ['js','css']; } var article = new Article(); //1.bolg用原型方式继承父类 function BlogPost(){}; BlogPost.prototype = article; var blog = new BlogPost(); //2.page用借用构造函数 function StaticPage(){ Article.call(this);//Article构造函数调用用于this对象,相当于复制了父类对象 } var page = new StaticPage(); //测试 console.log(article.hasOwnProperty('tags'));//是自己成员 console.log(blog.hasOwnProperty('tags'));//这个成员不是自己的 console.log(page.hasOwnProperty('tags'));//是自己的成员 }
例子:通过借用构造函数实现多重继承
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//类式继承模式1:借用构造函数实现多重继承 function jieyonggzhsdcjc(){ function Cat(){ this.legs = 4; this.say = function(){ return "meaowww"; } } function Brid(){ this.wings = 2; this.fly = true; } function CatWings(){ Cat.apply(this); Brid.apply(this); } //测试 var jane = new CatWings(); console.dir(jane.legs); }
3)借用和设置原型:结合前面两种思想
原理:借用apple()父类构造函数获得父类成员副本,设置子类原型指向父类实例对象,原型指向父类获得父类原型中的成员
优缺点:获得了父类成员副本,获得父类原型,基本满足java中继承的思想了,修改自身属性不会修改到父类的属性了;缺点:父构造被调用两次、
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//类式继承模式3借用和设置原型 //缺点:父类构造函数构造了两次 //优点:子类删除属性,父类不会受到影响 function jieyonggzyx(){ //父构造 function Parent(name){ this.name = name || 'Admin'; } //父原型添加方法 Parent.prototype.say = function(){ return this.name; } //子构造 function Child(name){ Parent.apply(this,arguments); } Child.prototype = new Parent(); //测试 var kid = new Child("xiaoping"); console.log(kid.name); console.log(kid.say()); delete kid.name; console.log(kid.say()); }
4)共享原型:子类父类共享原型,这样可复用的成员应该转移到原型中,子类能访问父类也能访问
原理:子类父类原型指向同一原型原型共享:function inherit(c,p){c.prototype = p.prototype}
优缺点:如果共享的原型的子类改变了原型中类型,父类,祖类都会收到影响
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//类式继承模式4原型共享 //缺点:如果共享的原型的子类改变了原型中类型,父类,祖类都会收到影响 function yuanxingxTest(){ //共享原型方法 function inherit(C,P){ C.prototype = P.prototype; } //父类 function Parent(name){ this.name = name } Parent.prototype.say = function(){ return this.name; } //子类 function Child(name){this.name = name;} inherit(Child,Parent); var c = new Child("xiaoping"); console.log(c.say());
5)临时构造函数(升级后的代理构造模式是最佳方案)
同过断开原型与原型中间直接联系从而解决共享原型的缺点:子类改变原型父类受到影响
原理:子类原型,父类原型之间通过定义一个中间对象让为子父原型间接连接,这样子类改变原型父类原型不会受到影响
function inherit(C,P){var F = function(){}; F.prototype = P.prototype;C.prototype = new F()}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//类式继承模式5临时构造函数 function linshigzhs(){ //方法 function inherit(C,P){ var F = function(){}; P.prototype = F.prototype; C.prototype = new F(); } //父类 function Parent(name){ this.name = name } Parent.prototype.say = function(){ return this.name; } //子类 function Child(name){this.name = name;} inherit(Child,Parent); var c = new Child("xiaoping"); console.log(c.say()); }
存储超类:临时构造函数的改进版,添加了超类uber概念
function inherit(C,P){var F = function(){};F.prototype = P.prototype;C.prototype = new F();C.uber = P.prototype}
6)使用代理模式/代理构造模式
重置构造函数指针(称为代理构造模式或者使用代理模式):针对这个近乎完美的类式继承函数,还需要做的是重置构造函数指针,以免在将来莫格时间还需要改构造函数
如果不做这个:以后测试子类的constructor.name时这里时父类的构造函数,所以我们需要在继承函数中添加子类构造函数指针
function inherit(C,P){var F = function(){};F.prototype = P.prototype;C.prototype = new F();C.uber = P.prototype;C.prototype.constructor = C;}
最终的优化模式:避免每次继承时都创建临时构造函数,可用即使函数将此方法执行
var =inherit = (function(){var F = function(){}; return function(C,P){
F.prototype = P.prototype;C.prototype = new F();C.uper = P.prototype;C.prototype.constructor = C;
}}());
7)KIass
8)原型集成
定义变量的:var,const,let
var : 定义变量,可用不赋值,为undefined;const:定义变量,必须赋值否早会报错;let:局部变量,函数内部创建对变量的改变不会影响外面
7.23种设计模式运用在js中
1.备忘模式 原理:给函数对象添加一个cache属性来存储之前计算的结果 应用场景:函数计算量大,想要减少不必要的计算 2.部分应用 原理:函数不是被调用而是被应用,函数部分应用输入参数得到一个新函数再应用一个参数得到一个新函数指到所有参数输入完才能得到结果 应用场景:应用函数不能一次输入全部参数时 3.揭示模式(模块模式) 原理:定义一个封闭对象,只暴露想用的接口 应用场景:做控件库 4.命名空间模式: 原理:创建一个全局变量,在它上面添加所有可用变量模仿java语言中的命名空间 应用场景:控制项目的全局变量数量和之间关系,使用全局变量时建议在开始时赋值给局部变量 5.单例模式: 原理: 应用场景:每次创建对象都是一样的 6.工厂模式 原理:将每个环境创建对象方法都写出来封装到统一方法中去 应用场景:需要依据不同环境创造出不同对象 7.装饰者设计模式 原理:给被装饰者添加新功能 应用场景:要实现的功能需要通过很多的小功能组合起来得到,且实现的这类功能次数很多 8.外观模式: 原理:重复调用的方法封装到一个方法中一起调用 应用场景:执行很多重复代码 9.策略模式: 应用场景:验证表单,不论表单什么类型什么数据,都能自行找到合适策略验证