简单的this,麻烦的this

周末的Hello World咖啡馆总是热闹非凡,门上贴着通知:java架构交流群:860170416,我们的目标是星辰大海。

Java , Python, Ruby, JavaScript围坐在一起,一边喝咖啡,一边海阔天空。

C老头儿则待在一旁,冷眼旁观。

聊着聊着,这话题不知怎么着转移到了“this”上来了。

Java 说: “唉!你们不知道吧,对于一个初学Java的人来说,this 是非常难于理解的。”

Python说:“this 在你那里已经够简单了啊。还难于理解?”

“我们都是支持面向对象编程的,在我这里,this 可以用到实例方法或者构造器中,表示对当前对象实例的引用。”

publicclassPoint{
privatedoublex=0.0;
privatedoubley=0.0;
publicPoint(intx,inty){
this.x=x;
this.y=y;
}

publicdoubledistanceTo(Pointthat){
doubledx=this.x-that.x;
doubledy=this.y-that.y;
returnMath.sqrt(dx*dx+dy*dy);
}
}
“这不很容易理解吗? ” Ruby 问道。

“对于第一次接触面向对象编程的人来说,他分不清这个当前对象this到底是哪个对象。” Java说,“我必须得再写一段代码给他掰扯一下。”

Pointp1=newPoint(1,1);
Pointp2=newPoint(2,2);

//this指向的是p1
p1.distanceTo(p2);

//this指向的是p2
p2.distanceTo(p1);
“对啊,this必须得有个上下文才能准确理解。” Python说,“还有,你那个this吧,是个隐式的,像我是显式的:”

classPoint:
def__init__(this,x,y):
this.x=x
this.y=y
defdistanceTo(this,point):
dx=this.x-point.x
dy=this.y-point.y
returnmath.sqrt(dx**2+dy**2)
Java 说:“你不是一直用self吗,怎么现在是this?”

Python笑道:“我这不是为了和你Java老弟保持一致嘛,反正只是个变量名,你想用this就用this,想用that就用that,只不过我们习惯于用self 。”

Ruby说: “Python兄,你把this放到方法中作为一个参数,实在是太丑陋了,一点美感都没有。”

“那是我们的哲学,我们信奉 Explicit is better than implicit。”

“可是在调用的时候,怎么不把一个对象传给那个方法?你的self去哪里了?”

p1=Point(1,1)
p2=Point(2,2)

p1.distanceTo(p2)
p2.distanceTo(p1)
“你怎么不写成: distanceTo(p1,p2) ?”

“那不行,” Python说,“如果那样的话我们就不是面向对象了,而面向过程了。”

“哼哼,” C老头儿在一旁冷笑一声,“说来说去,还不是披了一层面向对象的外衣,内部实现依然是面向过程的?!”

“此话怎讲?” Java一直以正统的面向对象自居,不像Python, Ruby ,Java即使是想输出一个Hello World也得定义一个类不可。

“就说你吧,Java小子,你的Java 源文件被编译以后变成了.class文件, 这个class文件被装载到了Java虚拟机的一个区域,这个区域叫什么?” C 老头儿出手不凡。

“当然是Method Area, 方法区了,这我会不知道?!”

“对啊,它为什么叫方法区? 为什么不叫Class Area, 类区?” C 老头儿真是别出心裁。

“这…… ” Java 语噻了,他从来就没有想过这个问题。

“你的方法区是被各个线程所共享的,存储虚拟机加载类的信息,常量池,其中最主要的就是类中定义的方法相关的代码。 而你创建的对象呢,却是放在‘堆中’,虚拟机在执行的时候,要从方法区找到‘方法’,这些方法的字节码在运行的过程中,会操作位于堆中的对象。 ”

简单的this,麻烦的this

“所以你看,你的数据和方法是分离的,一个地方是方法(所以叫方法区),一个地方是数据,和我们C写出的程序是一样的,都是面向过程的!” C老头儿经过一系列证明后做了最终陈述。

