codecamp

Express/Node introduction

先决条件: 基本的计算机素养。 服务器端网站编程的一般了解,特别是对 网站中的客户端 - 服务器互动
目的: 熟悉Express是什么,它如何适应Node,它提供了什么功能,以及Express应用程序的主要构建块。

什么是Express和Node?

节点(或更正式的 Node.js )是一个开源的跨平台, 运行时环境,允许开发人员在 JavaScript 中创建各种服务器端工具和应用程序。 运行时期望在浏览器上下文之外使用(即直接在计算机或服务器OS上运行)。 因此,环境省略了浏览器特定的JavaScript API,并支持更传统的操作系统API,包括HTTP和文件系统库。

从Web服务器开发的角度来看,Node有很多好处:

  • Great performance! Node has been designed to optimize throughput and scalability in web applications and is a very good match for many common web-development problems (e.g. real-time web applications).
  • Code is written in "plain old JavaScript", which means that less time is spent dealing with "context shift" between languages when you're writing both browser and web server code.
  • JavaScript is a relatively new programming language and benefits from improvements in language design when compared to other traditional web-server languages (e.g. Python, PHP, etc.) Many other new and popular languages compile/convert into JavaScript so you can also use CoffeeScript, ClosureScript, Scala, LiveScript, etc.
  • The node package manager (NPM) provides access to hundreds of thousands of reusable packages. It also has best-in-class dependency resolution and can also be used to automate most of the build toolchain.
  • It is portable, with versions running on Microsoft Windows, OS X, Linux, Solaris, FreeBSD, OpenBSD, WebOS, and NonStop OS. Furthermore, it is well-supported by many web hosting providers, that often provide specific infrastructure and documentation for hosting Node sites.
  • It has a very active third party ecosystem and developer community, with lots of people who are willing to help.

您可以简单地创建一个简单的Web服务器,以便只使用Node HTTP包来响应任何请求,如下所示。 这将创建一个服务器并监听URL http://127.0.0.1:8000/ 上的任何类型的HTTP请求; 当接收到它时,它将发送纯文本响应"Hello World"。

//Load HTTP module
var http = require("http");

//Create HTTP server and listen on port 8000 for requests
http.createServer(function (request, response) {

   // Set the response HTTP header with HTTP status and Content type
   response.writeHead(200, {'Content-Type': 'text/plain'});
   
   // Send the response body "Hello World"
   response.end('Hello World\n');
}).listen(8000);

// Print URL for accessing server
console.log('Server running at http://127.0.0.1:8000/')

Node本身不直接支持其他常见的Web开发任务。 如果要为不同的HTTP动词添加特定的处理(例如 GET POST DELETE 等) 路径("路由"),服务静态文件或使用模板动态创建响应,那么您将需要自己编写代码,或者您可以避免重复发明轮和使用Web框架!

Express 是最受欢迎的 Node Web框架,是许多其他基础库 受欢迎的节点网络框架。 它提供了以下机制:

  • Write handlers for requests with different HTTP verbs at different URL paths (routes).
  • Integrate with "view" rendering engines in order to generate responses by inserting data into templates.
  • Set common web application settings like the port to use for connecting, and the location of templates that are used for rendering the response.
  • Add additional request processing "middleware" at any point within the request handling pipeline.

虽然 Express 本身是相当简约的,但开发人员已经创建了兼容的中间件包来解决几乎任何Web开发问题。 有些库可以使用Cookie,会话,用户登录,URL参数, POST 数据,安全标题和更多 您可以在 Express中间件(以及)中找到由Express小组维护的中间件包列表 一些流行的第三方包的列表)。

注意:这种灵活性是一把双刃剑。 有中间件包来解决几乎任何问题或需求,但是制定正确的包使用有时可能是一个挑战。 还没有"正确的方法"来构建应用程序,并且您可能在Internet上发现的许多示例不是最佳的,或者只显示了开发Web应用程序所需要做的一小部分。

它从哪里来的?

节点最初是在2009年发布的,仅适用于Linux。NPM软件包管理器于2010年发布,本地Windows支持在2012年添加。当前版本是节点4。 如果您想了解更多信息,请深入了解维基百科)。

