面向 JavaScript 开发人员的 Golang - 第 1 部分 比较相似的事情 结论 参考文献:

2025-05-24

面向 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
Enter fullscreen mode Exit fullscreen mode


// 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")
}

Enter fullscreen mode Exit fullscreen mode

差异

  • 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();
Enter fullscreen mode Exit fullscreen mode


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()
}
Enter fullscreen mode Exit fullscreen mode
  • 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`);
Enter fullscreen mode Exit fullscreen mode


func holdMyBeer() (string, int64) {
    return "John", 2
}

func main() {
    a, b := holdMyBeer()
    fmt.Printf("Hey %s, hold my %d beer\n", a, b)
}
Enter fullscreen mode Exit fullscreen mode

范围

作用域是变量有效的上下文,它决定了变量可以在何处使用,JS 和 Go 在这方面有很多相似之处

相似之处

  • 两者都有函数作用域,并且函数可以记住其周围的作用域。
  • 两者都有块范围。
  • 两者都具有全球范围。

差异

  • Go 没有thisJavaScript 中比较棘手的概念。在我看来,这让 Go 中的事情变得简单多了。
  • Go 中,同一作用域内的变量不能重复声明。Govar更接近letJS 中的关键字。

流量控制

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]);
}
Enter fullscreen mode Exit fullscreen mode


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)
    }
}

Enter fullscreen mode Exit fullscreen mode

差异

  • Go 中没有三元运算符。
  • switch语句语法类似,但 Go 默认为 break,而 JS 默认为 fall through。在 Go 中,可以使用fallthrough关键字实现该功能,而在 JS 中,我们使用break关键字。
  • JS 有更多的迭代方式,例如while,,,forEach&循环等,这些方式在 Go 中是没有的,尽管它们中的大多数都可以使用for in语法实现for offor
  • if/else在 Go 中可以进行 init 赋值。在下面的代码中, forval赋值的作用域仅在ifandelse块内,而不在其外部。这在 JS 中是不可能的。


if val := getVal(); val < 10 {
    return val
} else {
    return val + 1
}
Enter fullscreen mode Exit fullscreen mode

内存管理

除了 JS 和 Go 中的细节之外,内存管理也非常相似。

相似之处

  • 两者在运行时都会被垃圾收集。
  • 两者都有堆和堆栈内存,这意味着相同。

差异

  • Go 具有向用户公开的指针,同时其内存管理被抽象出来,而在 JavaScript 中,指针被完全抽象出来,您只能使用值和引用。
  • Go 使用并发三色标记-清除算法,重点关注延迟,而 JS 引擎通常会实现不同的算法,其中标记-清除算法非常流行。例如,V8 引擎同时使用了标记-清除算法和清除算法。

杂项

  • //两者的评论都是一样的,/* */
  • JS 和 Go 都支持导入其他模块,尽管行为不一样
  • SetTimeout 两者类似。setTimeout(somefunction, 3*1000)vs time.AfterFunc(3*time.Second, somefunction)
  • 两者都有一个扩展运算符console.log(...array)vs。fmt.Println(array...)不过 Go 扩展仅适用于接口数组/切片。
  • 两者都有用于方法参数的 rest 运算...numsnums ...int

结论

在本部分中,我们了解了两种语言中相似的概念。在本系列的下一部分中,我们将了解 JS 和 Go 之间更多的不同之处。下一部分中会涉及比本部分更多的不同之处,但请注意,有些差异非常细微,因此 JavaScript 开发人员很容易理解。

下一章中我们将看到:

  • 类型和变量
  • 错误处理
  • 可变性
  • 组合而不是继承
  • 并发
  • 汇编
  • 范例

参考:


如果您喜欢这篇文章,请点赞或留言。

您可以在TwitterLinkedIn上关注我。


封面图片使用来自norfolkjs (由Lookmai Rattana设计)和juststickers 的图片制作

文章来源:https://dev.to/deepu105/golang-for-javascript-developers-part-1-38je
PREV
我美丽的Linux开发环境工作站ansible playbooks
NEXT
忘掉 NodeJS!用 Deno 构建原生 TypeScript 应用 🦖 安装 Deno 功能 Deno 应用实战 结论