JavaScript-OOP
JavaScript-OOP
咳咳!今天来谈谈一位逆袭的屌丝-----javascript,如果你还不知道它的逆袭,那你就哪凉快哪埋了去!这是一门让初学者生不如死的语言!为什么呢?javascript 本质是一种函数式编程语言, 砖家说它是披着C语言皮的Lisp,是的,它的语法借鉴Java,函数借鉴Scheme(一种Lisp),原型继承借鉴Self,好的,打住!估计你就听说过java,不过没有关系,因为它和java没有半毛钱关系!

学什么都离不开历史
好的,为什么说它逆袭呢!因为他原来臭名远扬,一个是弹窗广告(就是有时候弹出色色的窗口),而后是卡我网速(额外加载这些js文件),很多开发者的Ctrl+C 和 Ctrl+V 导致代码乱的一笔,微软和网警的浏览器大战导致其移植性极差,等等这些历史因素,使得做web的人觉得能不用js就不用js,而后,随着历史的发展,随着Web2.0的热潮(人们越来越注重交互),Ajax出现,使得页面可以像窗口程序一样,无需刷新的体验,使得大家纷纷开始学习Ajax,但是特么的Ajax用JavaScript编写啊!大家硬着头皮学,接着有个天才写了个JQuery库,使得js可以夸平台了,而且写个应用变得超简单,因为其思想是:写的越少,做的越多。
好吧,虽然这个思想现在受到大神的抨击,但是不得不说,我们的前端之路就是由JQuery开始的,接着一个JSON数据格式取代XML,JSON数据格式就是js对象的写法啊!而后的HTML5受到乔布斯大力支持,迅速在移动开发端占领一席之地,PhoneGap的出现更是弥补了JS缺乏原生特性,也就是说可以用JS写出安卓原生的效果,HTML5的API可都是js啊!Google Map不再向flash开放API,亏我当时还想用FLEX做地图应用啊!各种JQuery插件横行,前端开发越来越受到重视,现在有了专门的前端工程师,所以JS当然也变成了前端开发的第一语言,所以它不再是给表单添加添加验证,弹出个框来的小玩意了,我在前几天看了一下Node.js真是酷爆了,服务器端的JS,虽然还没得到广大的应用,但是真的很酷!
既然他已是可以写出一个单独的application了,必然开始要有种编程范式,什么意思?就是面向对象,面向过程,函数式编程,好了,我想你懂的,javascript和java没有半毛钱关系,那为何叫javascript呢?因为推出他的时候java正是火热,所以就傍上大款了,同理,现在要有个面向啥编程,那肯定找面向对象啊!因为现在火啊!PHP不也面向对象了吗?好吧,开个玩笑,因为一个application必然要讲究各种性,性!别想歪了,是软件质量属性,比如:扩展性,高效性,可维护性ba la ba la....在现在这个面向对象被神化了的编程世界,当然要面向对象搞起啦!
面向对象VS函数式编程
可惜啊!在面向对象的世界中,一切皆object,产生对象的模板叫class,也就是注重类型,所以动态类型语言,在这条路上走的很艰辛啊,比如PHP,其多态,接口等特性都是打折的,当然它还在发展中嘛!但是我喜欢PHP,它有独特的设计哲学:‘我就是不管它从哪里来(类型),我只管它可以干什么(方法)’。
其实这个是一种函数式编程的思想,真的,函数式编程的世界就是:一切皆函数。动态类型语言的出现是为了提高灵活性,但是动态类型语言里面的对象(也就是加了面向对象后的动态类型语言),嘿嘿,说白了就是函数式编程,因为不在乎类型啊,那类型有什么用?类有什么用?
你以为你懂类和对象?
我们来看优雅的面向对象语言:java,其class包含一组所有对象共享的方法,但是成员变量是每个实例所特有的,所以那组方法里会操作成员变量会需要用this引用,你没写是因为你懒,编译器帮你加上去了。也就是所有实例对象公用类的一组方法,但是每个实例的内容(成员变量)是不一样的。所以:类其实就是一组方法的命名空间。
而成员对象就是元数据,其实就是西语言的结构体罢了,破开PHP变量结构,发现其数组和对象非常像,唯一不同的是:对象(一个结构体)含有一个指向类(Class)的指针,通过这个指针调用类的方法。所以,我们现在把类和对象完全独立了出来:对象是对象,是一组数据的集合(结构体),类是类,是一组方法的集合,对象可以在内部通过类名来调用类内的方法(外部看来是通过对象调用的),每个对象只有唯一的指针指向其类,这个指针不可变。你滴,明白?
动态类型语言天生适合函数式编程
好,现在回来,其实我有点说不下去了,见谅,见谅!所以类决定了它能干什么,因为它是干什么的一个命名空间,纯面向对象的语言里,类不止决定了能干什么,还决定了其对象应该有哪些属性,php原本是面向过程的语言,后来差不多照抄了java的面向对象。但是接下它的发展来会怎样?不知道,如果是动态类型语言,那么似乎天生就不care类型,意思就是天生多态,注意:这里不在乎类型,是指的调用方不在乎类型,只关心能干嘛,不能干,就出错,而java里,类型错了就出错。那么似乎类这个命名空间的约束有点形同虚设,因为不管你这个实例对象来着那个类,只要你有这个方法,则都可以执行,例如在php中:
|
class Hello { public function say(){echo ‘hello’;}} class Hey { public function say(){echo ‘hey’;}} $a = new Hello(); $b = new Hey(); function C($c){$c->say();} |
js函数式编程版本:
|
function a(){echo ‘hello’;} function b(){echo ‘hey’;} function C(c){c();} |
试问有区别吗?试问前面的Hello类和Hey类,有何作用呢?这个命名约束根本没有得到检查,不要和我说instanceOf 操作符,这种马后炮没什么好放的,如果一开始就觉得类型重要,就会设计到语言的核心中,因为这决定着编程的范式,所以我说php的面向对象之路会很难走,最搞笑的是php5.3版本竟然出现了闭包(来自函数式编程),吓我一跳啊!
JS如何实现面向对象
要做出个面向对象,就得理解面向对象的本质,实话告诉你,JS不是面向对象的语言,真的,它不是,为什么说它是呢?因为面向对象其实是一种范式,范式就是公式啦,是编程的一种思想,既然是一种思想,那就按照这种思想来不就行了,是的,其实西语言也可以实现面向对象,但是不提供语言的原生支持,可能不是很好,比如java的interface,其实接口就是一个契约,一个规范,继承接口的类,必须实现它的所有方法,那么没有interface这个关键字又会如何呢?如果人人都按照这个规范来,把interface的方法写在文档中或者注释中的话,其实不要有interface这个关键字也没关系啊!对不对?只不过java对这个规范提供了语言上的支持,你不这么写就错。同样的JS也有这方面的支持,但是不是interface,而是面向对象,他的面向对象用的是原型链(prototype)和构造函数。
是的,似乎有了new这个关键字,就可以实例化对象了,再插一遍嘴,没有new关键字可不可以实例化对象?答案是可以的,因为我们说过实例对象其实就是一组元数据(结构体),只要你在内存中搞出这么一组数据来,它就是对象了,只是对其的访问,等等操作,你要人为的去规范它,如下,JS实例化一个对象:
|
function P(a){ this.a = a; } var p = new P(1);//这就实例化一个对象了 var p2 = {a:1};//这也是实例化一个对象 var p3 = []; p3[‘a’] = 1;//我说这是个对象,你敢说它是数组吗? |
是的,JS的面向对象就是靠,这么一说而来的,函数也是对象,其实这抓住了面向对象和函数式编程的一个共性,那就是一切都是对象和一切都是函数,偷换一下概念有啥关系呢?反正一切就是一切。这个很哲学吧!
前面谈到动态类型语言天生适合函数式编程,因为不强调类型的概念,也就是弱化了类这个命名空间的作用,那JS是如何处理这一点的呢?很简单,管那么多干嘛,你诺真要现在传入参数的类型,就用instanceOf去做检查啊!
确实,马克思说过:世界是辩证统一的,就像搞基一样(喂!是不是应该向伟人道歉啊!)好的,你说你的参数必须是某种类型的才可以传入,那么你java搞泛型干嘛?强制类型转换干嘛?(java表示中枪),是的,我们都觉得传入错误类型不可以,但是那是种先入为主的思想,因为java先这么规范了大家就觉得它是对的。
辩证统一这个接口确实不错,在解决了设计思想上的矛盾之后,该是实现方面了,我搞个构造函数基本上就可以实例化类了,是的,因为JS不要什么Class,为什么不要,不是说过了吗?因为有还不如没有,看前面PHP的例子便知,本身类就是作为个命名空间而且在动态类型语言里面被弱化了,那么要它干嘛?直接走本质,请看下面:
类实例化本质
什么是实例化?说白了就是构造函数的调用啊?怎么调用?用new关键字,OK刚刚的,那哪些对象该有的属性怎么办?没有类放哪去?当然放构造函数里啦!构造函数不是用来初始化对象的成员变量的吗?那么方法怎么办?问的好!在JS里面刚才说到,函数是对象,对象可以作为另一个对象的属性,那么函数当然可以作为另一个对象的属性了啊!好的,也可以放构造函数里:
所以有了这样的写法:
|
function Person(name){ this.name = name; this.sayName = function(){alert(this.name);}; } |
原型即是类
学过JS面向对象的人,都知道,将方法放在构造函数的Prototype对象中,就可以实现方法的共享了,这不就说明了prototype就是起到了class的作用吗?这是对的,抛弃class但是留下prototype,原型的意思就是一个共享对象,所有被实例化出来的对象都可以访问。实例化出来的对象(p)和构造函数(函数也是对象是Function的对象)Person是两个完全不同的东西,所以p是无法访问得到Person的属性的。Person也无法访问这个Prototype对象的属性,只能通过Person.prototype.属性名访问,因为Person共享的原型是Function.prototype,所以在Person这个函数被定义时,系统就给它加了个prototype,让其实例化的对象可以共享它,这个prototype默认为Object的对象,通过构造函数的prototype属性去指向它,而构造函数本身就是Function的实例,也就是function Person(name){...}和 var Person = new Function(...);是一样的,(其实就解析速度不一样而已)。
所以这个prototype就是承担命名空间的功能了,不是说命名空间弱化了吗?干嘛还要有类?是的,所以它是个匿名的命名空间,因为都是叫prototype啊,但是你非要去check类型的话,构造函数就是其类型。是构造函数把对象生出来的,而prototype只是提供方法的命名空间,而且是匿名的。同理,prototype也可以放属性,这个属性呢,因为是公共的,可以看成静态成员变量吗?不可以,因为所有方法都可以去访问和修改它,静态成员变量只有静态方法可以修改,所以JS呢,就将定义在构造函数上的属性和方法说为是静态变量和静态方法,Person.a = 1;Person.Add(){this.a++};因为对象p无法访问到a,也无法调用Add方法,只有这么调用才行:Person.Add(),更具类名来调用,因为类型由构造函数决定,那么类名自然为构造函数名了,那么在prototype添加属性为啥呢?这个能就涉及到原型链了。
原型链其实很简单,就是在获取对象的属性时,p.name;会先去对象内部查找name,有就返回,没有就去prototype对象中查找,有就返回没有就去prototype的prototype中查找,直到一切的根源(Object)也是因为这个,才可以让我们的方法放在prototype中而被找到。一些高级技术:原型委托:就是通过改写Object.prototype的属性去让所有对象得到一些公共属性。原型是一种共享手段,原型链是一种查找策略。
你最怕的闭包
下一个问题,私有属性,私有变量怎么办?所谓私有属性,私有变量的本质就是:外界访问不到,只要访问不到的话,JS有闭包啊,绝对安全。
|
Person.prototype.getNumber = (function(){ var number= {Milo:1,Andy:2,Shirly:3}; return function(){ return number[this.name]; }; })(); |
闭包&执行上下文&作用域链
一分钟讲解闭包,闭包是什么?就是在一个函数中再次申明定义了一个函数,也就是函数的嵌套定义就是闭包啊!我们知道函数的调用其实就是函数栈的变化,嵌套调用会把下一个函数入栈,调用完事时,会出栈,但是闭包实现了,栈的永久保留,这里不得不说执行上下文和作用域链了,每个函数都有其作用域链,执行上下文,就是函数的运行的环境,比如在执行如上例子的第一个匿名函数时(注意后面有个()表示立即执行),会切换到它的执行上下文(创立一个上下文对象),(你不要管这个上下文对象是啥),然后给这个上下文对象创立作用域链,而函数访问变量都是通过作用域链一级一级的去访问的,好,回到代码,这里有了上下文对象(与第一个匿名函数关联),然后为这个上下文对象建立作用域链(就是个链表)为作用域链建立活动对象(又是个对象),这个对象放入函数的arguments成员(这个认识吧)放入函数的局部变量,这里第一行number就被放进去了,然后将这个活动对象作为作用域的头,最上面,这样函数在访问number的时候就直接到活动对象里去取,假设函数外面也定义了number对象,也不会访问错,因为活动对象在作用域头,而在函数外定义的number在作用域尾(全局作用域),等到return后,函数的执行上下文对象会被销毁,同样作用域链也没了,但是这里,因为里面还嵌套定义了一个匿名函数,所以这形成了闭包,所以外面的这个匿名函数的上下文对象没被销毁,其作用域也没有,因为闭包它被保留了下来,因为它作为了里面那个匿名函数的作用域链的第二个节点,第一个节点为里面那个匿名函数的活动对象(存放里面那个匿名函数的arguments),所以闭包的作用就是保留本应该被销毁的作用域链对象
继承的本质
下面还剩继承没说了吧!继承的本质是什么?你肯定不知道,其实继承就是构造函数的嵌套调用,JS里面有两种继承方式原型继承和类继承,推荐用类继承,而我觉得:不要有继承,继承虽然不是会破坏封装性的行为,但是真的继承不是很好,多用组合少用继承,宁可一个类里面包含另一个类的引用,不是继承不好,而是你用不好,敬请关注关于JS的继承!
本文来源 我爱IT技术网 http://www.52ij.com/jishu/1156.html 转载请保留链接。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