Express最初于2010年11月发布,目前使用的是API的第4版。 您可以查看更改日志,了解有关当前版本更改的信息, "external"href ="https://github.com/expressjs/express/blob/master/History.md"> GitHub 以获取更详细的历史发行说明。

Web框架的普及是很重要的,因为它是一个指示符,它是否将继续被维护,以及在文档,附加库和技术支持方面可能有哪些资源可用。

对于服务器端框架的流行度,目前还没有任何现成和确定的衡量标准(虽然 Hot Frameworks 等网站 使用诸如计算GitHub项目数量和每个平台的StackOverflow问题的机制来评估流行度)。 一个更好的问题是Node和Express是否"足够流行",以避免不受欢迎的平台的问题。 它们是否继续演变? 如果你需要它,你能得到帮助吗? 如果你学习Express,你有机会获得有报酬的工作吗?

根据使用Express的知名公司的数量, 人们对代码库的贡献,以及提供免费和付费支持的人数,那么是的,Express 是一个流行的框架!

是Express意见?

Web框架通常将自己称为"意见"或"不庸俗"。

意见框架是那些对"正确的方式"处理任何特定任务有意见的框架。 他们经常支持特定领域的快速发展(解决特定类型的问题),因为正确的做任何事情的方式通常是很好理解的和有据可查的。 然而,他们在解决其主域以外的问题时可能不那么灵活,并且倾向于为他们可以使用的组件和方法提供较少的选择。

相比之下,未分离的框架对于将组件粘合在一起以实现目标的最佳方式,或者甚至应该使用什么组件的限制要少得多。 它们使开发人员更容易使用最合适的工具来完成特定的任务,尽管需要自己找到这些组件的成本。

Express是未经宣传的。 你几乎可以将任何兼容的中间件插入请求处理链,几乎任何你喜欢的顺序。 您可以在一个文件或多个文件中构建应用程序,并使用任何目录结构。 你有时可能会觉得你有太多的选择!

Express代码是什么样子?

在传统的数据驱动网站中,web应用程序等待来自web浏览器(或其他客户端)的HTTP请求。 当接收到请求时,应用程序基于包含在 POST 数据或 GET 数据中的URL模式和可能的相关信息来确定需要什么动作。 根据需要,它然后可以从数据库读取或写入信息或执行满足请求所需的其他任务。 应用程序然后将向web浏览器返回响应,通常动态地创建HTML页面以供浏览器通过将检索到的数据插入到HTML模板中的占位符来显示。

Express提供了指定为特定HTTP动词( GET POST SET 等)和URL模式 路由"),以及指定使用哪个模板("视图")引擎,模板文件所在的位置以及用于呈现响应的模板的方法。 您可以使用Express中间件添加对Cookie,会话和用户的支持,获取 POST / GET 参数等。您可以使用Node支持的任何数据库机制 不定义任何数据库相关行为)。

以下部分解释了使用Express 和节点代码时会遇到的一些常见问题。

Helloworld快递

首先,让我们考虑标准Express Hello World 示例(我们将在下面讨论此部分的每个部分 ,并在以下部分)。

提示:如果您已安装了Node和Express(或如果您按照下一篇文章中所示安装它们),则可以 将此代码保存在名为 app.js 的文件中,并通过调用 node app.js 在命令提示符处运行它。

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

前两行 require()(import)express模块并创建一个 ">快速应用。 这个传统上被命名为 app 的对象具有路由HTTP请求,配置中间件,渲染HTML视图,注册模板引擎和修改 /expressjs.com/en/4x/api.html#app.settings.table\">应用程序设置,用于控制应用程序的行为(例如,环境模式,路由定义是否区分大小写等)