Python也沉默了,他知道,自己在运行时也和这种方式差不多。

过了一会儿,Java 醒悟了过来:“不对,老头儿你这是混淆概念,我们是站在程序员的角度在谈论语言是不是面向对象的,而你则把我们拉到了实现层面,这是不对的。”

Python也附和道:“对对,我们是面向对象的语言,抽象程度比你的面向过程要高!”

“抽象? 哼哼,” C 老头儿又冷笑一声,“Linus 用C 写了Linux,用C 写了Git, 你觉得他没有做抽象? 笑话! 依我看来,抽象就是要在变化的东西中找到不变的东西,和具体的编程语言关系不大啊。” C老头说了一句至理名言。

Java 悄悄对Python说: “老头儿主要做操作系统内核,操作系统中的那些虚拟内存,进程,线程,文件系统概念都很清晰, 并且很稳定,估计他没有接触到应用层变态的,不讲道理的业务逻辑。 ”

C 老头儿说:“别以为你们面向对象有多么了不起,我告诉你,有很多程序员,用着面向对象的语言,写着面向过程的程序!关键是人!”

Ruby 说:“两位兄台,算了,不和老头儿争论了,来看看我的this吧, 奥不, 是self, 我这里必须用self。 我的self 和你们的都不一样,在不同的位置表示不同的含义。比如说:”

classPoint

此处的Self就是Point这个类

putsSelfis:#{self}

定义一个Class级别(静态)的方法,self还是Point这个类

defself.name
putsSelfinsideclassmethodis:#{self}
end

定义一个实例方法,此处的self就是对象实例了

defname
putsSelfinsideinstancemethodis:#{self}
end
end
Java 说:“你这搞得太麻烦了,定义一个静态方法,用static 不就结了?”

半天都没有说话的JavaScript突然说道:“这也叫麻烦,来看看我是怎么处理this的!”

functionadd(y){
returnthis.x+y
}
熟悉面向对象的Java, Python看到这么古怪的代码,大为吃惊, 这是什么鬼? add函数中的这个this 到底指向谁?

JavaScript说:“不要大惊小怪! 我的this和你们的this ,self都不一样,它是动态的,在定义时确定不了到底指向谁,只有等到函数调用的时候才能确定,this 指向的是最终调用它的那个对象,比如:”

functionadd(y){
//此时的this指向的是全局的对象,在浏览器运行就是window
returnthis.x+y
}

x=10
console.log(add(20))
在这里调用add函数的是全局上下文, 所以this指向的是全局对象,输出的值是30 。

JavaScript说:“我还可以给add函数传递一个对象当作this。”

functionadd(y){
//此时的this指向的是对象obj,this.x是20,不是10
returnthis.x+y
}

x=10

varobj={x:20};
//传递一个对象给add函数
add.call(obj,20)//40
大家更加吃惊了。

JavaScript又展示了一个例子:

varobj={
x:100,
print:function(){
console.log(this.x);
}
}

obj.print()//100
Python说: “这个很容易理解,这个this应该是指向obj这个对象实例, 所以print函数输出的x是100,对吧。”

“对的,再来看一个:”

varobj={
x:100,
y:{
x:200,
print:function(){
console.log(this.x);
}
}
}
obj.y.print()//200
Java 说道:“按照你的规则,这个this 指向的应该是最终调用它的对象,那就是y , 在y中,x是200,所以应该输出200 !”

JavaScript说:“如果我把对象y中的x:200 给去掉,输出是什么? ”

“难道是100 ? 不, 它不会向上一级去找,只会在y中寻找x 的值,如果没有,就是undefined, 唉!你这this规则实在是太麻烦。”

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=10
g=obj.y.print
g()//输出什么?
Python说:“这不还是一样嘛, 都应该输出200。”

JavaScript说: “不,point.f() 应该输出15, 注意此时f 是对象point的一个函数,最终调用f 的是point对象,此时x = 15 了! ”

Java接口说:“我明白了,调用函数g()的是全局对象,x = 10 ,所以应该输出10 。”

