语法全景对照

23 阅读6分钟

PHP、Go、JavaScript (ES6+) 核心语法全景对照指南

第一部分:变量声明与基础数据类型

三种语言在变量作用域、类型检查机制上有着本质区别:

  • PHP:弱类型(但 PHP 7/8 引入了强类型声明),变量以 $ 开头,作用域通常在函数内。
  • Go:静态强类型,编译型语言,严格区分类型,支持类型推导 :=,包级作用域与块级作用域。
  • JavaScript:弱类型,动态脚本语言,推荐使用 letconst 实现块级作用域。

1. PHP 实现

<?php
declare(strict_types=1); // 开启严格类型模式

// 1. 变量与常量声明
$username = "Alice"; // 字符串
$age = 25;           // 整型
$balance = 100.50;   // 浮点型
$isActive = true;    // 布尔型
$data = null;        // 空值

// 常量
define('MAX_LOGIN_ATTEMPTS', 5);
const APP_VERSION = "1.0.0";

// 2. 字符串操作
$greeting = "Hello, $username!"; // 双引号支持变量解析
$concat = 'Age: ' . $age;        // 单引号不支持解析,使用 . 拼接

// 3. 类型声明 (PHP 7.4+)
function printUserInfo(string $name, int $age, ?float $bal): void {
    echo "User: {$name}, Age: {$age}, Balance: " . ($bal ?? 0.0) . "\n";
}

printUserInfo($username, $age, $balance);

2. Go 实现

package main

import "fmt"

// 1. 包级变量与常量声明
const MaxLoginAttempts int = 5
const AppVersion = "1.0.0" // 无类型常量,根据上下文推导

var globalConfig string = "default"

func variablesDemo() {
    // 2. 局部变量声明
    var username string = "Alice"
    var age int = 25
    var balance float64 = 100.50
    var isActive bool = true
    
    // 短变量声明 (仅限函数内部)
    data := "Some Data" // 自动推导为 string
    
    // Go 中没有 null,只有各类型的零值 (nil 适用于指针、切片、映射、接口等)
    var ptr *int = nil 

    // 3. 字符串操作
    // Go 字符串不可变,支持双引号和反引号(多行)
    greeting := fmt.Sprintf("Hello, %s!", username)
    concat := "Age: " + fmt.Sprint(age)

    printUserInfo(username, age, balance)
    fmt.Println(greeting, concat, isActive, data, ptr)
}

// 4. 函数参数严格定型
func printUserInfo(name string, age int, bal float64) {
    fmt.Printf("User: %s, Age: %d, Balance: %.2f\n", name, age, bal)
}

3. JavaScript 实现

// 1. 变量与常量声明 (ES6+)
const MAX_LOGIN_ATTEMPTS = 5; // 常量,不可重新赋值
const APP_VERSION = "1.0.0";

let username = "Alice"; // 块级作用域变量
let age = 25;           // Number 类型 (JS 中不区分整型和浮点型)
let balance = 100.50;   // Number 类型
let isActive = true;    // Boolean
let data = null;        // Null 类型
let notDefined;         // Undefined 类型

// 2. 字符串操作
// 模板字符串 (Template Literals),支持多行和表达式插值
let greeting = `Hello, ${username}!`; 
let concat = 'Age: ' + age;

// 3. 弱类型函数 (可以使用 TypeScript 增加强类型)
function printUserInfo(name, age, bal) {
    // 使用 nullish coalescing operator (??) 赋默认值
    console.log(`User: ${name}, Age: ${age}, Balance: ${bal ?? 0.0}`);
}

printUserInfo(username, age, balance);

第二部分:复合数据结构(数组、字典、切片)

  • PHP:天下无敌的 Array(实际上是有序哈希表),既当列表又当字典。
  • Go:严格区分 Array(定长)、Slice(动态切片)和 Map(哈希表)。
  • JavaScript:区分 Array(动态列表)和 Object/Map(键值对)。

1. PHP 的万能数组

<?php
// 1. 索引数组 (List)
$fruits = ["Apple", "Banana", "Orange"];
$fruits[] = "Mango"; // 追加元素
array_push($fruits, "Grape");

