问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

从PHP 到Golang 的笔记 ( 转 )

发布网友 发布时间:2022-09-03 13:34

我来回答

1个回答

热心网友 时间:2024-01-12 15:21

———文章来源 YamiOdymel/PHP-to-Golang

PHP和模块之间的关系令人感到烦躁,假设你要读取 yaml 档案,你需要有一个 yaml 的模块,为此,你还需要将其编译然后将编译后的模块摆放至指定位置,之后换了一台伺服器你还要重新编译,这点到现在还是没有改善;顺带一提之后出了PHP 7效能确实提升了许多(比Python 3快了些),但PHP仍令我感到臃肿,我觉得是时候
(转行)了。

PHP 和Golang 的效能我想毋庸置疑是后者比较快(而且是以倍数来算),也许有的人会认为两种不应该被放在一起比较,但Golang 本身就是偏向Web 开发的,所以这也是为什么我考虑转用Golang 的原因,起初我的考虑有几个:Node.js 和Rust 还有最终被选定的Golang;先谈谈Node.js 吧。

Node.js的效能可以说是快上PHP 3.5倍至6倍左右 ,而且撰写的语言还是JavaScript,蒸蚌,如此一来就不需要学习新语言了!搭配Babel更可以说是万能,不过那跟「跳跳虎」一样的Async逻辑还有那恐怖的Callback Hell,有人认为前者是种优点,这点我不否认,但是对学习PHP的我来说太过于"Mind Fuck",至于后者的Callback Hell虽然有Promise,但是那又是另一个「Then Hell」的故事了。相较于Golang之下,Node.js似乎就没有那么吸引我了。你确实可以用Node.js写出很多东西,不过那V8引擎的效能仍然有限,而且要学习新的事物,不就应该是「全新」的吗;)?

题外话: 为什么Node.js不适合大型和商业专案?

在抛弃改用Node.js 之后我曾经花了一天的时间尝试Rust 和Iron 框架,嗯⋯⋯Rust 太强大了,强大到让我觉得Rust 不应该用在这里,这想法也许很蠢,但Rust 让我觉得适合更应该拿来用在系统或者是部分底层的地方,而不应该是网路服务。

Golang是我最终的选择,主要在于我花了一天的时间来研究的时候意外地发现Golang夭寿简洁( 关键字只有25个 ),相较之下Rust太过于「强大」令我怯步;而且Golang带有许多工具,例如 go fmt 会自动帮你整理程式码、 go doc 会自动帮你生产文件、 go test 可以自动单元测试并生产覆盖率报表、也有 go get 套件管理工具(虽然没有版本功能),不过都很实用,而且也不需要加上分号( ; ),真要说不好的地方⋯⋯大概就是强迫你花括号不能换行放吧(没错,我就是花括号会换行放的人)。

当我在撰写这份文件的时候 我会先假设你有一定的基础 ,你可以先阅读下列的手册,他们都很不错。

你能够在PHP 里面想建立一个变数的时候就直接建立,夭寿赞,是吗?

蒸蚌!那么Golang 呢?在Golang 中变数分为几类:「新定义」、「预先定义」、「自动新定义」、「覆盖」。让我们来看看范例:

在PHP中你会很常用到 echo 来显示文字,像这样。

然而在Golang中你会需要 fmt 套件,关于「什么是套件」的说明你可以在文章下述了解。

这很简单,而且两个语言的用法相差甚少,下面这是PHP:

只是Golang 稍微聒噪了一点,你必须在函式后面宣告他最后会回传什么资料型别。

在PHP 中你要回传多个资料你就会用上阵列,然后将资料放入阵列里面,像这样。

然而在Golang 中你可以不必用到一个阵列,函式可以一次回传多个值:

两个语言的撰写方式不尽相同。

主要是PHP 的阵列能做太多事情了,所以在PHP 里面要储存什么用阵列就好了。

在Golang里⋯⋯没有这么万能的东西,首先要先了解Golang中有这些型态: array , slice , map , interface ,

你*我到底看了三洨,首先你要知道Golang是个强型别语言,意思是你的阵列中只能有一种型态,什么意思?当你决定这个阵列是用来摆放字串资料的时候,你就只能在里面放字串。没有数值、没有布林值,就像你没有女朋友一样。

先撇开PHP 的「万能阵列」不管,Golang 中的阵列既单纯却又十分脑残,在定义一个阵列的时候,你必须给他一个长度还有其内容存放的资料型态,你的阵列内容不一定要填满其长度,但是你的阵列内容不能超过你当初定义的长度。

