PHP 转 Node 笔记(一. 谈谈两者的差异)

3,302 阅读5分钟

前言

本文章连载更新,以记录我的学习历程和踩过的坑,比较 NodePHP 在各种情景下的差异,分享一些实用代码

根据我的个人情况,Laravel 使用者是最核心的面向人群,本系列的大部分代码都是在 Node 下去实现传统后端语言或是框架的特性,譬如 Laravel 的 路由/控制器/中间件/依赖注入 等(见 Varal),以及最近准备实现的 LaravelCollectionORM

首先容我声明下:

  • PHP 是世界上最好的语言

这并不是开玩笑,而是在我刚刚接触 Node 时真实的感受

1. 同步与异步

第一个差异也是最重要的一个差异,对于习惯了同步开发的程序员来说最影响效率的差异。 作为 PHP 出身的我刚接触 Node 时,因为搞不懂“为什么 Node 代码里到处都是回调”,“为什么不能用全局函数”纠结了大半天,而这两个问题在搞懂了同步异步的差异后自然就清晰了。

PHP 是同步的(当然现在也能实现异步编程,如 Swoole,并且性能不比 Node 差),一般依赖 Apache 或是 Nginx 作为 Web Server,每个请求都发生在由 php-fpm 管理的独立的 PHP 进程上,请求结束后释放加载的资源,所以无论是全局变量还是全局函数,都只会对当前请求有效

对于 Node 来说,它常驻内存,自身就是 Web Server,所以定义全局变量与函数自然会影响到所有的请求,尤其是同时发生的。

举个例子: 在 Laravel 中,用全局函数 app() 就可以获取到 服务容器,通过它你几乎可以得到关于请求的所有信息。而在 express 中,app 变量必须通过回调的参数传递来获取

2. Js 数据结构

① 类型转换

JsPHP 一样无需声明变量类型以及支持自动转换,在 PHP 中,字符串拼接用 .,数值相加用 +,根据运算符来决定转换结果,清晰明了,但 Js 只能用 +,这导致只要因数中存在字符串,最后的结果就只能是字符串。

解决方案:参考 一元正号

+'123' // 123
1 + (+'123') // 124

② 数组

Js 中的数组并不支持 键值对,这导致在 PHP 中用数组能办到的很多事在 Js 中只能使用对象,虽然 ES6 引入了 Map 数据结构,但构造方法太反人类,存取值的方式也不方便,我认为它在大多数情况下还没有 数组+对象 好用

另外值得一提的是我们通常用 typeof 关键字来获取 Js 的数据类型,但是 数组对象 均返回 object,所以对于数组,一般使用 Array.isArray() 方法来判断

3. 遍历

因为上述的原因通常使用对象来储存 键值对 数据,导致我们经常需要用到适用于对象的遍历语法

foo...in 在每次迭代时,将不同的属性名分配给变量。它不应该用于迭代一个数组,因为迭代的顺序是依赖于执行环境的,所以数组遍历不一定按次序访问元素。它会遍历对象的原型,所以有时需要使用 hasOwnProperty() 来过滤原型属性

for...of 在每次迭代中,将不同属性的值分配给变量,但它需要对象实现了 [Symbol.iterator] 方法,详见 for...of

所以对于简单的用于 键值对 的对象使用 for...in,对于 ArrayMap 等内置对象时使用 for...of 是个比较好的选择

4. 原生辅助方法

拿数组举例,PHP 提供了约 80+ 个与数组操作有关的方法,而 JS 只有约 30+ 个,这导致被 PHP 娇生惯养的我经常会发出感叹:“WTF,连这个函数都要自己写?”当然这有弊也有利,如果你非常注重代码质量,你一定会比较这个函数的多种实现,并选择最优的那个,这虽然会花费不少时间,但也锻炼了你的算法与思考能力

5. 类与继承

ES6 提供了类与继承的语言级实现,但相比 PHP 的类还有以下差别:

  • 不支持接口
  • 不支持私有方法,解决方案: 将私有方法移出类,通过 call 方法来调用
    class Foo {
        foo(baz) {
            bar.call(this, baz);
        }
    }
    function bar(baz) {
        return this.bar = baz;
    }
    let a = new Foo();
    a.foo(1);
    console.log(a.bar); // 1
    
  • 不支持私有属性,目前已有提案,将支持私有属性与私有方法
  • 不支持静态属性,解决方案:
    class Foo {
        //
    }
    Foo.bar = 1;
    console.log(Foo.bar); // 1
    

尽管如此,还是能用的,另外 ES6 也提供了 ProxyReflect 对象,用于拦截与定义基本操作的默认行为,等同于在语言层面做出修改,类似于 PHP 的魔法方法但是更加强大!通过它们可以为原生语言添加一些更加合理的特性

6. 生态环境

PHPComposerJsnpm,当前的趋势,自然是后者更加火爆,说下我的感受吧:

  • 不同与 Composer 所有者/包名 的命名方式,npm 只有包名且唯一,自然有一些“占着茅坑不拉屎”的包
  • 基数多质量自然下降,找到一个靠谱的包花费更多时间,主观上给我一种非常乱的感觉
  • 不能完全理解一个简单的功能甚至是一个函数也能作为一个包并且被依赖数还那么高
  • 不能理解 express 这种轻量级框架安装后依赖文件夹比 Laravel 还大

结语与预告

总的来说,转换开发语言肯定是会有一点不习惯的,现在已经心平气和的在做 Node 的开发,只是想把我当时的真实感受记录下来,言语上多少带点戾气,还请前端童鞋见谅。

下一篇将会介绍我使用 Node 开发 Web Server 的历程,感谢各位大佬的阅读,文中错误欢迎指出,欢迎交流