// 2. 关联数组 (Map/Dictionary)
$user = [
    "id" => 101,
    "username" => "bob_smith",
    "email" => "bob@example.com",
    "roles" => ["admin", "editor"] // 嵌套数组
];

// 添加/修改键值对
$user["status"] = "active";

// 3. 数组遍历
foreach ($user as $key => $value) {
    if (is_array($value)) {
        echo "$key: " . implode(", ", $value) . "\n";
    } else {
        echo "$key: $value\n";
    }
}

// 4. 常用数组操作
$keys = array_keys($user);
$hasEmail = array_key_exists("email", $user);
$filtered = array_filter($fruits, fn($f) => strlen($f) > 5); // 闭包过滤

2. Go 的 Slice 与 Map

package main

import (
    "fmt"
    "strings"
)

func dataStructuresDemo() {
    // 1. 数组 (Array) - 长度固定,较少直接使用
    var arr [3]string = [3]string{"Apple", "Banana", "Orange"}

    // 2. 切片 (Slice) - 动态数组,最常用
    fruits := []string{"Apple", "Banana", "Orange"}
    fruits = append(fruits, "Mango", "Grape") // 追加元素

    // 3. 映射 (Map) - 键值对,必须使用 make 初始化或字面量初始化
    user := map[string]interface{}{ // 使用 interface{} 支持不同类型的值
        "id":       101,
        "username": "bob_smith",
        "email":    "bob@example.com",
        "roles":    []string{"admin", "editor"},
    }

    // 添加/修改键值对
    user["status"] = "active"

    // 4. 遍历
    for key, value := range user {
        // 类型断言 (Type Assertion)
        if roles, ok := value.([]string); ok {
            fmt.Printf("%s: %s\n", key, strings.Join(roles, ", "))
        } else {
            fmt.Printf("%s: %v\n", key, value)
        }
    }

    // 5. 检查键是否存在
    email, exists := user["email"]
    if exists {
        fmt.Println("Email found:", email)
    }
}

3. JavaScript 的 Array 与 Object

// 1. 数组 (Array)
const fruits = ["Apple", "Banana", "Orange"];
fruits.push("Mango", "Grape"); // 追加元素

// 2. 对象 (Object) - 作为字典使用
const user = {
    id: 101,
    username: "bob_smith",
    email: "bob@example.com",
    roles: ["admin", "editor"]
};

// 添加/修改键值对
user.status = "active";
user["last_login"] = "2023-10-01";

// 3. 遍历对象
for (const [key, value] of Object.entries(user)) {
    if (Array.isArray(value)) {
        console.log(`${key}: ${value.join(", ")}`);
    } else {
        console.log(`${key}: ${value}`);
    }
}

// 4. 常用高级操作 (ES6 数组方法)
const keys = Object.keys(user);
const hasEmail = "email" in user;
// 链式调用
const longFruits = fruits
    .filter(f => f.length > 5)
    .map(f => f.toUpperCase());

第三部分:函数、闭包与高阶特性

1. PHP 函数特性

<?php
// 1. 默认参数与可变参数
function buildQuery(string $table, array $conditions = [], string ...$fields): string {
    $select = empty($fields) ? "*" : implode(", ", $fields);
    $sql = "SELECT {$select} FROM {$table}";
    if (!empty($conditions)) {
        $sql .= " WHERE " . http_build_query($conditions, '', ' AND ');
    }
    return $sql;
}

// 2. 匿名函数与闭包 (使用 use 关键字引入外部变量)
$multiplier = 3;
$calculate = function (int $number) use ($multiplier): int {
    return $number * $multiplier;
};

// 3. 箭头函数 (PHP 7.4+,单行,自动捕获外部变量)
$calculateArrow = fn(int $number) => $number * $multiplier;

// 4. 命名参数 (PHP 8.0+)
$query = buildQuery(
    fields: "id", "name",
    table: "users",
    conditions: ["status" => 1]
);

2. Go 函数特性

package main

import (
    "fmt"
    "strings"
)