切片⋯⋯这听起来也许很奇怪,但是你确实可以「切」他,让我们先谈谈「切片」比起「阵列」要好在哪里:「你不用定义其最大长度,而且你可以直接赋予值」,没了。

我们刚才有提到你可以「切」他,记得吗?这有点像是PHP中的 array_slice() ,但是Golang直接让Slice「内建」了这个用法,其用法是: slice[开始:结束] 。

在PHP中倒是没有那么方便,在下列PHP范例中你需要不断地使用 array_slice() 。

你可以把「映照」看成是一个有键名和键值的阵列,但是记住:「你需要事先定义其键名、键值的资料型态」,这仍*你没办法在映照中存放多种不同型态的资料。

在Golang里可就没这么简单了,你需要先用 make() 宣告 map 。

也许你不喜欢「接口」这个词,但用「介面」我怕会误导大众,所以,是的,接下来我会继续称其为「接口」。还记得你可以在PHP 的关联阵列里面存放任何型态的资料吗,像下面这样?

现在你有福了!正因为Golang中的 interface{} 可以接受任何内容,所以你可以把它拿来存放任何型态的资料。

有时候你也许会有个不定值的变数,在PHP 里你可以直接将一个变数定义成字串、数值、空值、就像你那变心的女友一样随时都在变。

在Golang中你必须给予变数一个指定的资料型别,不过还记得刚才提到的:「Golang中有个 interface{} 能够存放任何事物」吗( 虽然也不是真的任何事物啦⋯⋯ )?

当我们程式中不需要继续使用到某个资源或是发生错误的时候,我们索性会将其关闭或是抛弃来节省资源开销,例如PHP 里的读取档案:

在Golang中,你可以使用 defer 来在函式结束的时候自动执行某些程式(其执行方向为反向)。所以你就不需要在函式最后面结束最前面的资源。

defer 可以被称为「推迟执行」,实际上就是在函式结束后会「反序」执行的东西,例如你按照了这样的顺序定义 defer : A->B->C->D ,那么执行的顺序其实会是 D->C->B->A ,这用在程式结束时还蛮有用的,让我们看看Golang如何改善上述范例。

这东西很*,不是吗?又不是在写BASIC,不过也许有时候你会在PHP 用上呢。但是拜托,不要。

Golang中仅有 for 一种回圈但却能够达成 foreach 、 while 、 for 多种用法。普通 for 回圈写法在两个语言中都十分相近。

在Golang请记得:如果你的 i 先前并不存在,那么你就需要定义它,所以下面这个范例你会看见 i := 0 。

在PHP里, foreach() 能够直接给你值和键名,用起来十分简单。

Golang里面虽然仅有 for() 但却可以使用 range 达成和PHP一样的 foreach 方式。

一个 while(条件) 回圈在PHP里面可以不断地执行区块中的程式,直到 条件 为 false 为止。

在Golang里也有相同的做法,但仍是透过 for 回圈,请注意这个 for 回圈并没有任何的分号( ; ),而且一个没有条件的 for 回圈会一直被执行。

PHP中有 do .. while() 回圈可以先做区块中的动作。

在Golang中则没有相关函式,但是你可以透过一个无止尽的 for 回圈加上条件式来让他结束回圈。

要是你真的希望完全符合像是PHP那样的设计方式,或者你可以在Golang中使用很*的 goto 。

在PHP中我们可以透过 date() 像这样取得目前的日期。

在Golang就稍微有趣点了,因为Golang中并不是以 Y-m-d 这种格式做为定义,而是 1 、 2 、 3 ,这令你需要去翻阅文件,才能够知道 1 的定义是代表什么。

俗话说:「爆炸就是艺术」,可爱的PHP用词真的很大胆,像是: explode() (爆炸)、 die() (死掉),回归正传,如果你想在PHP里面将字串切割成阵列,你可以这么做。

简单的就让一个字串给「爆炸」了,那么Golang 呢?

对了,记得引用 strings 套件。

这真的是很常用到的功能,就像物件一样有着键名和键值,在PHP 里面你很简单的就能靠阵列(Array)办到。

真是太棒了,那么Golang呢?用 map 是差不多啦。如果有必要的话,你可以稍微复习一下先前提到的「多资料储存型态-Stores」。

你很常会在PHP里面用 isset() 检查一个索引是否存在,不是吗?

在Golang里面很简单的能够这样办到(仅适用于 map )。

指针(有时也做参照)是一个像是「变数别名」的方法,这种方法让你不用整天覆盖旧的变数,让我们假设 A = 1; B = A; 这个时候 B 会复制一份 A 且两者不相干,倘若你希望修改 B 的时候实际上也会修改到 A 的值,就会需要指针。