Python说:“你小子号称前端之王,就这么用this来折磨程序员?”

JavaScript笑道:“其实吧,普通程序员直接操作this的机会也不太多,都被框架、类库封装好了!”

这时候就听到C老头儿在那里摇头晃脑: “简单就是美,简单就是美啊。你们这帮小子,把世界搞得这么复杂,让程序员们学习这么多不必要的复杂性,真是浪费生命啊。”

“浪费生命? 没有我们这些语言,怎么可能创建出这么多Web应用程序出来? 你行吗?”

“我是不行,我只知道你Java 虚拟机是用我C语言写的, 你Python解释器,Ruby解释器也是C语言写的, 就连JS的V8引擎也是用我的兄弟C++语言写的。”

C 老头儿把手中的咖啡往桌子上狠狠一摔,转身就离开了咖啡馆。
java架构交流群:860170416
(完)

文章来源:网络

### Vue.js 中 `this.$emit` 的用法 在 Vue.js 中,`this.$emit` 是用于子组件向父组件传递数据的核心机制之一。通过调用 `this.$emit(eventName, payload)` 方法,可以触发自定义事件并将附加的数据发送给父级组件。 以下是关于 `this.$emit` 的详细介绍及其具体用法: #### 基本语法 `this.$emit(event, ...args)` - **event**: 自定义事件名称(字符串),通常由开发者自行命名。 - **...args**: 可选参数,表示要传递到父组件的数据。 当子组件中执行 `this.$emit('eventName')` 后,父组件可以通过监听对应的事件名捕获此操作并处理逻辑[^3]。 --- #### 示例代码 假设有一个父子组件结构如下所示: ##### 子组件 (`ChildComponent.vue`) ```vue <template> <button @click="sendDataToParent">点击我</button> </template> <script> export default { name: 'ChildComponent', methods: { sendDataToParent() { const message = '来自子组件的消息'; this.$emit('custom-event', message); } } }; </script> ``` 在这个例子中,当按钮被点击时会触发 `sendDataToParent()` 方法,并通过 `this.$emit('custom-event', message)` 将消息传递至父组件。 --- ##### 父组件 (`ParentComponent.vue`) ```vue <template> <div> <h1>父组件接收的内容:</h1> <p>{{ receivedMessage }}</p> <child-component @custom-event="handleCustomEvent"></child-component> </div> </template> <script> import ChildComponent from './ChildComponent'; export default { components: { ChildComponent }, data() { return { receivedMessage: '' }; }, methods: { handleCustomEvent(message) { console.log('接收到子组件传来的消息:', message); this.receivedMessage = message; } } }; </script> ``` 在此处,父组件绑定了名为 `@custom-event` 的监听器来响应子组件发出的信号。一旦检测到该事件发生,则运行回调函数 `handleCustomEvent` 并更新状态变量 `receivedMessage`。 --- #### 使用场景扩展 除了基本功能外,在实际开发过程中还可以利用其他技巧增强灵活性与可维护性: 1. **动态绑定事件** 如果不确定具体的事件类型或者需要支持多种交互行为,可通过计算属性实现条件渲染或切换不同的处理器。 2. **跨多个层次通信** 对于较复杂的嵌套关系而言,单独依赖 `$emit/$on` 组合可能不够高效;此时推荐引入全局事件总线模式作为补充方案[^2]。 3. **Vue 3 新特性——emits声明** 在新版框架里新增了一个叫做 `emits` 的选项用来显式列举允许触发哪些类型的事件以及它们各自携带什么形式的有效载荷信息[^1]。 --- #### 注意事项 尽管 `this.$emit` 提供了一种简单而强大的方式让不同部分之间共享重要情报,但也需要注意以下几点以免造成不必要的麻烦: - 避免滥用过多双向绑定导致难以追踪因果链条; - 明确区分单方向流动原则即只读取而不修改上游提供的原始资料副本; - 当销毁当前实例前记得清理掉不再使用的订阅者以防内存泄漏等问题出现。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值