// 1. 多返回值与可变参数
func buildQuery(table string, conditions map[string]interface{}, fields ...string) (string, error) {
    if table == "" {
        return "", fmt.Errorf("table name cannot be empty")
    }
    
    selectFields := "*"
    if len(fields) > 0 {
        selectFields = strings.Join(fields, ", ")
    }
    
    sql := fmt.Sprintf("SELECT %s FROM %s", selectFields, table)
    // 省略复杂的 conditions 拼接逻辑...
    
    return sql, nil
}

func functionDemo() {
    // 2. 匿名函数与闭包 (自动捕获外部变量,无需类似 PHP 的 use)
    multiplier := 3
    calculate := func(number int) int {
        return number * multiplier
    }
    
    fmt.Println(calculate(10)) // 30

    // 3. 延迟执行 defer (Go 独有,常用于资源清理)
    defer fmt.Println("This runs at the end of functionDemo")
    
    // 4. 处理多返回值
    query, err := buildQuery("users", nil, "id", "name")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(query)
}

3. JavaScript 函数特性

// 1. 默认参数与剩余参数 (Rest parameters)
function buildQuery(table, conditions = {}, ...fields) {
    const selectFields = fields.length === 0 ? "*" : fields.join(", ");
    let sql = `SELECT ${selectFields} FROM ${table}`;
    
    const condKeys = Object.keys(conditions);
    if (condKeys.length > 0) {
        const where = condKeys.map(k => `${k}=${conditions[k]}`).join(" AND ");
        sql += ` WHERE ${where}`;
    }
    return sql;
}

// 2. 匿名函数赋值
const calculate = function(number) {
    return number * multiplier; // 依赖外部作用域变量
};

// 3. 箭头函数 (Arrow Functions, 不绑定自己的 this)
let multiplier = 3;
const calculateArrow = (number) => number * multiplier;

// 4. 解构赋值传参
function processUser({ id, username, roles = [] }) {
    console.log(`Processing ${username} (ID: ${id}) with roles: ${roles}`);
}

const userObj = { id: 1, username: "admin", email: "a@a.com" };
processUser(userObj); // 只提取需要的字段

第四部分:面向对象 (OOP) 与结构体

  • PHP:经典的基于类的单继承 OOP(Class, Interface, Abstract, Trait)。
  • Go:没有 Class 和继承。通过 Struct(结构体)封装数据,通过给结构体绑定方法实现行为,通过 Interface(鸭子类型)实现多态,通过结构体嵌套实现组合。
  • JavaScript:基于原型链(Prototype)。ES6 引入了 class 语法糖。

1. PHP 的经典 OOP

<?php

// 接口定义契约
interface LoggerInterface {
    public function log(string $message): void;
}

// Trait 代码复用机制
trait TimestampTrait {
    protected function getTimestamp(): string {
        return date('Y-m-d H:i:s');
    }
}

// 抽象类
abstract class BaseService {
    protected LoggerInterface $logger;
    
    public function __construct(LoggerInterface $logger) {
        $this->logger = $logger;
    }
    
    abstract public function execute(): bool;
}

// 具体实现类
class PaymentService extends BaseService {
    use TimestampTrait;

    // PHP 8.0 构造器属性提升
    public function __construct(
        LoggerInterface $logger,
        private float $amount
    ) {
        parent::__construct($logger);
    }

    public function execute(): bool {
        $time = $this->getTimestamp();
        $this->logger->log("[{$time}] Processing payment of {$this->amount}");
        return true;
    }
}

// 匿名类实现接口
$consoleLogger = new class implements LoggerInterface {
    public function log(string $message): void {
        echo "CONSOLE: $message\n";
    }
};

$service = new PaymentService($consoleLogger, 99.99);
$service->execute();

2. Go 的结构体与接口 (组合与鸭子类型)

package main

import (
    "fmt"
    "time"
)

// 1. 接口定义 (Go 的接口是隐式实现的)
type Logger interface {
    Log(message string)
}

// 2. 结构体 (代替类)
type ConsoleLogger struct {
    Prefix string
}