指针比起复制一个变数,他会建立一个指向到某个变数的记忆体位置,这也就是为什么你改变指针,实际上是在改变某个变数。

在Golang你需要用上 * 还有 & 符号。

有些时候你会回传一个阵列,这个阵列里面可能有资料还有错误代号,而你会用条件式判断错误代号是否非空值。

在Golang中函式可以一次回传多个值。为此,你不需要真的回传一个阵列,不过要注意的是你将会回传一个属于 error 资料型态的错误,所以你需要引用 errors 套件来帮助你做这件事。

该注意的是Golang没有 try .. catch ,因为Golang推荐这种错误处理方式,你应该在每一次执行可能会发生错误的程式时就处理错误,而非后来用 try 到处包覆你的程式。

在 if 条件式里宣告变数会让你只能在 if 内部使用这个变数,而不会污染到全域范围。

也许你在PHP中更常用的会是 try .. catch ,在大型商业逻辑时经常看见如此地用法,实际上这种用法令人感到聒噪(因为你会需要一堆 try 区块):

Golang中并没有 try .. catch ,实际上Golang也不鼓励这种行为(Golang推荐逐一处理错误的方式),倘若你真想办倒像是捕捉异常这样的方式,你确实可以使用Golang中另类处理错误的方式(可以的话尽量避免使用这种方式): panic() , recover() , defer 。

你可以把 panic() 当作是 throw (丢出错误),而这跟PHP的 exit() 有87%像,一但你执行了 panic() 你的程式就会宣告而终,但是别担心,因为程式结束的时候会呼叫 defer ,所以我们接下来要在 defer 停止 panic() 。

关于 defer 上述已经有提到了,他是一个反向执行的宣告,会在函式结束后被执行,当你呼叫了 panic() 结束程式的时候,也就会开始执行 defer ,所以我们要在 defer 内使用 recover() 让程式不再继续进行结束动作,这就像是捕捉异常。

recover() 可以看作 catch (捕捉),我们要在 defer 里面用 recover() 解决 panic() ,如此一来程式就会回归正常而不会被结束。

还记得在PHP里要引用一堆档案的日子吗?到处可见的 require() 或是 include() ?到了Golang这些都不见了,取而代之的是「套件(Package)」。现在让我们来用PHP解释一下。

这看起来很正常对吧?但假设你有一堆档案,这马上就成了 Include Hell ,让我们看看Golang怎么透过「套件」解决这个问题。

「 蛤???杀小??? 」你可能如此地说道。是的, main.go 中除了引用 fmt 套件( 为了要输出结果用的套件 )之外完全没有引用到 a.go 。

「 蛤???杀小?????? 」你仿佛回到了几秒钟前的自己。

既然没有引用其他档案,为什么 main.go 可以输出 foo 呢?注意到了吗,两者都是属于 main 套件,因此他们共享同一个区域,所以接下来要介绍的是什么叫做「套件」。

套件是每一个 .go 档案都必须声明在Golang原始码中最开端的东西,像下面这样:

这意味着目前的档案是属于 main 套件( 你也可以依照你的喜好命名 ),那么要如何让同个套件之间的函式沟通呢?

接着是Golang;注意!你不需要引用任何档案,因为下列两个档案同属一个套件。

一个由「套件」所掌握的世界,比起PHP的 include() 和 require() 还要好太多了,对吗?

在Golang 中没有引用单独档案的方式,你必须汇入一整个套件,而且你要记住:「一定你汇入了,你就一定要使用它」,像下面这样。

假如你不希望使用你汇入的套件,你只是为了要触发那个套件的 main() 函式而引用的话⋯⋯,那么你可以在前面加上一个底线( _ )。

如果你的套件出现了名称冲突,你可以在套件来源前面给他一个新的名称。

现在你知道可以汇入套件了,那么什么是「汇出」?同个套件内的函式还有共享变数确实可以直接用,但那并不表示可以给其他套件使用,其方法取决于函式/变数的「开头大小写」

是的。Golang依照一个函式/变数的开头大小写决定这个东西是否可供「汇出」

这用在区别函式的时候格外有用,因为小写开头的任何事物都是不供汇出的,反之,大写开头的任何事物都是用来汇出供其他套件使用的。

一开始可能会觉得这是什么奇异的规定,但写久之后,你就能发现比起JavaScript和Python以「底线为开头的命名方式」还要来得更好;比起成天宣告 public 、 private 、 protected 还要来得更快。

在Golang 中没有类别,但有所谓的「建构体(Struct)」和「接口(Interface)」,这就能够满足几乎所有的需求了,这也是为什么我认为Golang 很简洁却又很强大的原因。

