前言
本少观过诸多视频和文章,奈何使用频率少之又少,每次一到用处,发现忘的一干二净.然后又重头温习,记得笔记也丢三落四,找不到;所以存到网上,方便以后查询.这里吐槽一句,很多文章以偏概全,不得要领,实在让我头皮发麻.还是自己写一篇全文记载,美滋滋.
场景模拟
俗话说,好的解决方法,都是由于碰到了问题.没有问题的理论讲解,都是赤裸裸的耍流氓.讲解问题没有场景模拟,都是凑不要脸.那么看下,是哪些问题让我痛定思痛,写这篇文章:
场景1:我与我漂亮的女同事,一起写一个项目,等她上传完svn后,我更新到我自己的电脑
上跑,因为她加了一些js和java类,导致我项目跑不起来,然后我干脆一不做二不休,把她加
的都删除掉,结果删啊删,删啊删,我发现项目快被我删完了.这就是典型的面向过程写程序
一环套一环.从那时起,我就清楚的认识到了面向对象编程的好处.
场景2:我的项目大概有几十万行代码.项目上线后,中途boss让我加一个模块;我想么,加个
模块很easy的啦.然后我就写了个js,往main.html里面一丢.就报各种错误.项目大了,很不
好查的.然后在我排查了一天的情况下,发现是变量污染了,也就是变量重复了.我的天啊,
一天就没了,从此我意识到了防止变量污染的重要性.
场景复现
我这里复现下变量污染的情况,这是我的目录,你可以假设js很多,有几十万个.
上面是我文件的代码.显然,我是希望输出5,3;也就是说,在test.html中,money是5,在js1.js中
money是3;但是你会发现,我输出的都是3.所以也就是说,money被污染了.
让我们来谈谈函数(万物皆对象)
函数:Funtion类型,其实函数也是对象,仔细想想这句话;
我们声明对象的方式:
方式一: var superman={};
方式二: var superman=new Superman();
我们声明函数的方式:
函数声明: function superman(x,y){};
函数表达式: var superman = function(x,y){};
Function构造函数: var superman = new Function("x","y","dosomething");//不推荐
我们可以看到,函数其实真的是一个对象…函数是对象,函数名是指针(哎,又引入指针了);我们还是不理解了.反正总的来说.我们知道了函数是对象(死记硬背吧);
咳咳咳,貌似没讲清楚,不过我们往下看:
我们稀里糊涂的可以认为:“我认识一个超人,他叫tom,今年25岁,喜欢把裤衩穿在外面”,那么
我们一般可以这么定义它:
var superman={
"name":"tom",
"age":25,
out_kucha:function(){console.log("out kucha")}
}
说明:out_kucha是方法,不能写成out_kucha=function(){}的形式,因为在对象里,存储的是键值对的形式.
我们的大脑会很自然的想到这种定义方式,那么问题又来了,超人那么厉害,我们自然希望能有更多的超人来保卫地球,我们能想到的除了让超人自然繁衍外,就只生下克隆了.说道克隆,我们肯定不希望让克隆医生一个一个的去克隆.因为那样既麻烦又费力.设想一下,克隆一个超人,要花费1年的时间,那么克隆100个,不就要花费100年的时间了吗?所以,我们希望能有一种即时复制的办法,就跟用光盘给电脑装系统一样.所以,我们希望有一个这样的办法,取个名字,输入个年龄,就能自动的给我们产生一个超人:
function createSuperman(name,age){
var o =new Object();
o.name = name;
o.age=age
o.out_kucha=function(){console.log("out kucha")}
return o;
}
那么我们只需要传入姓名和年龄,就能得到一个超人了.
var superman1=createSuperman("小王","22");
这样我们就得到了超人1,小王;我们可以通过superman1.name去查他的名字.
以前我们批量创建超人,是通过var super1={…},var super2={…},…;
现在我们批量创建超人,直接通过上面的方法就可以了,这样,对于创建的属性,我们就可以提出来,节省了不少代码.
诶,你想到了这些,那其他人肯定也能想到.所以就有个大牛,叫什么呢.我去查了下,没查到…他就牛逼了.他感觉啊.这样是不错.但是应该还可以改进.他就发明了构造函数.他认为.所有的函数,创建object对象,赋值,和返回都是共通的.那他就封装下(这个封装很麻烦);然后让操作这直接传参就可以了;
function CreateSuperman(name,age){
//var o =new Object();
//o.name = name;
//o.age=age
this.name=name;//this 指向调用者
this.age=age;
o.out_kucha=function(){console.log("out kucha")}
//return o;
}
使用方法:var super1=new CreateSuperman("小明","21");
new 字符帮我们做了我注释的三步.就是声明Object,赋值,和返回本体.
构造函数约定俗称的,首字母大写.人为规定的.
诶,我们发现,通过构造函数批量创建对象,是一件很愉快的事.然后呢,我就打开了我的vscode,试验了一下:
诶,我发现构造函数当普通函数使用的话,就是没有new,变量赋给了window.想想也是可以理解的.因为没有new,相当于没有绑定到对象;this向上级找,自然就是到window了.
然后我们通过new再来看看:
是不是很神奇,我记得以前看视频讲的时候,new其实是做了很多事情的,远比我们想象的多.
这一切看似很美好.可是随着人类文明的进步,很多人啊,年龄各不相同,但是呢,都做着同样的事.比如啊,我们程序员,年龄,性别,家庭都不同,但是呢,我们只需要做一件事,就是敲代码;那这样的话,我们通过构造函数来声明,就会发现,我们的dosth都是同样的事.那么在函数里,我们就会占用很多内存?(反正要占用很多东西,是内存还是栈堆忘记了,毕竟我不懂指针);哦,老天,反正你知道,这样很不好就对了,比如像下面这样:
emmmm,有些牛逼的人,就开始作妖了.我要是能把它搞出来就好了;于是…
有人就把方法提出来了;但是他发现,如果要做的事情很多呢?那么我就要在全局中定义很多个普通函数;没事,多就多,我可以手写一个月!!!我可以接受,我认为可以.但是,突然有一天,我发现我定义的函数,别人也可以引用.我靠,这我就不能忍了.我好容易new的苍老师,别人也能用,那是万万不行的.因为在全局定义函数,是个人都能引用啊.所以我要想个办法解决它.诶,这个时候又有牛逼的人站了出来,弄了原型.
原型
诶,我们来看看,我们刚new出来的小明
他有age,name,dosth,_proto_四个属性.前三个,我们很熟悉,毕竟我们自己创建的嘛.
emmmmm.我们来看下原型的定义:
创建的每个函数,都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的包含由特定类型的所有实例共享的属性和方法.(其实我压根没懂).虽然我没懂,但是我会敲代码啊:
这不敲不知道,一敲吓一跳.原来通过prototype写的内容,都跑到_proto_里面去了啊.那我们岂不是能把公共的属性方法通过这样去搞?答案是肯定的.
对不起,在这里我们不会记录什么Prototype指向谁,然后什么原型指向什么对象,对,没错,就是那个图!!因为我根本记不住.我只想拿起来就用.我不会去说什么instansof去检测啊,has…去检测啊,cconstructor指向谁啊.因为我根本记不住.好了.我现在知道了原型是有什么用处了.但是,很显然,原型致命的弱点,就是它的属性和方法都是共享的,那么我们要解决它.
确定风格
我们通过上述的分析.基本已经了解了构造函数和原型的作用.为了更好的编程,我们要有自己的风格,也就是规范.
构造函数和原型共用风格
这样写,我们能够确保共用的和独特的属性都满足.这种风格用的最多,最广泛,所以我也像这样用了.至于上面说的什么工厂模式啊,寄生模式啊,了解下就行了.
终极问题
既然我们确立了编程风格,但有一个问题一直困扰我很久,我经常看到jquery源代码里,各种立即执行函数封装,各种原型…终于;我明白了.立即执行可以让prototype变的私有啊.简单的说,如果我有一个main.html,one.js;我在HTML中引入one.js;我在one.js中如果只用构造函数和原型来声明的话,那么我的原型是会被污染的,而立即执行函数解决了这个问题;哈哈哈哈哈,这就是传说中的封装和模块开发啊.哎,又要去研究模块开发了.