// 3. 为结构体绑定方法 (实现 Logger 接口)
// 只要实现了 Log 方法,它就是 Logger
func (c *ConsoleLogger) Log(message string) {
    fmt.Printf("%s: %s\n", c.Prefix, message)
}

// 4. 基础服务结构体
type BaseService struct {
    logger Logger // 依赖注入
}

// 5. 具体服务结构体 (通过嵌套实现类似继承的"组合")
type PaymentService struct {
    BaseService // 匿名嵌套,继承了 BaseService 的字段
    Amount      float64
}

// 为 PaymentService 定义方法
func (p *PaymentService) Execute() bool {
    timestamp := time.Now().Format("2006-01-02 15:04:05")
    msg := fmt.Sprintf("[%s] Processing payment of %.2f", timestamp, p.Amount)
    // 调用嵌套结构体中的 logger
    p.logger.Log(msg)
    return true
}

func oopDemo() {
    logger := &ConsoleLogger{Prefix: "SYS_LOG"}
    
    service := &PaymentService{
        BaseService: BaseService{logger: logger},
        Amount:      99.99,
    }
    
    service.Execute()
}

3. JavaScript 的 Class 语法糖

// JS 没有内置的 Interface,通常靠文档或 TypeScript 约束

// 1. 定义类
class BaseService {
    // 私有字段 (ES2022+)
    #logger;

    constructor(logger) {
        this.#logger = logger;
    }

    // Getter
    get logger() {
        return this.#logger;
    }

    // 抛出错误模拟抽象方法
    execute() {
        throw new Error("Method 'execute()' must be implemented.");
    }
}

// 2. 继承
class PaymentService extends BaseService {
    #amount;

    constructor(logger, amount) {
        super(logger); // 必须调用 super
        this.#amount = amount;
    }

    // 私有方法
    #getTimestamp() {
        return new Date().toISOString();
    }

    // 方法重写
    execute() {
        const time = this.#getTimestamp();
        this.logger.log(`[${time}] Processing payment of ${this.#amount}`);
        return true;
    }
}

// 3. 对象字面量实现依赖 (鸭子类型)
const consoleLogger = {
    log: function(message) {
        console.log(`CONSOLE: ${message}`);
    }
};

const service = new PaymentService(consoleLogger, 99.99);
service.execute();

第五部分:错误与异常处理

1. PHP (Try-Catch)

<?php
class CustomDatabaseException extends Exception {}

function connectDB(string $host) {
    if (empty($host)) {
        // 抛出异常
        throw new CustomDatabaseException("Host cannot be empty");
    }
    // 模拟连接成功
    return true;
}

try {
    connectDB("");
} catch (CustomDatabaseException $e) {
    error_log("DB Error: " . $e->getMessage());
} catch (Exception $e) {
    // 捕获其他所有异常
    error_log("General Error: " . $e->getMessage());
} finally {
    // 无论是否报错都会执行,常用于释放资源
    echo "Cleanup resources.\n";
}

2. Go (Error 值返回与 Panic)

Go 不推荐使用类似 try-catch 的控制流,而是将错误作为普通的返回值处理。

package main

import (
    "errors"
    "fmt"
)

// 定义自定义错误变量
var ErrEmptyHost = errors.New("host cannot be empty")

func connectDB(host string) (bool, error) {
    if host == "" {
        // 返回错误值
        return false, ErrEmptyHost
    }
    return true, nil
}

func errorDemo() {
    success, err := connectDB("")
    
    // 显式检查错误 (Go 的标志性写法)
    if err != nil {
        // 错误判定 (Go 1.13+)
        if errors.Is(err, ErrEmptyHost) {
            fmt.Println("DB Error: Provided host is empty.")
        } else {
            fmt.Println("Unknown Error:", err)
        }
        return
    }
    
    fmt.Println("Connected:", success)
    
    // Go 中的 Panic/Recover 仅用于极其严重的不可恢复错误
    // 类似于 try-catch,但不应作为常规业务逻辑
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    // panic("Critical failure!") 
}

3. JavaScript (Try-Catch)