代码的中间部分(以 app.get 开头的三行)显示了路由定义 app.get()方法指定一个回调函数,只要有一个路径(\'/\' )相对于站点根。 回调函数将请求和响应对象作为参数,只需调用 send() ,返回字符串"Hello World!"。

最后一个块启动端口\'3000\'上的服务器,并向控制台打印一个日志注释。 在服务器运行时,您可以在浏览器中转到 localhost:3000 ,查看返回的示例响应。

导入和创建模块

模块是一个JavaScript库/文件,您可以使用Node的 require()函数将其导入其他代码。 本身是一个模块,我们在 Express 应用程序中使用的中间件和数据库库也是一样。

下面的代码显示了如何使用 Express 框架作为示例,按名称导入模块。 首先我们调用 require()函数,将模块的名称指定为字符串(\'express\' 代码>),并调用返回的对象以创建 Express应用程序。 然后我们可以访问应用程序对象的属性和函数。

var express = require('express');
var app = express();

您还可以创建自己的模块,可以以相同的方式导入。

提示:您将希望创建自己的模块,因为这允许您将代码整理成可管理的部分 - 单一的单文件应用程序很难理解和维护。 使用模块还可以帮助您管理命名空间,因为只有在您使用模块时才导入显式导出的变量。

要使对象在模块外部可用,您只需要将它们分配给 exports 对象。 例如,下面的 square.js 模块是导出 area() perimeter()方法的文件:

exports.area = function (width) { return width * width; };
exports.perimeter = function (width) { return 4 * width; };

我们可以使用 require()导入此模块,然后调用导出的方法如下所示:

var square = require('./square'); // Here we require() the name of the file without the (optional) .js file extension
console.log('The area of a square with a width of 4 is ' + square.area(4));

注意:您还可以指定模块的绝对路径(或名称,如我们最初所做的那样)。

如果要在一个分配中导出完整的对象,而不是一次创建一个属性,请将其分配给 module.exports ,如下所示(您也可以执行此操作以创建导出的根 对象的构造函数或其他函数):

module.exports = {
  area: function(width) {
    return width * width;
  },
       
  perimeter: function(width) {
    return 4 * width;
  }
};

有关模块的更多信息,请参阅模块(节点API文档)。

使用异步API

JavaScript代码经常使用异步而不是同步API进行可能需要一些时间才能完成的操作。 同步API是每个操作必须在下一个操作开始之前完成的API。 例如,以下日志函数是同步的,并将按顺序(第一,第二)将文本打印到控制台。

console.log('First');
console.log('Second');

相比之下,异步API是其中API将开始操作并立即返回(在操作完成之前)的API。 一旦操作完成,API将使用一些机制来执行额外的操作。 例如,下面的代码将打印出"Second,First",因为即使 setTimeout()方法被首先调用,并立即返回,操作不会完成几秒钟。

setTimeout(function() {
   console.log('First');
   }, 3000);
console.log('Second');

使用非阻塞异步API在Node上比在浏览器中更重要,因为 Node 是一个单线程事件驱动的执行环境。 "单线程"意味着对服务器的所有请求都在同一线程上运行(而不是生成到单独的进程)。 这种模式在速度和服务器资源方面非常高效,但它的意思是,如果你的任何函数调用同步方法需要很长时间来完成,它们将阻塞不仅当前请求,而每隔一个请求被处理 您的Web应用程序。

异步API有多种方式可以通知应用程序已完成。 最常见的方法是在调用异步API时注册回调函数,该操作将在操作完成时调用。 这是上面使用的方法。

提示:如果您有一系列必须按顺序执行的依赖异步操作,则使用回调可能相当"混乱",因为这会导致多级嵌套回调。 这个问题通常被称为"回调地狱"。 通过良好的编码实践(请参阅 http://callbackhell.com/ )可以减少此问题,使用类似 a class ="external"href ="https://www.npmjs.com/package/async"> async ,甚至移至ES5的功能,例如 org / zh-CN / docs / Web / JavaScript / Reference / Global_Objects / Promise"> Promises

注意: Node和Express的常见约定是使用错误优先回调。 在这个约定中,回调函数中的第一个值是错误值,而后续参数包含成功数据。 有一个很好的解释为什么这种方法是有用的在这个博客: -js /%C2%A0"> Node.js方式 - 了解错误 - 第一回调(fredkschott.com)。

创建路由处理程序

在我们的 Hello World Express示例中,我们定义了一个(callback)路由处理函数,用于向网站根(\'/\' )。

app.get('/', function (req, res) {
  res.send('Hello World!');
});

回调函数接受请求和响应对象作为参数。 在这种情况下,该方法只需调用 send() 代码>上的响应返回字符串"Hello World!" 有用于结束请求/响应周期的其他响应方法数 ,例如您可以调用 res.json() / code>发送JSON响应或 res.sendFile() a> 发送文件。

JavaScript提示:您可以在回调函数中使用任何您喜欢的参数名称; 当回调被调用时,第一个参数总是请求,第二个参数总是响应。 命名它们是有意义的,以便可以在回调的主体中标识您正在使用的对象。

对象也提供了为所有其他HTTP动词定义路线处理程序的方法,大多数使用方式完全相同: post() put (), delete() options() lock() mkcol() move() purge ), proppatch() unlock() report() mkactivity ), checkout() checkout() > notify(), subscribe() unsubscribe() 代码>和 connect()

有一个特殊的路由方法, app.all(),它将被调用以响应任何HTTP方法。 这用于在所有请求方法的特定路径加载中间件函数。 下面的示例(来自Express文档)显示了一个处理程序,它将对 / secret 的请求执行,而不考虑所使用的HTTP动词(假设它受到 "https://nodejs.org/api/http.html#http_http_methods\">http模块)。

app.all('/secret', function (req, res, next) {
  console.log('Accessing the secret section ...')
  next() // pass control to the next handler
})

路由允许您匹配URL中的特定字符模式,并从URL中提取一些值,并将它们作为参数传递给路由处理程序(作为作为参数传递的请求对象的属性)。

通常,将站点的特定部分的路由处理器组合在一起并使用公共路由前缀来访问它们是有用的(例如,具有Wiki的站点可以在一个文件中具有所有与wiki相关的路由,并且使用路由前缀 的 / wiki / )。 使用 快递 .Router 对象。 例如,我们可以在名为 wiki.js 的模块中创建wiki路由,然后导出 Router 对象,如下所示:

// wiki.js - Wiki route module

var express = require('express')
var router = express.Router()

// Home page route
router.get('/', function (req, res) {
  res.send('Wiki home page')
})

// About page route
router.get('/about', function (req, res) {
  res.send('About this wiki')
})

module.exports = router

注意:向 Router 对象添加路由就像向 app 对象添加路由(如前所示)。

要在我们的主要应用程序文件中使用路由器,我们将 require()路由模块( wiki.js ),然后调用 use Express 应用程序上将路由器添加到中间件处理路径。 然后,可以从 / wiki / 正常"> / wiki / about /

var wiki = require('./wiki.js')
// ...
app.use('/wiki', wiki)

我们将向您展示更多关于使用路由的信息,特别是关于使用 Router ,稍后在链接的部分 -US / docs / Learn / Server-side / Express_Nodejs / routes">路由和控制器

使用中间件

中间件广泛用于Express应用程序,用于从提供静态文件到错误处理,到压缩HTTP响应的任务。 路由功能通过向HTTP客户端返回一些响应来结束HTTP请求 - 响应循环,而中间件功能通常对请求或响应执行一些操作,然后调用"堆栈"中的下一个功能, 可能更多的中间件或路由处理程序。 中间件被调用的顺序取决于应用程序开发人员。

注意:中间件可以执行任何操作,执行任何代码,更改请求和响应对象,也可以结束请求 - 响应周期。 如果它没有结束循环,它必须调用 next()将控制传递给下一个中间件函数(或者请求将被挂起)。

大多数应用程序将使用第三方中间件,以简化常见的网站开发任务,如使用Cookie,会话,用户身份验证,访问请求 POST 和JSON数据,日志记录等 。您可以找到 Express小组维护的中间件软件包列表(其中还包括其他 流行的第三方包)。 其他Express包在NPM包管理器上可用。

要使用第三方中间件,您首先需要使用NPM将其安装到您的应用程序中。 例如,要安装摩根 HTTP请求记录器中间件,您需要执行此操作 :

$ npm install morgan

然后,您可以在 Express应用程序对象上调用 use()将中间件添加到堆栈:

var express = require('express');
var logger = require('morgan');
var app = express();
app.use(logger('dev'));
...

注意:中间件和路由功能按照声明的顺序调用。 对于一些中间件,顺序很重要(例如,如果会话中间件依赖于cookie中间件,则必须首先添加cookie处理程序)。 几乎总是在设置路由之前调用中间件,或者路由处理程序将无法访问中间件添加的功能。

您可以编写自己的中间件函数,并且可能必须这样做(如果只创建错误处理代码)。 中间件函数和路由处理程序回调之间的区别在于中间件函数具有第三个参数 next ,如果中间件函数不完成请求, 循环(当调用中间件函数时,它包含必须调用的 next 函数)。

您可以使用 app.use() app.add()向处理链添加中间件函数,具体取决于您是否要将中间件应用于所有响应 或具有特定HTTP动词( GET POST 等)的响应。 您可以在两种情况下指定相同的路由,但在调用 app.use()时路由是可选的。

下面的示例显示了如何使用这两种方法添加中间件函数,以及使用/不使用路由。

var express = require('express')
var app = express()

// An example middleware function
var a_middleware_function = function(req, res, next) {
  //... perform some operations
  next(); //Call next() so Express will call the next middleware function in the chain.
}

// Function added with use() for all routes and verbs
app.use(a_middleware_function)

//Function added with use() for a specific route
app.use('/someroute', a_middleware_function)

// A middleware function added for a specific HTTP verb and route
app.get('/', a_middleware_function )

app.listen(3000)

JavaScript提示:我们在上面单独声明中间件函数,然后将其设置为回调。 在我们以前的路由处理函数中,我们在使用回调函数时声明了它。 在JavaScript中,两种方法都是有效的。

Express文档提供了许多关于使用 外部"href ="http://expressjs.com/en/guide/writing-middleware.html">写 Express中间件。

提供静态文件

express.static middleware to serve static files, including your images, CSS and JavaScript (static() is the only middleware function that is actually part of Express). For example, you would use the line below to serve images, CSS files, and JavaScript files from a directory named \'public\' at the same level as where you call node:\">

app.use(express.static('public'))

公共目录中的任何文件通过将它们的文件名(相对添加到基本"public"目录)添加到基本URL来服务。 例如:

http://localhost:3000/images/dog.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
http://localhost:3000/about.html

您可以多次调用 static()来提供多个目录。 如果一个文件不能被一个中间件函数找到,它将被简单地传递到后续的中间件(中间件被调用的顺序是基于你的声明顺序)。

app.use(express.static('public'))
app.use(express.static('media'))

您还可以为静态URL创建虚拟前缀,而不是将文件添加到基本URL。 例如,在这里,我们指定装载路径,以便加载文件 带前缀"/ media":

app.use('/media', express.static('public'))

现在,您可以从 / media 路径前缀加载 public 目录中的文件。

http://localhost:3000/media/images/dog.jpg
http://localhost:3000/media/video/cat.mp4
http://localhost:3000/media/cry.mp3

有关详细信息,请参阅在Express中提供静态文件

处理错误

错误由一个或多个具有四个参数的特殊中间件函数处理,而不是通常的三个:(err,req,res,next)。 例如:

app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

这些可以返回任何所需的内容,但必须在所有其他 app.use()之后调用,并路由调用,以便它们是请求处理过程中的最后一个中间件!

Express附带一个内置的错误处理程序,它处理应用程序中可能遇到的任何剩余错误。 此默认错误处理中间件函数在中间件函数堆栈的末尾添加。 如果你向 next()传递错误,并且你不在错误处理程序中处理它,它将由内置的错误处理程序处理; 该错误将被写入具有堆栈跟踪的客户端。

注意:生产环境中不包括堆栈跟踪。 要在生产模式下运行它,您需要将环境变量 NODE_ENV 设置为\' production\'

注意: HTTP404和其他"错误"状态代码不会被视为错误。 如果你想处理这些,你可以添加一个中间件函数来这样做。 有关详情,请参阅常见问题

有关详细信息,请参阅错误处理(Express docs)。

使用数据库

应用程序可以使用 Node 支持的任何数据库机制( Express 本身没有定义数据库管理的任何特定附加行为/要求)。 有很多选项,包括PostgreSQL,MySQL,Redis,SQLite,MongoDB等。

为了使用这些,您必须首先使用NPM安装数据库驱动程序。 例如,要为受欢迎的NoSQL MongoDB安装驱动程序,您可以使用以下命令:

$ npm install mongodb

数据库本身可以在本地安装或在云服务器上安装。 在Express代码中,您需要驱动程序,连接到数据库,然后执行创建,读取,更新和删除(CRUD)操作。 下面的示例(从Express文档)显示如何使用MongoDB查找"哺乳动物"记录。

var MongoClient = require('mongodb').MongoClient

MongoClient.connect('mongodb://localhost:27017/animals', function (err, db) {
  if (err) throw err

  db.collection('mammals').find().toArray(function (err, result) {
    if (err) throw err

    console.log(result)
  })
})

另一种流行的方法是通过对象关系映射器("ORM")间接访问您的数据库。 在这种方法中,您将数据定义为"对象"或"模型",ORM将这些映射到底层数据库格式。 这种方法有一个好处,作为开发人员,您可以继续考虑JavaScript对象而不是数据库语义,并且有一个明显的地方执行验证和检查传入数据。 我们将在后面的文章中更多地讨论数据库。

有关详情,请参阅数据库集成(Express docs)。

呈现数据(视图)

模板引擎(通过Express 称为"视图引擎")允许您在模板中指定输出文档的结构,使用占位符来填充数据 当页面生成时。 模板通常用于创建HTML,但也可以创建其他类型的文档。 Express支持一些模板引擎,并且有一个有用的比较 这里更受欢迎的引擎:比较JavaScript模板引擎:翡翠,小胡子,灰尘 和更多。

在您的应用程序设置代码中,您设置要使用的模板引擎以及Express使用\'views\'和\'view engines\'设置查找模板的位置,如下所示(您还必须安装包含模板库的包 !)

var express = require('express');
var app = express();

// Set directory to contain the templates ('views')
app.set('views', path.join(__dirname, 'views'));

// Set view engine to use, in this case 'some_template_engine_name'
app.set('view engine', 'some_template_engine_name');

模板的外观将取决于您使用的引擎。 假设您有一个名为"index。< template_extension>"的模板文件 包含名为"title"和"message"的数据变量的占位符,您可以调用 "> Response.render() 在路由处理函数中创建和发送HTML响应:

app.get('/', function (req, res) {
  res.render('index', { title: 'About dogs', message: 'Dogs rock!' })
})

有关详情,请参阅使用带有Express的模板引擎(Express docs)。

文件结构

Express对结构或您使用的组件不做任何假设。 路由,视图,静态文件和其他特定于应用程序的逻辑可以存在于任何目录结构的任意数量的文件中。 虽然完全有可能将整个 Express 应用程序放在一个文件中,但通常将应用程序分成基于功能(例如帐户管理,博客,讨论板)和架构问题域 例如模型,视图或控制器(如果您正在使用 MVC体系结构) >)。

在后面的主题中,我们将使用快速应用程序生成器 ,它创建一个模块化的应用程序框架,我们可以轻松地扩展它来创建Web应用程序。

    概要

    恭喜,您已完成Express / Node旅程中的第一步! 您现在应该明白Express和Node的主要优点,以及Express应用程序的主要部分(路由,中间件,错误处理和模板代码)。 你也应该明白,Express是一个非主动的框架,你把这些部分组合在一起的方式和你使用的库大部分取决于你!

    当然Express是一个非常轻量级的Web应用程序框架,所以它的好处和潜力来自第三方库和功能。 我们将在下面的文章中更详细地讨论这些。 在下一篇文章中,我们将介绍如何设置Node开发环境,以便您可以开始看到一些Express代码在运行。

    也可以看看

    Tools and testing
    Express Tutorial Part 6: Working with forms
    温馨提示
    下载编程狮App,免费阅读超1000+编程语言教程
    取消
    确定
    目录
    CSS

    关闭

    MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }