学习Node.js真的js接口有必要填写吗么

博客分类:
首先声明,原本 JS
是没有模块库机制(module)的,这必然为创建实质项目带来很大的麻烦,毕竟,我们的目标,还是要创造一个系统的、科学现代的、规范合理的控制各逻辑
代码的边界,否则如果是一种落后的管理机制那就是一块明显的短板。nodejs 本身没有发明一种“新的”模块管理方式,而是直接继承自志愿者组织的
CommonJS 规范作为模块管理的规范,所以开发 nodejs 的开发者都应遵守该规范。Nodejs 本身就有几个已编译到进程之中的模块。
要使用这些定义好的模块,一般的做法是调用 require(包名称的字符串),分到某个局部变量。我们经常使用的sys.puts(“foo
bar”)方法其定义在“sys的命名空间”,sys 即是 nodejs
内建自带的一个系统包。虽然如此,如果我们要使用到sys.puts方法,还必须在 js 源码顶部声明引用 sys
var sys = require('sys');
sys.puts(“Hello world!”); // 也可以这样用: require('sys').print(“Hello world!”);
var md = require(__dirname + '/ext/markdown'); // __dirname 为全局变量,表示所在的目录
引用后,可以在当前上下文语句中访问 sys 之内的任何公开成员(对象、方法或属性)。当然 sys 提供 sys.puts()/*
带换行的conslose输出*/、sys.log(); /* 不带换行的conslose输出
*/、sys.debug();/*与打点debugger类似*/、sys.inspect ();/*列出对象信息*//
这几个方法(上一期的学习调试时也曾有讲)。控制台输出对象 conslose 离不开全局对象
process.stdout。process.stdout 是 nodejs
内置对象,不需要用加载包来获取,那是一个经由V8暴露出来的底层对象。进入核心源码 node.js
发现,调用代码语句process.binding('stdio') 还不能直接使用,nodejs 在 js
多一层封装。通过下面的两种内部用法可以更真实地接触控制台的输出:
process.stdout.write(sys.inspect(object) + '/n');
process.binding('stdio').writeError(x + "/n");
与process.stdout (在第611行起)相对应的就是 process.stdoin,定义 I/O 流一出一进,在代码634行。
引入自己写的包
CommonJS 所规范的 require 方法使用相当简单(到底让人感觉多少有 java import 语句/ C# using
语句的影子),需要哪个就引入哪个包,某个包之中也可以在引用别的包。要加载一个自定义的包一点也不困难。假定一个自定义包用于计算圆周和面积,如下
circle.js:
var PI = 3.14;
exports.area = function (r) {
return PI * r *
exports.circumference = function (r) {
return 2 * PI *
如何引用这个包?将 circle.js 放置在与 node.exe 同一个目录下,就可以用 require('./circle') 在你的代码引入这个包。执行 nodejs 程序如下:
var circle = require('./circle');
console.log( 'The area of a circle of radius 4 is '+ circle.area(4));
注意此时前缀“./”不能省略,否则被视为内置的 nodejs 包。下面则是一个多层目录的例子。
var circle = require('./edk/base/core');
如果位于一个目录,要加入 js 文件的数量很多可怎么办,能不能以整个目录下的所有文件去批量加入?是的,nodejs
考虑这点并支持的。为此,nodejs 利用一个全局数组require.paths
维护着搜索源文件的路径,使得用户可以加入新的目录。因为这是一个标准的JavaScript数组,所以用操控 JS 数组的方法即可。
require.paths.unshift('/usr/local/node');
console.log(require.paths); // /usr/local/node,/Users/mjr/.node_libraries
动态运行JavaScript代码
通过前面的介绍,我们已经知道,利用 require() 可以加载磁盘的任意一份 Javascript 源文件到 nodejs 运行时 Runtime 之中。甚至,连下面的方式也是允许的:
// 精简加载
if(miniCoreOnly){
require('./edk/server/core');
}else if(loadFull){
// 完全加载
require('./edk/server/full');
尽管这样子——无论实际上还是理论上都被支持,但是过头了,显得怪怪的,是不是太 hack 了啊? 呵呵,既然那样子不好,就应该使用更正规的做法,我们要写规范的代码!
若希望动态执行 JavaScript 代码,按需加载,nodejs
可让我们选择的途径有几种,prcess进程对象身上的complie()便是其中的一种。process.compile(code,
filename) 和我们熟知 JS 的动态执行的 eval() 函数非常相似,相似的就是动态执行 JS 代码。虽然动态语言中 eval
的“恶名”昭著,其中成份有好的也有不好的,但不管怎么样 nodejs 还是考虑到了,而且进而提供比 eval 更强大的封装。回到
process.compile() 与 eval
的比较上,两者也有着明显的不同,区别在于 eval()
是在当前作用域运行的,与当前作用域有关,你可修改涉及当前作用域上的一切成员,拥有这种的权限,eval()
的代码和当前的代码都是“透明的”,但是——process.compile() 执行的时候,就对当前作用域是隔绝的,非透明的,换句话说,在
process.compile() 运行的代码它是对当前作用域的看不见的(cannot see the local
scope)。我们接着可以通过下面的这个给出例子看到它们之间的区别哪里:
// 文档中对比eval()和process.compile()的例子
var localVar = 123, compiled,
compiled = process.compile('localVar = 1;', 'myfile.js');
console.log('localVar: ' + localVar + ', compiled: ' + compiled);
evaled = eval('localVar = 1;');
// localVar: 123, compiled: 1 // localVar: 1, evaled: 1
console.log('localVar: ' + localVar + ', evaled: ' + evaled);
由于 process.compile() 没有访问本地的作用域空间,所以 localVar 没有发生变化,若执行 eval()
的时候可访问本身上下文的作用域空间,故所以使得 lcoalVar 改变了。如果process.compile
中的运行代码错误,process.compile 则退出当前 node 结束了该独立过程,不影响本地代码运行。
怎么去理解这个本地作用域(local
scope)呢?要理解“本地作用域”这点概念,最好引入一个相对于“本地”的这么一个“异地”的参照物,换句话说,相当于在新加入代码“外层”加多一层
壳 function(){...},产生一个“异地”的 scope,scope chain 不就被改了嘛。最后这个
function(){...} 返回后就是 process.compile 返回的结果。
但是必须强调一点,还是在同一个全局空间中,所以,上面的测试代码,实际是 Script.runInThisContext('localVar
= 1;', 'myfile.js'); 建立了全局变量 localVar 影响了本地的 var localVar 才会改变 var
localVar 为1,覆盖掉了。
另外的一个参数 filename,它是可选参数,用于输出异常或者错误的信息。
前面说过,process.* 很多的方法都源自于 node 对 v8 的扩展,简单说仅仅调用 C++方法而已;按 C++
源码迹象亦表明是调用了 V8 原生的方法。摘录 process.complie() 其C++ 的源码如下(位于 lib/node.cc):
Handle&Value& Compile(const Arguments& args) {
if (args.Length() & 2) {
return ThrowException(Exception::TypeError( String::New("needs two arguments.")));
Local&String& source = args[0]-&ToString();
Local&String& filename = args[1]-&ToString();
TryCatch try_
Local&v8::Script& script = v8::Script::Compile(source, filename);
if (try_catch.HasCaught()) {
// Hack because I can't get a proper stacktrace on SyntaxError ReportException(try_catch, true); exit(1); } Local&Value& result = script-&Run();
if (try_catch.HasCaught()) {
ReportException(try_catch, false); exit(1);
return scope.Close(result);
特殊的Script对象
Script “脚本”这一特定的词汇居然在 Nodejs 中称作是“类”的一种,那么这个 Script 类将有什么作用呢?用途还是在于运行
JavaScript 代码。我们可以使用 binding('evals') 绑定evals 模块中 Script,首先用 var Script
= process.binding('evals').S 把它的类引用调出来。
首先是 Script.runInThisContext(code, [filename]) 方法,参数 code 是要执行的
JavaScript 实际代码,filename 是可选参数,用于输出异常或者错误的信息。从文档给出的代码看,与
process.compile() 的结果一样,依旧不会访问本地作用域,然全局空间仍开放着的——之所以叫做 “runInThisContext”
还是基本符合原意的。
下面是它们对比的例子:
var localVar = 123, usingscript, evaled, Script = process.binding('evals').S
usingscript = Script.runInThisContext('localVar = 1;', 'myfile.js');
onsole.log('localVar: ' + localVar + ', usingscript: ' + usingscript);
evaled = eval('localVar = 1;');
// localVar: 123, usingscript: 1 // localVar: 1, evaled: 1
console.log('localVar: ' + localVar + ', evaled: ' + evaled);
但是比起 process.complie() 或
Script.runInThisContext(),Script.runInNewContext()
就严格多了,引入代码与正在运行的代码之间则不能通过全局空间来相会影响,不开放全局空间。因为 Script.runInNewContext()
会生成一个全新的 JS
运行空间,与本地代码完全隔绝。这种隔绝的程度,几乎可以用沙箱(sandbox)描述之。沙箱本来是安全方面话题的概念,这不,在 nodejs
中使用 Script.runInThisContext()
的话,代码之间的安全性是最高的。人为的限制一下还是有必要的。
不过,是不是两种代码之间就不能相互访问,老死不相往来呢?也没那么绝对的,不过是绕一点,折中去做。让程序员或有企图的代码不能轻易跨越这种障
碍。Script.runInThisContext(code, sandboxObj) 可通过其第二个参数
sandboxObj,这是一个对象类型的参数。对于动态代码而言,这个 New Context 就是动态代码全局对象。我们可以透过
sandboxObj 就可达到两个运行空间之间交互数据之目的。
var sys = require('sys'),
Script = process.binding('evals').Script, scriptObj, i,
sandbox = { animal: 'cat', count: 2 };
scriptObj = new Script( 'count += 1; name = "kitty"', 'myfile.js');
for (i = 0; i & 10 ; i += 1) {
scriptObj.runInNewContext(sandbox);
console.log(sys.inspect(sandbox)); // { animal: 'cat', count: 12, name: 'kitty' }
顺便说说,像 sandbox 的东东记得在 Adobe AJAX AIR里也有过,也借助某个对象互通往来——总让人感觉有点异曲同工!
Sciript 对象本小节最后要说说的是Script类本身了。实例化 Script 可得到也是叫做 runInThisContext() 和
runInNewContext() 一模一样的方法,但由于是实例方法(注意是小写的
script.runInThisContext(),按文档上的提示……),必须实例化 Script
之后才能使用,所以其定位也是有所区别。区别在于不是立刻执行 JS 代码,runInThisContext()
和runInNewContext() 调用的时候才真正调用 JS,也就是分开来加载
JS 的运行 JS 两个步骤,至于其他什么 Context 的不同就跟前面的一样了,如出一撤,只是在于是否延时的不同,参照一下前面的大家
reuse 思维即可,补充一句的就是 nodejs 用实例方法和静态方法之间的分野来区分是否延时执行的标识,大家可以看看是不是。
module 机制如何工作?
结束本天的学习之前,有必要再说说 nodeje 包是怎么的一个种“包”。module 的功能代码从 node.js
的第65行开始起。实际上 nodejs 会为 require 中所有的代码设置“顶层”的上下文空间。打开node.js源码观察,只要环境变量
NODE_MODULE_CONTEXTS=1 便可以初始化顶层上下文空间(contextLoad = true)。这样的结果,导致定义包中的
var成员即为私有成员,而通过 exports 或 this 对象身上绑定的就是暴露公共成员。如上例 circle,用户通过在
exports 上定义了两个方法 area() 和
circumference(),允许外界可以访问这两个方法。一般静态方式加入源码大概是这样,应该还有上述的 Script
动态加载方式。不管哪一种方式,在运行时 Runtime 执行阶段,全体*.js会透过Script.runInThisContext() 或
process.compile(code, filename) 进入到V8解析器(第421行开始),原理上大致如此。
有一个影响性能的问题,就是多次声明的包引用会不会产生重复加载?——应该不会。因为 nodejs
有缓存的机制,遇到重复的包不需要重新加载,使用缓存中的即可。JavaScript 中使用所谓的缓存 cache
说开了是非常简单、非常朴素、一贯以之的概念,相当于一个 hash 结构,key/value 命中匹配的 target。好像 nodejs 中的
var internalModuleCache = {} 与 var extensionCache = {} 分别代表着内部模块的 cache
和外部模板的
cache。这两对象一般都不会被GC回收。
通盘考虑模块的机制比较复杂,但的确其占了 node.js 很大的一个篇幅。如果对 require() 原理有兴趣的朋友可以看看 src/node.js 源码Module部分(最主要看 module 类的 _compile() 部分,第390行)。
浏览: 51264 次
来自: 成都
请问我获取到类后想要创建对象, 报错java.lang.Cla ...
楼主import javacommon.util.String ...
依赖的包有吗
加入程序中执行没有效果啊,表名还是没有修改
呵呵,虽然放弃了这种很费力也没多大用处的方法,但还是很感谢博主 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
点击阅读原文
当我们学习 Node.js 时,我们在学习什么?
日 发布,来源:
本文来自作者:死月 在 GitChat 上精彩分享。「阅读原文」看看大家与作者交流了哪些问题
在写文章的最开头,我想引用一段我已经引用烂了的一段前同事的话,也是对于 Node.js 圈中近几年开始浮躁的风气的一种质疑。
大家都说学 Node.js 学 Node.js,到底是学它的什么呢?是学 JavaScript 这门语言,还是学 Node.js 的 API?还是学 Node.js 各种三方库?
正如问题所说,Node.js 其实就三块内容,一块是 JavaScript,也就是老生常谈的 ECMAScript;
另一部分是也就是它的 API;最后一部分也就是最不需要学的,也就是生态圈中的各种库。
学 Node.js 的人大抵是两个方向过来的,一是前端同学,二是后端同学。
对于前端同学来学 Node.js 来说,对于他们最大的优势是 Node.js 使用了 ECMAScript,与他们的老本行一样一样的;
而对于后端同学来说,优势在于他们有后端的一整套体系思想,而对于 Node.js 的 API 看起来会更加好理解。
ECMAScript
ECMAScript 是 Node.js 的根基,也就是大家所熟知的 JavaScript。目前市面上使用地最多的其实还是 ES5,即使是前端同学在写 ES6,在经打包之后,通常编译成的还是 ES5 的代码——出于兼容考虑。
而对于 Node.js 来说,基本上的 ES6 语法都已支持,但是还遗留了一些小坑,如 let 的效率实际上没有 var 高等。
举个例子,在 Node.js 的一些源码中,很多的提交都是将 var 批量替换成
const,而少见批量将 var 替换成 let,比如这里。不过由于新版的 V8 中开启了 TurboFan,这效率就不一定了。
不过,哪怕是前端小伙伴们,也不一定能把 ECMAScript 给啃全咯。除了书中自有黄金屋之外,我在这里更推荐的是学习如何看 ECMAScript 规范。
ECMAScript 要入门,很简单,无非是 C-Style。稍微精髓点的就是原型与原型链那一套了。
不过现在 ES6 出来,大家习惯了 class 之后,兴许就把原型链给淡忘了。希望大家还是不忘本,这东西还是得拾起来。
但是一到比较偏僻的地方,就需要网上提问了。比如这些问题:
为什么 1 + undefined 结果是 NaN,而 1 + null 结果却是 1?( https://www.zhihu.com/question//answer/ )
关于两个 {} 的比较(如 ==、!=、&=、&= 等)结果的真假值问题。( https://www.zhihu.com/question//answer/ )
就结果来看,很多已经理解的人就不必吱声了,大多其他小伙伴要么是靠死记结果,要么就是上百度、知乎等地儿搜,搜出来还大多是你抄我我抄你的答案,剩下的小缕同学就干脆不知道结果了。
其实去 StackOverflow 等地查效果应该会更好,然而答案最直接的地方还数 ECMAScript 规范(https://www.ecma-international.org/)了。
其实在一看到这两个问题的时候,我也是不知道原因的,也信不过网上的大多答案解释。然后就上 ECMAScript 规范去追根溯源了。
首先第一个问题,它就是关于操作符 + 的一些定义,我们稍微检索一下,就能找到它的出处在 ES5 的(再早的版本我们就不追溯了)11.6.1 节(https://www.ecma-international.org/ecma-262/5.1/#sec-11.6.1)中。
这一节的内容讲述的就是加号操作符的计算方法。将两个值先执行一个 ToPrimitive 操作,而对于这个操作,ECMAScript 规范中也原原本本给出了一张对照表。
1、undefined 和 null 三个值经过 ToPrimitive 操作后一点都没变;接下去判断左右值是否有字符串,若没有,则将两个值再进行 ToNumber 操作进行相加。然后对于 ToNumber 操作,规范也原原本本给出了一张对照表。
从这张对照表看出来,undefined 的结果是 NaN 而 null 的结果是 +0,自然两个相加结果一个是 NaN 一个是 1 了。
第二个问题的结果也是类似,只要顺藤摸瓜,自然能在规范中找到相应的答案。其实什么原因不原因的,最后追溯到结果都是人(也就是委员会)制定出来的结果。
这里略微提醒一下,等值操作和大小比较操作分别在 ES5 的 11.9.3 节(https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3)和 11.8.5 节(https://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5)中。
所以本节中,我们说的就是要学好 JavaScript,在初步入门之后,借助 ECMAScript 规范加深自己对其的理解。
Node.js API
讲完 ECMAScript,我们接下来再要讲讲的就是 Node.js 内置的 API 了。大家都知道,Node.js 就是靠着 V8 来作为 JavaScript 的运行引擎,libuv 作为事件循环的框架,然后上面像胶水一样黏上一堆易用的 API 就成了。
所以这些 API 就是大家入门 Node.js 的时候最先遇到的必修课了。
相信很多人跟我一样,第一次接触 Node.js 的时候都是这样的几行代码:
一开始学的人可能会觉得很神奇,其实现在看来无非就是 Node.js 内置的 http 模块中的一些 API,如 http.createServer(callback) 等。
要说这些 API 去哪学,其实用不着看什么网上的教程啊什么东西,所有的这些原原本本都在 Node.js 的文档之内。
如 6.x 的 Node.js 文档就可以看(https://nodejs.org/dist/latest-v6.x/docs/api/),里面无非三十几个模块,而且内容都十分简单且实用。
例如 http 和 https 模块就是我们最常用的用于创建我们 HTTP(S) 服务器以及用于 HTTP(S) 客户端请求的模块,虽然现在大家用于线上开发大多都用了类似 Express、Koa 等框架或者 Request 这类库,但是实际上他们的最底层用的还是 Node.js 的这些模块这些 API。
API 没多少要学的,就算真的从头到尾看一遍,甚至也花不了一天(前提是对其中的各种前置知识了解)。大不了在一开始学的时候先过一遍,拣常见的学熟络了,然后在日常开发中再积攒肌肉记忆,就差不多了。
为什么这么说呢,因为哪怕是你真的死记硬背背下来了,下个版本说不定得变。举个例子来说,dns.setServer() 这个函数,自 Node.js 8.2.0 起,就支持了自定义 DNS 服务器端口,如:
dns.setServers([ "103.238.225.181:666" ]);
这个函数的新特性自 Issue#7903 (https://github.com/nodejs/node/issues/7903)提出并在 PR#13723 (https://github.com/nodejs/node/pull/13723)中被实现并且在 8.2.0 版本中正式发布。
除此之外,在 Node.js 的弃置 API 中也列了一堆在各种版本中被弃用的 API,就我以前用最多的就是 fs.exists() 函数,但是在 1.0.0 中就被标记为弃用了,只是目前来说还没有下架而已——指不定哪天就下架了呢。
这些被弃用的 API 也在 Node.js 的文档(https://nodejs.org/dist/latest-v8.x/docs/api/deprecations.html)中被列了出来。
总之在 Node.js 的快速迭代中,各种事情都是有可能的。在维护者和贡献者的提交中,分为 Patch 提交、SEMVER-MINOR 和 SEMVER-MAJOR 三类提交,其中 SEMVER-MINOR 是中间的版本号更新,即非 Break 但是有功能改动、更新的提交。
而 SEMVER-MAJOR 指的是那类 SEMVER-MAJOR 提交,如下架 API 就是一类 SEMVER-MAJOR 的提交。
由此可见,学 Node.js 的 API 时,我们大多只需要记住并熟练使用常用的一些即可,其余的就仰仗实时翻阅 Node.js 文档,以及关注 Node.js 新版本发布时候的 Changelog,尤其是那些 Notable changes(https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V8.md#notable-changes-1)。
再下一步就可以有事没事去看看 Node.js 源码仓库中的 PR 了,参与讨论讨论也是不错的选择,还能参与到 Node.js 的贡献当中来。
Node.js 生态圈
个人认为,这是 Node.js 中最不需要学的一块内容了。
怎么说呢,Node.js 的生态圈非常庞大,其生态圈中的库质量参差不齐。有有着很大用户群的 Express、Koa 等,也有着闹着玩的 five 这类库。
无论是什么,总之根源还是在于检索加上看文档以及造轮子。
首先第一步检索,无论是谷歌、NPM 自带的搜索引擎,还是自己脑海里的人肉搜索库,都是检索的根基所在。
例如我们如果要写一个 Web 应用,首先就是想到要一个 Web 框架,然后脑子里应该首先冒出 Express、Koa 等既是框架又算不上框架的 Web 库,或者再小众点就是 hapi、egg 等框架;
如果觉得自己想另辟蹊径,找找看有没有什么更小众能体现自己逼格的东西,就可以上搜索引擎搜了,比如 Node.js Web Framework 等关键字;
最后,如果觉得还不过瘾,那就自己造哇,比如我们团队就用的是自己造的 Akyuu.js 框架(https://github.com/akyuujs/akyuu 框架还在沉淀中,文档还未来得及写)。
再比如,我们在写 Web 应用的时候,要用 md5 给密码进行简易加密。其中一个办法是使用 Node.js 的 crypto 模块,但是写起来也略冗长,还有个办法就是去 NPM 上搜搜看有什么好的包可以用咯。
输入 md5 关键字,我们能看到好多可以用的包。可以拣一两个看着顺眼的用,如:
blueimp-md5
apache-md5
至于用嘛,到自己拣好的包里面看看文档就好用了。所以这就是我所说的,Node.js 生态圈中的包,看文档就够了。
大不了看官方出的教程,例如 Express 的官网(http://expressjs.com/)。看这些东西要远好于自己去百度搜索抄来抄去的教程。
还有一类库,是通过其它的库被包装而成的,那在看文档之前,可能要先去了解一下原库的一些内容。
举个例子来说,gm 这个包是 Node.js 中用于图像处理的包,其原理是通过执行 GraphicsMagick 命令的形式加上管道来进行处理的。
所以要用 gm 的话,还需要事先去了解 GraphicsMagick 的一些用法,尤其是一些参数等等。
还有就是像 tensorflow2 这种包,相信大家都想到了,用它之前还得先去了解 TensorFlow。
最后就是,当大家找不到想要的包的时候,就轮到自己造轮子,反哺 Node.js 生态圈了。
比如我就曾经实现了一套 Node.js 中使用的 ORM 库,唤作 Toshihiko,与市面上常见的 ORM 如 Sequelize 等大相径庭,主张简约和性能,并目前在我厂广泛应用。
其它更多的我参与或者我发起的一些 Node.js 生态圈的贡献,可以查看我的 me 仓库。
所以本节中我们所说的,学习 Node.js 生态圈,无非就是学习如何检索生态圈的内容、好好过滤并去其糟普取其精华,然后还是很重要一点就是学习如何查看文档,在有前置知识的库面前要先学习它的前置知识。
包与模块机制
Node.js 中的包与模块机制都是借鉴了 CommonJS 的模块规范和包规范。关于这一块我曾经开了一场知乎 Live 详细讲解——足足讲了三四个小时还是虎头蛇尾。
这么长的一块内容还是值得一学的。如果大家偷个懒,也可以看看我的知乎 Live——《深入了解 Node.js 包与模块机制》(https://www.zhihu.com/lives/667136)。
很多从后端转过来学 Node.js 的同学中,一开始入门最大的障碍就是它的异步非常令人烦恼,尤其是回调地狱。
就连我当年一开始学习 Node.js 的时候,也非常不适应。在什么也不懂的时候硬生生搞了个自己习惯的框架,把里面的异步操作通过 fibers 硬生生转成了同步的写法。现在回过头来想想,还是太年轻了。
习惯 Node.js 中的异步,个人认为是非常有必要的。
回调函数是 Node.js 中的基础所在,大量的内置 API 都是通过回调函数来触发事件返回结果的,如 fs、http 中的一堆堆函数,以及目前市面上大部分的三方库。
不过这货最让大家烦恼的就是传说中的回调地狱了。
但是我个人认为这都不是事儿,通过 async 这个包就能比较大程度地化解了,而且它还有非常多地流程控制的函数,形如 eachLimit、waterfall、parallel、auto 等等,在 async 中就能非常简单地实现了一堆的流程。
可以说 Callback 正是 Node.js 已至 JavaScript 的精髓所在,连响马大大都在微博中挺 Callback 了。
callback 虽然慢,但是并不太慢。而为了解决 callback hell 引入的流程框架,一个更比一个慢。导致使用这些框架的应用,性能直线下滑 50-100 倍。【没错,说的就是 async】 (指的是 async 语法)
把 callback 玩到这么精致,也就是 JavaScript 了。
以及在交流过程中说的话:
如果没得选(指 fibjs),肯定是用 callback。
这玩意儿,主要就是要熟练,和思维转换。当大家熟悉了 Node.js 中事件循环的原理后,再回过头来看 Callback,其实也没那么难了。
当再配合上 async 这个包,照样能写出非常优雅的代码。
Promise 与 yield
Promise 是另一套异步之旅中的概念。相信当下入门的 Noder 们应该会比较了解 Promise。而且当下的 Node.js 中已经加入了内置的 Promise 支持。
而且实际上在学习 Node.js 中,Promise 相较于 Callback 来说是更易学、易于掌控的,这也是为什么 Promise 更被大众所接受。
而 yield 来说,这个概念对于很多其它语言转过来的后端同学不会陌生,如 python 就有这个概念。它主要配合菊花函数(即生成器函数)使用。
而且 Koa 框架中的异步大多使用 yield 来完成。
实际上这一块来说,仍然是前面章节讲到的 ECMAScript 的一块子内容,所以还是归于要好好学习 ECMAScript。
async 与 await
这是 Node.js 8 中推出的另一套异步解决方案。在先前的 7 版本中,其实已经加入了该方案的支持,只不过没加入默认支持而已。
只不过目前而言,实际上 async 与 await 仍然在 ECMAScript 的草案(https://tc39.github.io/ecmascript-asyncawait/)之中,Node.js 中提前把它弄起来了。
如果是 C# 转过来开发的童鞋应该对该函数不陌生。话说回来,实际上这一块也算是 ECMAScript 学习的子内容。
当大家学会了 Node.js 中《表象之学》一小章的内容之后,可以说是入门了 Node.js 了,这个时候基本上对于平常的 Node.js 开发已经游刃有余了。甚至可以就着网上的教程,开始各种 Web 应用的开发了。
不过本文不是教大家如何看教程的,而是别的内容。
表象之学的内容通常有点编程基础的人来说都易学,前端同学在语言上会更占便宜一些。如果前端的同学们想开始企业级的线上应用,下面的内容才是应该深入学习的。
虽然看起来下面的内容与 Node.js 无甚关系,但是作为一个合格的开发来说,更深入的一个层次,就是网上流传甚广的一句话了。
忘记语言。(虽然 PHP 是最好的语言 ?|???|?*~●)
如果仅把 Node.js 当做玩具来看待,随便三下五除二学一下数据库就可以了。
我曾经接手过一个 Node.js 的外包项目二期的开发,非常痛苦。数据库完全是乱设计,看起来更像是被各种劣质教程害了的那种。
所以我在这里比较真诚地给大家推荐一下,如果作为 Node.js 的后端开发来说,数据库是必备的前置知识。
对于学习新兴的 NoSQL 来说,还是推荐顺带扎扎 SQL 的相关底子,如 MySQL。
记得大学的时候我们用的数据库书是自己本校老师写的,里面各种原理还是受益匪浅的,虽然说现在很多地方都忘得差不多了,但是学过之后脑子里有个印象,下次提起的时候就又会恍然大悟了。
例如 SQL 的检索语句的语法树、索引相关内容等等。
以及数据量到了一定程度的时候,分表、分库、读写分离等等内容。虽然很多都是 DBA 给的建议,但是也有很多公司是并不具 DBA 岗位的,很多时候都是要靠自己。
不过在线上各种应用当中,大多的 Node.js 程序都是使用了 ORM 来搞定,所以很多时候用不着大家亲自写 SQL 语句,而是直接使用 ORM 里面的各种函数。
本节说的数据库学习中,更多还是需要学习如何更好地设计数据库,弄更好的各种解决方案等等。
在大家扎好了 SQL 的底子之后,也可以开始尝试着各种 NoSQL 了。目前比较火的就是 MongoDB,并且某种意义上,Redis 这类也是属于 NoSQL。
后端思想以及代码抽象
要学习 Node.js,若想做后端开发的话,后端思想以及代码抽象是非常重要的。
关于后端思想,其实是个很虚很悬的东西。它的整个学习体系中包括但不局限于这些内容:
算法与数据结构
其中数据库在前面一节中我们就提到了,是一个非常重要的后端组成部分。安全、网络编程和算法与数据结构其实在前端中也可以有体现,但是占比还是比较少,甚至很多前端同学也还没思考过这些问题。
而对于要学习 Node.js 的童鞋来说,还是推荐学习这些关于后端的思维。
做接口、方案、编码的时候,首先要考虑到设计是否安全,其次是性能以及服务器压力等。后端是一个应用最后的防线,一定要严把关。
在前端的安全性做得再好,一旦有人绕过前端直接给后端发请求,攻击就直勾勾发到后端去了。
大多的后端转 Node.js 的同学在这一方面比前端同学占便宜,因为思维上大多会惯性地考虑这些点,而前端同学转过来的时候往往会栽在这些地方,设计接口、方案的时候不够「后端化」。
所以在学习 Node.js 的时候,学习后端的思维是非常重要的。
关于后端思维,还有很重要的一点就是代码层面的抽象。举个简单的例子,当前很多前端的框架都是抽象成 MVVM,而后端当下最通用的抽象就是 MVC。
我在前面讲过一句话,说 Express 等既是框架又不是框架,就是因为它们没有一个非常好的层次抽象,一千个人写出来的应用有一千个模样。
这导致了还是我说的之前接手的一个项目,代码根本没有抽象,所有的 SQL 代码都是硬生生写到各种路由逻辑当中。
在一个企业的项目当中,这种抽象是非常必要的,一段解耦的代码会非常好维护,使得接盘侠会特别爽。
其实不只是 MVC 的抽象,抽象还分很多种方式。如很多面向过程的代码,在稍加修饰之后,就能抽象成优雅面向对象的代码(当然,在 FP 中另当别论,不过本文讲的是跟 Node.js 相关的学习,所以我还是比较推崇面向对象的抽象方法)。
关于抽象还有一个不得不提的就是设计模式,这也是一个合格的程序员(Node.js 程序员当然也是程序员中的一类)可能所需要了解的内容。
尤其是其中的适配器模式、工厂模式、单例模式等,在日常中大家也经常会用到。
那么,为什么说算法和数据结构重要呢?可能很多时候大家在平时写代码的时候觉得算法或者数据结构离自己都挺远的。实际上你可能在不知不觉中就用到它了。
Redis 中大家用到的 Set 就是一种常见的数据结构,只不过 Redis 都帮大家给包裹好了,包括查询的操作,最终通过网络给返回结果给大家。
再比如,我在我以前写过一篇文章《我为什么要使用哈希》(https://xcoder.in//why-i-use-hash/)中就介绍了哈希的一些妙用。其中文章中的「报告图问题」和「唯一主键问题」两个问题都是我在实际 Node.js 工作中进行的使用哈希做的一些事情。
这跟后端的思想也都紧密联系在了一起,例如为了节省空间的去冗余,同时由于减少了记录数也提高了查询的效率;又例如由于有时候考虑到不使用自增主键的情况,就可能要通过哈希来生成一个。
这一块应该是 Node.js 乃至所有程序员需要学习的最重要的一块内容了。内容非常多,也不是三言两语能讲完的,就我自己而言也还只是在山脚下往上看,只是希望能给大家指一条路,与诸君共勉。
很多时候,当我们做项目在技术选型,除了说生态圈中哪个技术更成熟以外,性能优劣以外,还有一个考虑的重点是,实施项目开发者的趁手兵器是什么。
论 Web 开发,Java 无疑是更成熟的方案,但是我就是不会 Java,我就是会 Node.js,那如果项目让我来做,我肯定会选 Node.js——除非有方面遇到了瓶颈。
那么其它领域也一样,作为一个能在本地跑的运行时(而不是像前端 JavaScript 一样只能寄宿在浏览器当中),理论上 Node.js 可以做其它同类语言都能做的事。如科学计算、人工智能等等,无非是领域的生态圈强不强。
比如还是我们先前提到的 tensorflow2 这个包,就让 Node.js 在深度学习的领域中有了可能性。而对于硬件开发,同样的,我们能用树莓派中装上一个 Node.js 来驱动我们用该派组装的一部小车。
在前端领域中,实际上 Node.js 做的事情非常有限,毕竟它不能在浏览器中跑——它主要做的事情是为前端各种生态圈提供命令行工具,以及可以使用类似 nw.js 之类的东西来写桌面 GUI 应用。
说前端生态圈的命令行工具,实际上用什么语言写也都一样,只不过还是我的那句话,前端生态的开发者对 JavaScript 更熟悉,用 Node.js 更为合适,要知道,在很早之前 JavaScript 代码压缩等活也都是通过其它语言写的脚本或者工具实现的,如 YUICompressor。
所以对于 Node.js 在其它领域中的应用,我的推荐是大家先去了解该领域的各种基础知识,语言只是工具,换了谁都一样,包括后端。
最终大家用的时候看的主要还是熟练度,也就是开发成本和开发效率——毕竟除非是真的需要压价性能到极致的时候,通常性能不会成为我们选型的瓶颈。
就比如我们在做网关开发的时候(这里有我上次分享的一个 Slide),反向代理层之间上了非常成熟的 OpenResty,而与之交互的请求解析服务,我们毫不犹豫地选择了我们最熟悉的 Node.js,如果这个项目交给我们公司的 Java 团队来做,我相信他们也会毫不犹豫地选择 Java。
前面讲了各种 Node.js 学习时,我们所需要关注的点。后端思想这一块由于太大,可以贯穿大家的整个程序员生涯(哪怕是只想拿 Node.js 来写写命令行工具,或者 GUI 应用,学习这一块内容也是无害的)。
剩下的其它点应该是不难的,等大家熟悉了这些点、学习得差不多之后,就可以开始剖析 Node.js 的源码了。
网上不乏 Node.js 源码的剖析文章,我这里也介绍一些比较好的剖析方法。由于文章篇幅原因,我这里只介绍方法,不做详细剖析。
首先 V8 是 Node.js 的根本,所以希望大家在剖析之前,能了解 V8 的一些基本内容,使大家在阅读 Node.js 的代码时不至于太累。
这里有一篇《Embedder’s Guide》(https://github.com/v8/v8/wiki/Embedder's-Guide),是关于 V8 的基础,如句柄、句柄作用域等基础概念。
其次是 libuv,研读 libuv 文档会让我们在理解 Node.js 中一些异步代码会更容易一些。
不过这一切都需要我们有一定的 C 以及 C++ 的基础,相信这对我们好奇的程序员们并没有什么特别大的难度。
在有了这两块的基础后,我们有两个方向的阅读方法。
其一,从入口文件开始看,开始理解各种源码是如何整合进入 Node.js 的。C++ 的入口文件就是 main 函数的那个文件,从该文件中追根溯源进去,一步一步剥洋葱一样看,在遇到比较难以理解的地方的时候不一定要完全搞懂,挑关键字和语义来阅读即可——毕竟 Node.js 的函数很大程度上都是语义化的;
JavaScript 的入口文件则是 lib/internal/bootstrap_node.js 文件,这一点其实在剥洋葱阅读 C++ 源码的时候就会发现,在 Node.js 初始化一定阶段的时候会执行这个文件,完成 JavaScript 环境的初始化。
其二,从胶水内置库开始看。当我们遇到一些问题,如 http 某个函数的实现原理的时候,我们就应该直接跑到 lib/http.js 开始阅读,在回溯其依赖的各种函数的时候,按照自己的需求去探索函数栈的深度。
在遇到 process.binding() 函数的时候,要根据其获取的内容,对应到 C++ 代码中的相应文件,如 process.binding('util') 对应的就是:
https://github.com/nodejs/node/blob/v8.2.0/src/node_util.cc#L236 这个文件。
掌握了这两种阅读方式,即使不是太了解 V8 的 API 和各种概念以及 libuv 的各种概念,也能比较轻松地阅读 Node.js 的源码了。
但是如果需要深入阅读理解 Node.js 源码的话,还是推荐大家去了解一下关于 V8 和 libuv 的一些概念。
在阅读代码的时候,大家也可以动手尝试修改一下相应的代码,以验证自己的理解是否正确。
这个时候就可以把 Node.js 源码仓库克隆到自己本地,然后可以着手修改一些内容并在编译之后执行一下,看看自己修改的地方是否如自己预期所示。
克隆编译 Node.js 的步骤还是比较简单的,如果是 *nix 下的用户,可以这么搞:
其中 &核数& 是你的 CPU 核数,即一个数字,如 make -j8。
如果你有其它的疑问或者你是 Windows 下的开发者的话,可以自行阅读 Node.js 的构建文档(https://github.com/nodejs/node/blob/master/BUILDING.md)以获取更多信息。
本文主要给大家介绍了当我们在学习 Node.js 的时候,推荐的正确学习姿势,防止大家误入歧途,光学习了最简单的表象内容,而忽略了学习一门后端语言更深一层次的体系。
其实在「深入学习」一节中,已经脱离了语言的范畴,而且事实上在实际工作中,语言反而是次要的内容,在开发经验中学习、总结、抽象出一套体系才是核心的思想。
最后再理一遍本文推荐大家在学习 Node.js 的时候比较舒适的一条路线吧。
首先走平地入门的时候,ECMAScript 是根基,在停留在表象语法的阶段上,可以更深一层次地去扒一些规范中约定的内容,如各种操作符在内部的流程是怎么样的;
在根基扎实的情况下入门 Node.js 是最好的,然后开始阅读其 API 文档,内容较浅,主要是快速入门如何使用它的基本 API 以供可以快速开发一些东西;
在熟悉 Node.js 内置 API 的同时,要了解它的生态圈,检索和阅读文档的能力是非常必要的,在生态圈无法满足自身的情况下,一种进阶的做法就是自己提供轮子。
在走这一条线路的时候,其它语言转过来的开发者可能会踩到一些异步的坑。
哪怕平时不使用 Callback 而是去使用 Promise 等东西,贯彻 Callback 的思想还是比较有必要的——不要忘本,毕竟这才是 Node.js 的精髓所在,而其它内容都是别的语言中借鉴过来的,很多别的语言转过来的开发者会感到比较亲切。
在入门之后,大家需要关注的点要从 Node.js 语言本身移开,关注后端开发的一整套体系。当然也不是凭空关注的,而是结合 Node.js 自身的特性,摸索一条适合它的体系。
正如「中国特色社会主义理论体系」一般,再怎么中国特色它也是社会主义,大家需要学习的是弄出一套「Node.js 特色后端理论体系」。
但是其根本不变,Node.js 特色再怎么浓厚它还是后端体系,如结合 Node.js 开发的方式,搞出 Express 这类基于中间件的 Web 框架,但是安全、性能、结构分层还是非常有必要的。
还有一个重点就是数据库需要好好学,而不是简简单单地 CURD 会用即可——不然很多公司要 DBA 干嘛?哪怕我们不去抢 DBA 的饭碗,我们也不能对性能优化、表的设计一无所知随手一搞。
而脱开后端领域来说,实际上只要你乐意,Node.js 也可以做任何领域的事情,只不过是是否真的适合和是否趁手而已,摆正心态很重要。
学习用 Node.js 快速出原型也是一个不错的选择,例如深度学习的领域。
万丈高楼平地起,在我们深入学习一整套体系的时候,可以根据自身的能力,开始尝试阅读 Node.js 源码,理解它究竟是个怎么样的东西——其实它就是一个胶水层,V8 和 libuv 胶上一堆易用的 API。
最后,太极生两仪,两仪生四象,四象生八卦,八卦生万物。Node.js 就是万物中的一个,而整个编程界可以比作太极,我们从万物开始学,最后都需要归宗到太极当中去——当我们在学习 Node.js,我们其实就是在学编程,不要把自己局限住了。
当大家阅读完这篇文章后,对我的疑问不是局限于 Node.js 语法如何,异步的时候到底采用什么方案比较好的时候,我想我的文章思想才算是真正传递给各位开发者了。
「阅读原文」查看本场Chat交流实录
好文,学习了?
好文,学习了?
我要该,理由是:
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)

我要回帖

更多关于 js接口有必要填写吗 的文章

 

随机推荐