当前位置: 首页 > 科技观察

简单这个,麻烦这个

时间:2023-03-21 14:16:01 科技观察

周末的HelloWorldCafe总是熙熙攘攘。Java、Python、Ruby、JavaScript围坐在一起,喝着咖啡,一边享受着大海和天空。老C留在一旁,冷眼旁观。聊着聊着,话题莫名转移到了“这个”上。Java说:“哦!你不知道,对于一个Java初学者来说,这是很难理解的。”蟒蛇说:“这对你来说很简单了,难懂吗?”“我们都支持面向对象的编程。在我的例子中,可以在实例方法或构造函数中使用this来表示对当前对象实例的引用。”publicclassPoint{privatedoublex=0.0;privatedoublex=0.0;publicPoint(intx,inty){这个。x=x;this.y=y;}publicdoubledistanceTo(Pointthat){doubledx=this.x-that.x;doubledy=this.y-that.y;returnMath.sqrt(dx*dx+dy*dy);}}“这不是很容易理解吗?”露比问道。“对于第一次接触面向对象编程的人来说,他分不清这个当前对象是哪个对象。”Java说:“我得再写一段代码给他破解。”Pointp1=newPoint(1,1);Pointp2=newPoint(2,2);//this指向p1p1.distanceTo(p2);//this指向p2p2.distanceTo(p1);"是的,this必须有context可以准确理解。“Python说,”另外,你的this是隐式的,就像我的是显式的一样:“point.xdy=this.y-point.yreturnmath.sqrt(dx**2+dy**2)Java说:“你不是一直用self吗,怎么现在变成这个了?”Python笑道:“我不想与您的Java兄弟保持一致。不管怎样,它只是一个变量名。要用这个就用这个,要用那个就用那个,只是我们习惯用self。”Ruby说:“Python兄弟,你把这个作为参数放在方法里,真的很丑,一点美感都没有。”“那是我们的哲学,我们相信Explicit比implicit好。“但是在调用时,为什么不将一个对象传递给那个方法呢?”你自己去哪儿了?“p1=Point(1,1)p2=Point(2,2)p1.distanceTo(p2)p2.distanceTo(p1)”你为什么不写:distanceTo(p1,p2)?”“那不行,”Python说,“如果是这样的话,那我们就不是面向对象了,而是面向过程的。披着面向对象的外衣,内部实现还是面向过程的?!”“怎么说呢?”Java一直以正统的面向对象自居。不像Python和Ruby,Java要输出一个HelloWorld也要定义一个类。”说说你吧,Java小子,你的Java源文件编译后变成了.class文件,这个class文件加载到Java虚拟机的一个区域,这个区域叫什么名字?”C老头出奇的出手。“当然是MethodArea,方法区,我不知道?!”“对,为什么叫方法区?为什么不叫ClassArea,类区?”C老头真是机灵。“这……”爪哇神魂颠倒,他从来没有想过这个问题。"你的方法区是各个线程共享的,存放着虚拟机加载的类的信息,常量池,其中最重要的是类中定义的方法相关的代码。但是你创建的对象是放在在‘堆内’中,虚拟机在执行时,需要从方法区中寻找‘方法’,这些方法的字节码在运行过程中会操作位于堆内的对象。”"所以你看,你的数据和方法是分开的,一个地方是方法(所以叫方法区),另一个地方是数据,和我们用C写的程序是一样的,和他们都是面向过程的!”经过一系列的论证,C区的老大爷做出了最后的陈述。Python也沉默了,他知道自己在以类似的方式运行。过了一会儿,Java醒了过来:“不,老头,你在混淆概念,我们是站在程序员的角度讨论语言是不是面向对象的,你把我们拉到实现层面,这是不对的。”是的。”Python也附和:“对,我们是面向对象的语言,抽象层次比你们面向过程的语言高!”Linux,我用C写Git,你以为他没有做抽象?真是笑话!在我看来,抽象就是在变化中找到常量,与具体的编程语言关系不大。”C老头说一句至理名言。Java悄悄对Python说:“老夫主要搞操作系统的内核,操作系统中的虚拟内存、进程、线程、文件系统的概念都非常清晰和稳定,估计他还没有接触过异常的应用,合理的业务逻辑。”C老头说:“别以为你面向对象多厉害,我告诉你,用面向对象语言写面向过程程序的程序员大有人在!!Ruby说:“兄弟俩,算了,别跟老夫计较了,先看看我的这个,不对,是self,我这里必须要用self。”我的self和你们的不一样,意思是不同地方含义不同,比如:"classPoint#Self这里是Point类puts"Selfis:#{self}"#定义一个Class级别的(静态)方法,self还是Point类defself.nameputs"Selfinsideclassmethodis:#{self}"end#定义一个实例方法,这里的self是对象实例defnameputs"Selfinsideinstancemethodis:#{self}"endendJava说:"你搞这个太麻烦了,定义一个静态方法,用static不就完了吗?”许久没有说话的JavaScript忽然说道:“这也叫麻烦,看我怎么处理!"functionadd(y){returnthis.x+y}熟悉Java面向对象,Python看到这么奇葩的代码很惊讶,这是什么鬼?add函数中this指向谁?JavaScript说:"Don'真是大惊小怪!我的this与你的this和self不同。它是动态的,定义的时候无法确定指向谁。只有在调用函数时才能确定。this指向最终调用它的对象,例如:"functionadd(y){//此时this指向全局对象,浏览器运行时为windowreturnthis.x+y}x=10console.log(add(20))add函数在全局上下文中被调用。所以this指向全局对象,输出值为30。JavaScript说:“我也可以像this一样将对象传递给add函数。"functionadd(y){//此时this指向对象obj,this.x是20,不是10returnthis.x+y}x=10varobj={x:20};//传递一个对象给addfunctionadd.call(obj,20)//40大家更惊讶了,javascript又展示了一个例子:varobj={x:100,print:function(){console.log(this.x);}}obj。print()//100Python说:“这个很好理解,这个this应该指向obj的对象实例,所以print函数输出的x是100,对吧。""是的,我们再看一个:"varobj={x:100,y:{x:200,print:function(){console.log(this.x);}}}obj.y.print()//200Java说:“按照你的规则,this应该指向最后调用它的对象,也就是y,在y中,x是200,所以应该输出200!“JavaScript说:“如果我从对象y中删除x:200,输出是什么?”“是100吗?”不会的,它不会查找上层,只会查找y中x的值,没有值就是undefined,唉!你这个规矩真是太麻烦了。"JavaScript笑了:"让我们看一个更奇怪的例子:"varobj={x:100,y:{x:200,print:function(){console.log(this.x);}}}varpoint={x:15,y:15,f:obj.y.print}point.f()//输出什么?varx=10g=obj.y.printg()//输出什么?Python说:“这不是还是一样,应该输出200。”JavaScript说:“不,point.f()应该输出15。注意此时f是对象point的函数,最终调用f的对象是point对象。此时,x=15!”Java接口说:“我明白了,调用函数g()是全局对象,x=10,所以应该输出10。”Python说:“你这小子号称前端之王,所以你拿这个来折磨程序员?”JavaScript笑道:“其实普通程序员直接操作这个的机会不多,都是框架和类库封装的!”这时,我听到老C摇头:“简单就是美,简单就是美。”你们让世界变得如此复杂,让程序员学习这么多不必要的复杂性,这是在浪费生命。”“浪费生命?如果没有我们的语言,怎么能创建这么多Web应用程序?你可以吗?”“我不能,我只知道你的Java虚拟机是我C语言写的,你的Python解释器和Ruby解释器也是C语言写的,连JS的V8引擎也是我哥C++语言写的.》C老爷子将手中的咖啡往桌上一摔,转身离开了咖啡厅。【本文为专栏作家“刘鑫”原创稿件,转载请通过作者微信获取授权♂coderising】点击在这里,阅读更多作者的好文章