让我们先用PHP 建立一个类别,然后看看Golang 怎么解决这个问题。

虽然Golang没有类别,但是「建构体(Struct)」就十分地堪用了,首先你要知道在Golang中「类别」的成员还有方法都是在「类别」外面所定义的,这跟PHP在类别内定义的方式有所不同,在Golang中还有一点,那就是他们没有 public 、 private 、 protected 的种类。

在PHP中,当有一个类别被 new 的时候会自动执行该类别内的建构子( __construct() ),通常你会用这个来初始化一些类别内部的值。

但是在Golang 里因为没有类别,也就没有建构子,不巧的是建构体本身也不带有建构子的特性,这个时候你只能自己在外部建立一个建构用函式。

让我们假设你有两个类别,你会把其中一个类别传入到另一个类别里面使用,废话不多说!先上个PHP 范例(为了简短篇幅我省去了换行)。

在Golang中你也有相同的用法,但是请记得:「任何东西都是在「类别」外完成建构的」。

在PHP 中没有相关的范例,这部分会以刚才「嵌入」章节中的Golang 范例作为解说对象。

你可以看见Golang在进行 Foo 嵌入 Bar 的时候,会自动将 Foo 的成员暴露在 Bar 底下,那么假设「双方之间有相同的成员名称」呢?

这个时候被嵌入的成员就会被「遮蔽」,下面是个实际范例,还有你如何解决遮蔽问题:

虽然都是呼叫同一个函式,但是这个函式可以针对不同的资料来源做出不同的举动,这就是多形。你也能够把这看作是:「讯息的意义由接收者定义,而不是传送者」。

目前PHP 中没有真正的「多形」,不过你仍可以做出同样的东西。

嗯⋯⋯那么Golang呢?实际上更简单而且更有条理了,在Golang中有 interface 可以帮忙完成这个工作。

如果你对Interface还不熟悉,可以试着查看「 解释Golang中的Interface到底是什么 」文章。

谢谢你看到这里,可惜这篇文章却没有说出Golang 最重要的卖点:「Goroutine」和「Channel」

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
为什么我每过一段时间就有“鬼压床”?求解决方法! 低保户可以买产权房吗 民政审批低保户的条件有那些低保户年审会将名,下有小产权房作为审核条件... 什么情况下西班牙语的字母上要加一个上升或下降的符号? 除了这些有没有别的情况也用连字符: 书写法语时,连字符“-”是不能... 的必须是什么意思吗? 杭州43路公交车在那换乘599路公交车 杭州43路公交车到德胜西路站么 杭州德胜路到文二路坐车要多久 你好十月唯美句子 你好十月唯美的句子有哪些 彻底理解Golang Map 西兰花必须水处理吗? java我写了一个过滤器,所有访问的(js、jsp、各种图片等等)都会进过滤器... js正则过滤掉图片后面的问号 求js正则表达式过滤图片地址 谢了 无需手机号怎么注册? 没手机号怎么注册新 可以这样做 寓言故事一叶障目 什么是铝型材表面处理脱脂工艺? 手机号码绑定了一个不用的,怎么解除? 消防控制室与消防值班室可以设置在一起吗 消防值班室和消防控制室是一样的吗 消防控制室与消防值班室 怎样解除绑定的手机号? 想学漫画素描要买什么教程书? 相素还是像素 推荐一本好的 素描基础教程 的书 有好多这方面的书不知道该选什么 胖大海能否搭配金银花,金盏菊,无籽山楂一起使用? 还有一次应该用多少... 素描初学者用什么书比较好?求推荐! 想看90年前的电影下载什么软件 可以注销吗 正确的洗脸方法是什么?眼部按摩的手法是什么?去除轻微皱纹怎么去除? 可以注销吗? 能注销吗? 个人可以注销吗 可以注销吗?怎么注销? 利用手机号或者能不能找到人- 问一问 “不见五陵豪杰墓,无花无酒锄做田”是什么意思?武陵豪杰墓在哪?_百度... 利用手机号或者能不能找到人 能注销吗?怎么注销? 怎么注销60天内可以注销吗? 如果网恋的时候用网图,但你却爱上了对方,当对方要与你见面怎么办?_百 ... 网恋女朋友想和我见面了 我用的是网图 我已经爱上她了 我现在要不要... 河南变蛋怎么做 JS字符串截取常用方法 我不想长大的歌带歌词 求陈楚生《不想长大》歌词,感谢啊! 请问有一首老歌 歌词是这样的春季到来百花开 请问:同花顺自选股最小化之后,如何操作才能在屏幕上滚动显示即时股价和... 同花顺红色绿色怎么调出来- 问一问