面向 JavaScript 开发人员的 Golang - 第 1 部分
更相似的事情
结论
参考:
最初发表于deepu.tech。
如果您是一名 JavaScript 开发者,并且正在考虑学习其他编程语言,那么 Golang 是一个不错的选择。它简单易用,发展势头强劲,性能卓越,并且与 JavaScript 有一些相似之处。
编辑:有人在评论中问我,为什么 JS 开发者应该在众多可用选项中选择 Go。在我看来,JS 并非一门完美的语言,因此学习其他几种语言将极大地帮助 JS 开发者更务实地使用 JS,并有助于巩固其对基础编程概念的理解。当然,市面上有很多选择,比如 Rust、Go、Haskel、Kotlin 等等,但我认为 Go 是一个很好的起点,因为它是所有可用选项中最简单的之一,并且被广泛采用。我的第二选择是 Kotlin 或Rust。
这篇文章并非旨在比较这两种语言,也并非旨在表明它们非常相似。它只是一份帮助 JavaScript 开发者快速掌握 Golang 的指南。Go 有很多方面与 JavaScript 截然不同,我们也会对此进行探讨。
更相似的事情
Go 中有很多概念与 JavaScript 中的概念非常相似。它们虽然大部分内容并不相同,但又很相似。让我们先来了解一下它们。在本系列的第一部分中,我们将了解它们之间的相似之处,并指出它们之间的关键区别。
功能
JS 和 Go 最相似的特性就是函数。
相似之处
- 函数是一等公民。
- 函数可以分配给变量。
- 函数可以作为参数传递给其他函数,也可以从函数返回。
- 函数可以嵌套。
- 函数可以被柯里化(部分函数)。
- 函数可以记住其周围的环境,从而创建闭包。
- 函数可以是命名函数,也可以是匿名函数。匿名函数可以立即调用(IIFE)。
JavaScript
// A normal function with access to `this`
function standardFunction(arg1, arg2) {
return `${arg1}:${arg2}`;
}
// A function assigned to a variable
const assignedFunction1 = standardFunction;
// An arrow function assigned to a variable
const assignedArrowFunction = (arg1, arg2) => {
return `${arg1}:${arg2}`;
};
// A higher-order-function that accepts functions as argument and returns a function
function functionAsArgumentAndReturn(addFn, arg1, arg2) {
const out = addFn(arg1, arg2);
// This returns a closure
return function (numArg) {
return out + numArg;
};
}
const out = functionAsArgumentAndReturn(
(a, b) => {
return a + b;
},
5,
10
)(10);
// returns 25
// Nested functions
function nested() {
console.log("outer fn");
function nested2() {
console.log("inner fn");
const arrow = () => {
console.log("inner arrow");
};
arrow();
}
nested2();
}
nested(); // prints:
// outer fn
// inner fn
// inner arrow
// this is a higher-order-function that returns a function
function add(x) {
// A function is returned here as closure
// variable x is obtained from the outer scope of this method and memorized in the closure
return (y) => x + y;
}
// we are currying the add method to create more variations
var add10 = add(10);
var add20 = add(20);
var add30 = add(30);
console.log(add10(5)); // 15
console.log(add20(5)); // 25
console.log(add30(5)); // 35
// An anonymous function invoked immediately(IIFE)
(function () {
console.log("anonymous fn");
})();
// prints: anonymous fn
去
// A normal function, this cannot be nested
func standardFunction(arg1 string, arg2 string) string {
return fmt.Sprintf("%s:%s", arg1, arg2)
}
func main() {
// A function assigned to a variable
var assignedFunction1 = standardFunction
// An anonymous function assigned to a variable and nested
var assignedFunction2 = func(arg1 string, arg2 string) string {
return fmt.Sprintf("%s:%s", arg1, arg2)
}
// A higher-order-function that accepts functions as argument and returns a function
var functionAsArgumentAndReturn = func(addFn func(int, int) int, arg1 int, arg2 int) func(int) int {
var out = addFn(arg1, arg2)
// This returns a closure
return func(numArg int) int {
return out + numArg
}
}
var out = functionAsArgumentAndReturn(
func(a, b int) int {
return a + b
},
5,
10,
)(10)
fmt.Println(out) // prints 25
// Nested anonymous functions
var nested = func() {
fmt.Println("outer fn")
var nested2 = func() {
fmt.Println("inner fn")
var nested3 = func() {
fmt.Println("inner arrow")
}
nested3()
}
nested2()
}
nested() // prints:
// outer fn
// inner fn
// inner arrow
// this is a higher-order-function that returns a function
var add = func(x int) func(y int) int {
// A function is returned here as closure
// variable x is obtained from the outer scope of this method and memorized in the closure
return func(y int) int {
return x + y
}
}
// we are currying the add method to create more variations
var add10 = add(10)
var add20 = add(20)
var add30 = add(30)
fmt.Println(add10(5)) // 15
fmt.Println(add20(5)) // 25
fmt.Println(add30(5)) // 35
// An anonymous function invoked immediately(IIFE)
(func() {
fmt.Println("anonymous fn")
})()
// prints: anonymous fn
assignedFunction1("a", "b")
assignedFunction2("a", "b")
}
差异
- JavaScript 函数有两种形式:常规函数和箭头函数;而 Go 函数则分为普通函数和接口函数。普通 Go 函数没有
this
,因此更类似于箭头函数;而接口函数有 ,this
因此更接近 JavaScript 中的普通函数。Go 没有全局 的概念this
。
JavaScript
function normalFnOutsideClass() {
console.log(`I still can access global this: ${this}`);
}
const arrowFnOutsideClass = () => {
console.log(`I don't have any this`);
};
class SomeClass {
name = "Foo";
normalFnInsideClass = function () {
console.log(`I can access the callee as this: ${this.name}`);
};
arrowFnInsideClass = () => {
console.log(`I can access the class reference as this: ${this.name}`);
};
}
new SomeClass().normalFnInsideClass();
new SomeClass().arrowFnInsideClass();
去
type SomeStruct struct {
name string
}
func (this *SomeStruct) normalFnInsideStruct() {
// you can name the variable this or anything else
fmt.Printf("I can access the struct reference as this\n: %s", this.name)
}
func main() {
var normalFnOutsideStruct = func() {
fmt.Println("I can access variables in my scope")
}
normalFnOutsideStruct()
var structVal = SomeStruct{"Foo"}
structVal.normalFnInsideStruct()
}
- JavaScript 函数与任何其他值类型相同,因此甚至可以保存 Go 中无法实现的附加属性。
- Go 函数可以有隐式命名的返回。
- Go 中只能嵌套匿名函数。
- Go 函数可以返回多个值,而 JavaScript 只能返回一个值。不过,在 JS 中,你可以使用解构来解决这个问题,这样就可以在 Go 和 JavaScript 中实现类似的函数。
JavaScript
function holdMyBeer() {
return ["John", 2];
}
let [a, b] = holdMyBeer();
console.log(`Hey ${a}, hold my ${b} beer\n`);
去
func holdMyBeer() (string, int64) {
return "John", 2
}
func main() {
a, b := holdMyBeer()
fmt.Printf("Hey %s, hold my %d beer\n", a, b)
}
范围
作用域是变量有效的上下文,它决定了变量可以在何处使用,JS 和 Go 在这方面有很多相似之处
相似之处
- 两者都有函数作用域,并且函数可以记住其周围的作用域。
- 两者都有块范围。
- 两者都具有全球范围。
差异
- Go 没有
this
JavaScript 中比较棘手的概念。在我看来,这让 Go 中的事情变得简单多了。 - Go 中,同一作用域内的变量不能重复声明。Go
var
更接近let
JS 中的关键字。
流量控制
Golang 中的流控制在很多方面与 JavaScript 非常相似但更简单。
相似之处
for
两者的循环非常相似。while
循环非常相似,尽管 Go 使用相同的for
关键字。forEach
功能上也类似,但语法却截然不同。- 你可以中断/继续循环。你也可以使用标签来执行此操作。
if/else
语法非常相似,Go 版本更强大一些
JavaScript
// For loop
for (let i = 0; i < 10; i++) {
console.log(i);
}
// While loop
let i = 0;
while (i < 10) {
console.log(i);
i++;
}
// Do while
let j = 0;
do {
j += 1;
console.log(j);
} while (j < 5);
// ForEach loop
["John", "Sam", "Ram", "Sabi", "Deepu"].forEach((v, i) => {
console.log(`${v} at index ${i}`);
});
// for of loop
for (let i of ["John", "Sam", "Ram", "Sabi", "Deepu"]) {
console.log(i);
}
// For in loop
const obj = {
a: "aVal",
b: "bVal",
};
for (let i in obj) {
console.log(obj[i]);
}
去
func main() {
// For loop
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// While loop
i := 0
for i < 10 {
fmt.Println(i)
i++
}
// Do while
j := 0
for {
j += 1
fmt.Println(j)
if j == 5 {
break
}
}
// ForEach and for of loop
for i, v := range []string{"John", "Sam", "Ram", "Sabi", "Deepu"} {
fmt.Printf("%v at index %d\n", v, i)
}
// For in loop
var obj = map[string]string{
"a": "aVal",
"b": "bVal",
}
for i, v := range obj {
fmt.Printf("%v at index %s\n", v, i)
}
}
差异
- Go 中没有三元运算符。
switch
语句语法类似,但 Go 默认为 break,而 JS 默认为 fall through。在 Go 中,可以使用fallthrough
关键字实现该功能,而在 JS 中,我们使用break
关键字。- JS 有更多的迭代方式,例如
while
,,,forEach
&循环等,这些方式在 Go 中是没有的,尽管它们中的大多数都可以使用for in
语法实现。for of
for
if/else
在 Go 中可以进行 init 赋值。在下面的代码中, forval
赋值的作用域仅在if
andelse
块内,而不在其外部。这在 JS 中是不可能的。
去
if val := getVal(); val < 10 {
return val
} else {
return val + 1
}
内存管理
除了 JS 和 Go 中的细节之外,内存管理也非常相似。
相似之处
- 两者在运行时都会被垃圾收集。
- 两者都有堆和堆栈内存,这意味着相同。
差异
- Go 具有向用户公开的指针,同时其内存管理被抽象出来,而在 JavaScript 中,指针被完全抽象出来,您只能使用值和引用。
- Go 使用并发三色标记-清除算法,重点关注延迟,而 JS 引擎通常会实现不同的算法,其中标记-清除算法非常流行。例如,V8 引擎同时使用了标记-清除算法和清除算法。
杂项
//
两者的评论都是一样的,/* */
- JS 和 Go 都支持导入其他模块,尽管行为不一样
- SetTimeout 两者类似。
setTimeout(somefunction, 3*1000)
vstime.AfterFunc(3*time.Second, somefunction)
。 - 两者都有一个扩展运算符
console.log(...array)
vs。fmt.Println(array...)
不过 Go 扩展仅适用于接口数组/切片。 - 两者都有用于方法参数的 rest 运算
...nums
符nums ...int
。
结论
在本部分中,我们了解了两种语言中相似的概念。在本系列的下一部分中,我们将了解 JS 和 Go 之间更多的不同之处。下一部分中会涉及比本部分更多的不同之处,但请注意,有些差异非常细微,因此 JavaScript 开发人员很容易理解。
在下一章中我们将看到:
- 类型和变量
- 错误处理
- 可变性
- 组合而不是继承
- 并发
- 汇编
- 范例
参考:
- http://www.pazams.com/Go-for-Javascript-Developers/
- https://github.com/miguelmota/golang-for-nodejs-developers
如果您喜欢这篇文章,请点赞或留言。
封面图片使用来自norfolkjs (由Lookmai Rattana设计)和juststickers 的图片制作
文章来源:https://dev.to/deepu105/golang-for-javascript-developers-part-1-38je