class CustomDatabaseError extends Error {
    constructor(message) {
        super(message);
        this.name = "CustomDatabaseError";
    }
}

function connectDB(host) {
    if (!host) {
        throw new CustomDatabaseError("Host cannot be empty");
    }
    return true;
}

try {
    connectDB("");
} catch (error) {
    if (error instanceof CustomDatabaseError) {
        console.error("DB Error:", error.message);
    } else {
        console.error("General Error:", error);
    }
} finally {
    console.log("Cleanup resources.");
}

第六部分:并发与异步处理 (核心差异)

这是三种语言差异最大的地方:

  • PHP:传统模型是同步阻塞的(多进程模型,如 PHP-FPM),每次请求一个进程。
  • Go:天生为并发设计。使用轻量级的 goroutinechannel 进行通信。
  • JavaScript:单线程事件循环。使用回调、Promiseasync/await 处理非阻塞 I/O。

1. PHP (同步阻塞)

<?php
// PHP 原生核心不支持非阻塞异步(不借助 Swoole/ReactPHP 等扩展)
function fetchData(string $url): string {
    sleep(2); // 模拟耗时网络请求,这里会阻塞整个进程
    return "Data from $url";
}

echo "Start\n";
$data1 = fetchData("API_1"); // 阻塞 2 秒
$data2 = fetchData("API_2"); // 阻塞 2 秒
echo "End: $data1, $data2\n"; // 总耗时 4 秒

2. Go (Goroutines 与 Channels)

Go 语言可以通过 go 关键字瞬间启动成千上万个并发任务。

package main

import (
    "fmt"
    "sync"
    "time"
)

// 模拟耗时请求
func fetchData(url string, ch chan<- string, wg *sync.WaitGroup) {
    defer wg.Done() // 函数结束时通知 WaitGroup 完成
    
    time.Sleep(2 * time.Second) // 模拟耗时
    // 将结果发送到通道 Channel
    ch <- fmt.Sprintf("Data from %s", url) 
}

func concurrencyDemo() {
    fmt.Println("Start")
    
    // 创建一个通道用于接收结果
    results := make(chan string, 2)
    // WaitGroup 用于等待所有 goroutine 完成
    var wg sync.WaitGroup
    
    urls := []string{"API_1", "API_2"}
    
    for _, url := range urls {
        wg.Add(1)
        // 开启 Goroutine 并发执行
        go fetchData(url, results, &wg) 
    }
    
    // 开启一个后台 Goroutine 等待所有任务完成并关闭通道
    go func() {
        wg.Wait()
        close(results)
    }()
    
    // 从通道中读取数据(阻塞直到有数据或通道关闭)
    for data := range results {
        fmt.Println("Received:", data)
    }
    
    fmt.Println("End") // 总耗时约 2 秒
}

3. JavaScript (Async / Await 与 Promise)

JS 使用异步非阻塞 I/O,主线程不等待,而是把回调挂起。

// 模拟返回 Promise 的耗时请求
function fetchData(url) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(`Data from ${url}`);
        }, 2000);
    });
}

// 使用 async/await 以同步的代码风格写异步逻辑
async function main() {
    console.log("Start");
    
    try {
        // 并发执行多个 Promise
        const [data1, data2] = await Promise.all([
            fetchData("API_1"),
            fetchData("API_2")
        ]);
        
        console.log(`Received: ${data1}`);
        console.log(`Received: ${data2}`);
    } catch (error) {
        console.error("Async Error:", error);
    }
    
    console.log("End"); // 总耗时约 2 秒
}

main();

总结

  • PHP:围绕 Web 请求生命周期构建,数组操作极其灵活,OOP 体系严谨成熟,适合快速开发传统 Web 后端。
  • Go:静态编译,极简语法,舍弃了传统类继承,以强悍的并发能力(Goroutine/Channel)和极高的运行效率见长,适合微服务和云原生架构。
  • JavaScript:一处编写到处运行,对象和函数高度灵活,以事件循环和异步非阻塞为核心,全栈(Node.js + 前端)开发的霸主。