node

yarn

yarn 是由 Facebook、Google、Exponent 和 Tilde 联合推出了一个新的 JS 包管理工具,yarn 是为了弥补 npm 的一些缺陷而出现的。

阅读全文

redoc

https://github.com/Redocly/redoc redoc自己号称是一个最好的在线文档工具。它支持swagger接口数据,提供了多种生成文档的方式,非常容易部署。使用redoc-cli能够将您的文档捆绑到零依赖的 HTML文件中,响应式三面板设计,具有菜单/滚动同步。

阅读全文

automerge

Automerge是一个用于在JavaScript中构建协作应用程序的数据结构库。

阅读全文

yield *

yield表达式的值,是下一个iter.next的参数值

阅读全文

Sequelize Transactions

Sequelize 支持两种使用事务的方法:

阅读全文

this

node this总结: 1,类的method指向的是当前对象 2,在node 中函数里的this 指向global 3,在浏览器中this 指向window 4,()=>{} 默认会绑定当前作用域的 this https://www.yisu.com/zixun/691554.html https://blog.csdn.net/weixin_44154094/article/details/120006139 https://www.bbsmax.com/A/x9J2YxOZz6/

阅读全文

js中页面加载完成后执行的几种方法及执行顺序

在js和jquery使用中,经常使用到页面加载完成后执行某一方法。通过整理,大概是五种方式(其中有的只是书写方式不一样)。

阅读全文

quill

{ ops: [ { insert: ‘Gandalf’, attributes: { bold: true } }, { insert: ‘ the ‘ }, { insert: ‘Grey’, attributes: { color: ‘#cccccc’ } } ] }

阅读全文

npm-check-updates

npm升级package.json依赖包到最新版本号 1、安装: npm install -g npm-check-updates 2、使用:

阅读全文

Mocha

Mocha 是一个功能丰富的Javascript测试框架,它能运行在Node.js和浏览器中,支持BDD、TDD、QUnit、Exports式的测试, 安装 npm install mocha -g

阅读全文

IFrame 中的Cookie问题

在一个应用(domain: A)的某个page中, 通过IFrame的方式嵌入另一个应用(domain: B)的某个页面. 当两个应用的domain 不一样时, 在被嵌入的页面中不允许使用cookie(即使用cookie实现的session会失效). 在XP SP2和IE6之后,从安全性角度考虑,默认状态下不允许在iframe里使用跨站点cookie。

阅读全文

CodeMirror

CodeMirror is a versatile text editor implemented in JavaScript for the browser. It is specialized for editing code, and comes with over 100 language modes and various addons that implement more advanced editing functionality. Every language comes with fully-featured code and syntax highlighting to help with reading and editing complex code.

阅读全文

webrtc 实现远程光标

https://github.com/SilentTiger/laser-pen

阅读全文

window.location.protocol window.location.host

protocol 属性是一个可读可写的字符串,可设置或返回当前 URL 的协议。

阅读全文

useEffect

函数组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

阅读全文

jsrun

国外的: jsfiddle , jsbin , codepen 国内的: jsrun,jsdm,runjs http://go.jsrun.net/

阅读全文

addIceCandidate

ClientA首先创建PeerConnection对象,然后打开本地音视频设备,将音视频数据封装成MediaStream添加到PeerConnection中。 ClientA调用PeerConnection的CreateOffer方法创建一个用于offer的SDP对象,SDP对象中保存当前音视频的相关参数。ClientA通过PeerConnection的SetLocalDescription方法将该SDP对象保存起来,并通过Signal服务器发送给ClientB。 ClientB接收到ClientA发送过的offer SDP对象,通过PeerConnection的SetRemoteDescription方法将其保存起来,并调用PeerConnection的CreateAnswer方法创建一个应答的SDP对象,通过PeerConnection的SetLocalDescription的方法保存该应答SDP对象并将它通过Signal服务器发送给ClientA。 ClientA接收到ClientB发送过来的应答SDP对象,将其通过PeerConnection的SetRemoteDescription方法保存起来。 在SDP信息的offer/answer流程中,ClientA和ClientB已经根据SDP信息创建好相应的音频Channel和视频Channel并开启Candidate数据的收集,Candidate数据可以简单地理解成Client端的IP地址信息(本地IP地址、公网IP地址、Relay服务端分配的地址)。 当ClientA收集到Candidate信息后,PeerConnection会通过OnIceCandidate接口给ClientA发送通知,ClientA将收到的Candidate信息通过Signal服务器发送给ClientB,ClientB通过PeerConnection的AddIceCandidate方法保存起来。同样的操作ClientB对ClientA再来一次。 这样ClientA和ClientB就已经建立了音视频传输的P2P通道,ClientB接收到ClientA传送过来的音视频流,会通过PeerConnection的OnAddStream回调接口返回一个标识ClientA端音视频流的MediaStream对象,在ClientB端渲染出来即可。同样操作也适应ClientB到ClientA的音视频流的传输。 https://www.cnblogs.com/fangkm/p/4364553.html

阅读全文

.d.ts

用 ts 写的模块在发布的时候仍然是用 js 发布,这就导致一个问题:ts 那么多类型数据都没了,所以需要一个 d.ts 文件来标记某个 js 库里面对象的类型

阅读全文

jsfiddle

https://jsfiddle.net/1f8ckg90/ https://github.com/jsfiddle/togetherjs 这个工具可以有效的帮助web前端开发人员来有效分享和演示前端效果,大家可以在blog和论坛里用jsFiddle解答或者提问。

阅读全文

prototype

在任意对象和Object.prototype之间, 存在着一条以非标准属性__proto__进行连接的链, 我们将这条链称为原型链, 在默认情况下,一个任意的对象的原型是Object.prototype

阅读全文

WebTorrent

https://webtorrent.io/ https://github.com/webtorrent/webtorrent

阅读全文

n

n 是Node的一个模块,作者是TJ Holowaychuk(鼎鼎大名的Express框架作者),就像它的名字一样,它的理念就是简单:

阅读全文

electron

https://www.electronjs.org/ https://github.com/electron/electron

阅读全文

electron-android cordova

改造可行性: electron应用也是在web项目上套了一层应用壳而已,所以移植到混合应用hybird上面,通过webview支持web显示也是可行的,所以!改造只要在浏览器上面跑得通就行!但是有些功能可能会稍有不同,需要做依据需求删除。

阅读全文

React useState

useState()是改变状态的开关,将状态添加到函数组件需要4个步骤:启用状态、初始化、读取和更新。

阅读全文

typescript可选参数

在参数名称的后面加一个 ? 号 使这个参数变为可选项

阅读全文

WebRTC 协议

总的来说,WebRTC现在已经覆盖所有的现代浏览器了。 https://webrtc.org.cn/webrtc-in-browsers/ peer-to-peer communications and video-conferencing in HTML covered by this specification:

阅读全文

Node.js 的协程coroutine

实现 coroutine 的方式有很多,比如 ES6 的 generator,ES7 的 async/await。而 tj 大神开发的 co 模块,也是取名字 coroutine 的缩写。 coroutine 需要底层的支持,对于不支持 generator/async/await 的平台来说,需要编译为 es5 代码。

阅读全文

webpack vue

webpack是一款模块加载器兼打包工具,把JS、样式,图片都作为模块来使用和处理。项目下有个配置文件webpack.config.js,用来告诉webpack需要做什么,本项目的webpack.config.js文件内容如下:

阅读全文

max_old_space_size

node –max_old_space_size

阅读全文

ace 编辑器

https://ace.c9.io/ https://github.com/chairuosen/vue2-ace-editor https://github.com/chairuosen/vue-ace-editor-demo/tree/vue2 https://github.com/ajaxorg/ace/

阅读全文

YSLOW

http://yslow.org/ Yslow-23条规则编辑 播报 [3] 1. 减少HTTP请求次数 合并图片、CSS、JS,改进首次访问用户等待时间。

  1. 使用CDN 就近缓存==>智能路由==>负载均衡==>WSA全站动态加速
  2. 避免空的src和href 当link标签的href属性为空、script标签的src属性为空的时候,浏览器渲染的时候会把当前页面的URL作为它们的属性值,从而把页面的内容加载进来作为它们的值。测试
  3. 为文件头指定Expires 使内容具有缓存性。避免了接下来的页面访问中不必要的HTTP请求。
  4. 使用gzip压缩内容 压缩任何一个文本类型的响应,包括XML和JSON,都是值得的。旧文章
  5. 把CSS放到顶部
  6. 把JS放到底部 防止js加载对之后资源造成阻塞。
  7. 避免使用CSS表达式
  8. 将CSS和JS放到外部文件中 目的是缓存,但有时候为了减少请求,也会直接写到页面里,需根据PV和IP的比例权衡。
  9. 权衡DNS查找次数 减少主机名可以节省响应时间。但同时,需要注意,减少主机会减少页面中并行下载的数量。 IE浏览器在同一时刻只能从同一域名下载两个文件。当在一个页面显示多张图片时,IE 用户的图片下载速度就会受到影响。所以新浪会搞N个二级域名来放图片。
  10. 精简CSS和JS
  11. 避免跳转 同域:注意避免反斜杠 “/” 的跳转; 跨域:使用Alias或者mod_rewirte建立CNAME(保存域名与域名之间关系的DNS记录)
  12. 删除重复的JS和CSS 重复调用脚本,除了增加额外的HTTP请求外,多次运算也会浪费时间。在IE和Firefox中不管脚本是否可缓存,它们都存在重复运算JavaScript的问题。
  13. 配置ETags 它用来判断浏览器缓存里的元素是否和原来服务器上的一致。比last-modified date更具有弹性,例如某个文件在1秒内修改了10次,Etag可以综合Inode(文件的索引节点(inode)数),MTime(修改时间)和Size来精准的进行判断,避开UNIX记录MTime只能精确到秒的问题。 服务器集群使用,可取后两个参数。使用ETags减少Web应用带宽和负载
  14. 可缓存的AJAX “异步”并不意味着“即时”:Ajax并不能保证用户不会在等待异步的JavaScript和XML响应上花费时间。
  15. 使用GET来完成AJAX请求 当使用XMLHttpRequest时,浏览器中的POST方法是一个“两步走”的过程:首先发送文件头,然后才发送数据。因此使用GET获取数据时更加有意义。
  16. 减少DOM元素数量 是否存在一个是更贴切的标签可以使用?人生不仅仅是DIV+CSS
  17. 避免404 有些站点把404错误响应页面改为“你是不是要找***”,这虽然改进了用户体验但是同样也会浪费服务器资源(如数据库等)。最糟糕的情况是指向外部 JavaScript的链接出现问题并返回404代码。首先,这种加载会破坏并行加载;其次浏览器会把试图在返回的404响应内容中找到可能有用的部分当作JavaScript代码来执行。
  18. 减少Cookie的大小
  19. 使用无cookie的域 比如图片 CSS 等,Yahoo! 的静态文件都在主域名以外,客户端请求静态文件的时候,减少了 Cookie 的反复传输对主域名的影响。
  20. 不要使用滤镜 png24的在IE6半透明那种东西,别乱使,淡定的切成PNG8+jpg
  21. 不要在HTML中缩放图片
  22. 缩小favicon.ico并缓存 https://baike.baidu.com/item/YSLOW/10384699?fr=aladdin
阅读全文

bluebird

https://github.com/petkaantonov/bluebird

阅读全文

Promise

对象的状态不受外界影响 (3种状态) Pending状态(进行中) Fulfilled状态(已成功) Rejected状态(已失败) 一旦状态改变就不会再变 (两种状态改变:成功或失败) Pending -> Fulfilled Pending -> Rejected

阅读全文

prom-client

https://github.com/siimon/prom-client 这是一个支持histogram, summaries, gauges and counters四种数值格式的prometheus nodejs客户端。 停止轮询默认metrics

阅读全文

EventSource

An EventSource instance opens a persistent connection to an HTTP server, which sends events in text/event-stream format. The connection remains open until closed by calling

阅读全文

protobuf2json

protobuf2json是一个json 和proto互转的命令行工具

阅读全文

ejs

https://github.com/mde/ejs

阅读全文

socketio

https://github.com/socketio/engine.io https://github.com/socketio/socket.io-redis-adapter https://github.com/socketio/socket.io-client https://github.com/Terry-Mao/goim

阅读全文

pdf.js

https://github.com/mozilla/pdf.js

阅读全文

quill

https://quilljs.com/docs/modules/keyboard/ https://github.com/quilljs/quill https://quilljs.com/ https://www.jianshu.com/p/01a1a7029276 中文文档 https://kang-bing-kui.gitbook.io/quill/wen-dang-document/delta https://quilljs.com/playground/#autogrow-height

阅读全文

excalidraw

https://github.com/excalidraw/excalidraw

阅读全文

node object diff javascript中的require、import和export

https://www.npmjs.com/package/@dreamworld/deep-object-diff

阅读全文

axeslide

斧子演示 http://axeslide.com,我们团队开发的,演示效果和上面提到Prezi效果一样,不同的是我们的软件是基于H5开发的,完全免费,同时支持移动端浏览器播放,无需安装app、Flash插件。支持图表插入和编辑,支持svg,支持导出视频,支持gif文件,支持排版对齐,图像等大小等。不必在纠结中文字体,破解的问题。 https://www.zhihu.com/question/20204473

阅读全文

Luckysheet

https://github.com/mengshukeji/Luckysheet

阅读全文

Generator

https://www.bookstack.cn/read/es6-3rd/spilt.4.docs-async-iterator.md

阅读全文

vue init webpack project 一直处于...downloading template状态处理

 运行npm install webpack -g,重新安装webpack。   https://www.cnblogs.com/diweikang/p/9461530.html

阅读全文

vue

npm install vue npm install –global vue-cli vue init webpack exp3 cd exp3 npm install npm run dev

阅读全文

服务器端渲染 (SSR)

Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记”激活”为客户端上完全可交互的应用程序。

阅读全文

quill 文本编辑器

https://zhuanlan.zhihu.com/p/272499379 https://github.com/quilljs/quill https://quilljs.com/ 文档: http://doc.quilljs.cn/1409370 体验: https://codepen.io/liuwave/pen/wvBwqod

阅读全文

eslint

Line 7:9: Parsing error: Unexpected token

阅读全文

Generator yield

注意Generator、yield;async、await要配套使用,且yield 的外层的函数一定要声明称Generator () 为了解决异步的嵌套问题,真是操碎了心,先是出了个Promise,然后又是Generator、yield组合,直到ES7的async、await组合。 Generator 生成器对象是由function 返回的,并且符合可迭代协议和迭代器协议。 这里有几个概念生成器、可迭代协议、迭代器协议。具体的概念可以点击链接查看MDN文档。

阅读全文

koa 中间件

https://www.kancloud.cn/aibabel/koafornodejs/1812621 中间件工作原理 初始化koa实例后,我们会用use方法来加载中间件(middleware),会有一个数组来存储中间件,use调用顺序会决定中间件的执行顺序。 每个中间件都是一个函数(不是函数将报错),接收两个参数,第一个是ctx上下文对象,另一个是next函数(由koa-compose定义) 在建立好http服务器后,会调用koa-compose模块对middleware中间件数组进行处理。具体代码这里就不贴了,原理就是:会从middleware数组中取第一个函数开始执行,中间件函数中调用next方法就会去取下一个中间件函数继续执行。每个中间件函数执行完毕后都会返回一个promise对象。(ps:调用next方法并不是表示当前中间件函数执行完毕了,调用next之后仍可以继续执行其他代码) /**

  • koa 中间件的执行顺序 / // 引入模块 const Koa = require(‘koa’); const router = require(‘koa-router’)(); /引入是实例化路由 推荐*/

// 实例化 let app = new Koa();

// Koa中间件 // 匹配任何路由,如果不写next,这个路由被匹配到了就不会继续向下匹配

// www.域名.com/news app.use(async (ctx, next) => { console.log(‘1、这是一个中间件01’); await next();

console.log(‘5、匹配路由完成以后又会返回来执行中间件’); })

app.use(async (ctx, next) => { console.log(‘2、这是一个中间件02’); await next();

console.log(‘4、匹配路由完成以后又会返回来执行中间件’); })

router.get(‘/’, async (ctx) => { ctx.body = ‘首页’; })

router.get(‘/news’, async (ctx) => { console.log(‘3、匹配到了news这个路由’); ctx.body = ‘这是一个新闻页面’; })

router.get(‘/login’, async (ctx) => { ctx.body = ‘登录页面’; })

app.use(router.routes()); app.use(router.allowedMethods()); /**

  • router.allowedMethods() 作用:这是官方文档的推荐用法,我们可以
  • 看到 router.allowedMethods() 用在了路由匹配 router.routes()之后,
  • 所以在当所有路由中间件最后调用,此时根据 ctx.status 设置 response 响应头 */

app.listen(3000);

阅读全文

vue

https://cn.vuejs.org/

阅读全文

sso

sso(单点登录) 单点登录 的相关概念就不累述了,通俗的讲就是一次登录,到处有效,免去你在不同的服务间跳转时的繁琐验证。比如某宝网站是很多个系统相互调用形成的,如果没做单点登录的话,你每跳转一个服务,都需要验证身份,想想就可怕

阅读全文

sequelize

https://sequelize.org/

阅读全文

require 文件夹

node中使用require的时候如果路径是一个文件夹时,或者特殊的情况require(‘..’);require(‘.’);

阅读全文

jwt json web token

JWT全称是jsonwebtoken,JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源

阅读全文

bluebird

http://bluebirdjs.com/docs/getting-started.html https://github.com/petkaantonov/bluebird

阅读全文

Generator 自动执行

单个异步任务

阅读全文

cli

首先,使用 JavaScript 语言,写一个可执行脚本 hello 。

阅读全文

bin

npm install –save-dev node-sass 1 安装好依赖后, node_modules 文件夹下 .bin 就出现了node-sass 文件,

阅读全文

npm

npm 可以在项目package.json里面自定义脚本命令

阅读全文

x-request-id

当您操作由客户端访问的Web服务时,可能难以将请求(客户端可以看到)与服务器日志(服务器可以看到)相关联。 X-Request-ID的想法是,客户端可以创建一些随机ID并将其传递给服务器。然后,服务器在其创建的每个日志语句中包含该ID。如果客户端收到错误,它可以将ID包含在错误报告中,允许服务器运算符查找相应的日志语句(而不必依赖于时间戳,IP等)。

阅读全文

authentication cookie token

权限认证基础:区分Authentication,Authorization以及Cookie、Session、Token

阅读全文

node_modules

如果我们在外部js文件中直接require(‘slib’),nodejs会自动: 1)看它内置模块中是否有,如果有就优先加载内置模块 2)如果没有就看是否是“路径级”的引用 3)以上都不是就会在node_modules寻找同名文件夹。首先会默认寻找index.js,如果没有则会查看是否在package.json中做了main定义 内置模块如require(‘http’),路径级如require(‘./xxx.js’),注意这里的./代表的是当前js文件所在的目录,.js可写可不写,在下载gulp时由于包跟包之间有引用,因此会下载其他一些插件。 我们也可以在node_modules里自定义插件,如在node_modules里新建一个文件夹,里面的js文件一定要定义成index.js,这样当我们引用这个js文件时,node会自动加载这个文件下的index.js 5.自定义插件

阅读全文

v8 vm

http://nodejs.cn/api/

阅读全文

use strict

ES6 的模块自动采用严格模式,不管你有没有在模块头部加上”use strict”;。

阅读全文

node 路由

我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。

阅读全文

package.json

使用 package.json package.json 位于模块的目录下,用于定义包的属性。

阅读全文

node 面向对象

https://blog.csdn.net/yangding_/article/details/52824209

阅读全文

node 事件模型

Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。

阅读全文

node 模块系统

为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。

阅读全文

function 匿名函数

基本用法: ES6中允许使用“箭头”(=>)定义函数

阅读全文

express

Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

阅读全文

vscode 调试 node.js

项目根目录下新建.vscode文件夹,同时该文件夹下新建launch.json文件 { “configurations”: [ { “type”: “node”, “request”: “launch”, “name”: “nodemon”, “runtimeExecutable”: “nodemon”, “program”: “${workspaceFolder}/app/starter.js”, “restart”: true, “console”: “integratedTerminal”, “internalConsoleOptions”: “neverOpen” } ] } 1.需要在项目跟目录下,不然点击启动调试的时候识别不出来该项目中有这个调试任务

阅读全文

tj/co 函数生成器

https://github.com/tj/co JS函数生成器,function* () {}

阅读全文

多进程

Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能。

阅读全文

node Buff Stream

Node.js Buffer(缓冲区) JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。

阅读全文

async await

async 函数是什么?一句话,它就是 Generator 函数的语法糖。

阅读全文

browserify

http://browserify.org/#install npm install -g browserify

阅读全文

callback hell

什么是回调地狱 异步的JavaScript程序,或者说使用了回调函数的JavaScript程序,很难地去直观顺畅地阅读,大量的代码以这种方式结束: fs.readdir(source, function (err, files) { if (err) { console.log(‘Error finding files: ‘ + err) } else { files.forEach(function (filename, fileIndex) { console.log(filename) gm(source + filename).size(function (err, values) { if (err) { console.log(‘Error identifying file size: ‘ + err) } else { console.log(filename + ‘ : ‘ + values) aspect = (values.width / values.height) widths.forEach(function (width, widthIndex) { height = Math.round(width / aspect) console.log(‘resizing ‘ + filename + ‘to ‘ + height + ‘x’ + height) this.resize(width, height).write(dest + ‘w’ + width + ‘_’ + filename, function(err) { if (err) console.log(‘Error writing file: ‘ + err) }) }.bind(this)) } }) }) } }) 有没有看到这些以”})”结尾的金字塔结构?这个形状为“亲切地”称为回调地狱。 二、什么是callback(回调)? “callback”仅仅只是一种使用JavaScript函数的一种通用称呼。在JavaScript语言中,并没有一种特定的东西称之为“callback”,这个只是一种方便的称呼。不同于大部分立即返回结果的函数,这些使用callback的函数需要消耗一些时间才能返回结果。“asynchronous”(异步),或者简称为“async”仅仅表示需要花费一些时间,或者是“在未来发生,而不是现在”。通常情况下,callback仅仅用于操作I/O的时候使用到。比如下载、读写文件、与数据库交互等。 当调用一个普通的函数的时候,你可以这样使用返回值: var result = multiplyTwoNumbers(5, 10) console.log(result) // 50 gets printed out 然而,异步函数,也就是使用了callback函数的不会立刻返回任何东西。 var photo = downloadPhoto(‘http://coolcats.com/cat.gif’) // photo is ‘undefined’! 在这种情况下,下载gif文件会花费相当长的时间,而且你并不希望你的程序在等待下载结束的过程中处于“暂停”(也就是阻塞,block)状态。 相反,你可以把下载结束后需要执行的操作存放到一个函数中,这个就是callback(回调)!你提供了一个“downloadPhoto”的函数,并且这个函数会在下载完成的时候执行callback(call you back later)函数,并且传递photo参数(或者出错的时候返回一个错误信息)。 downloadPhoto(‘http://coolcats.com/cat.gif’, handlePhoto) function handlePhoto (error, photo) { if (error) console.error(‘Download error!’, error) else console.log(‘Download finished’, photo) } console.log(‘Download started’) 人们在尝试理解“callback”这个概念的最大困难之处在于,程序运行的过程中,程序中的代码,是怎么样按照规则执行的。在这个例子中,有三件主要的代码段会发生:首先 “handlePhoto”函数被申明,然后是“downloadPhoto”函数会被调用并且把“handlePhoto”函数作为回调函数“callback”参数传递进入,最后,打印一句话“Download started”。 要注意以下,“handlePhoto”并没有立即被调用,只是创建并且作为一个参数传递给了“downloadPhoto”。但是,一旦“downloadPhoto”函数执行完成之后,”handlePhoto“就会运行。这个取决于网络连接到底有多快。 这个例子试图说明两个重要的概念: (1)回调函数“handlePhoto”仅仅是一种“存放”操作的方式,而这些操作需要延迟一段时间之后进行。 (2)代码的执行规则并不是按照阅读代码的“从上到下”的方式去遵守的,代码执行会根据事情结束的时间跳转嵌套。 三、我们如何解决“回调地狱”? 回调地狱的产生往往来源于对编码练习的缺乏,幸运的是,写出更好的代码并不困难! 我们只需要遵循如下三个原则: (1)保持代码浅显易读 下面是一个杂乱的JavaScript代码,这个代码用于通过使用“ browser-request ”从浏览器想服务端提交一个Ajax请求: var form = document.querySelector(‘form’) form.onsubmit = function (submitEvent) { var name = document.querySelector(‘input’).value request({ uri: “http://example.com/upload”, body: name, method: “POST” }, function (err, response, body) { var statusMessage = document.querySelector(‘.status’) if (err) return statusMessage.value = err statusMessage.value = body }) } 这段代码有两个匿名函数,我们给他们赋上名字吧! var form = document.querySelector(‘form’) form.onsubmit = function formSubmit (submitEvent) { var name = document.querySelector(‘input’).value request({ uri: “http://example.com/upload”, body: name, method: “POST” }, function postResponse (err, response, body) { var statusMessage = document.querySelector(‘.status’) if (err) return statusMessage.value = err statusMessage.value = body }) } 正如你看到的,给函数进行命名是一件超级简单的事情,而且会立刻体验到几个好处: 1)感谢这些具有描述性意义的函数名称把,这些名称使代码更加容易地阅读; 2)当异常发生的时候,你会在异常堆栈中看到确切的函数名称,而不是“anonymous”之类的名字; 3)你可以把函数移动出去,并且通过名字去引用他们; 现在,我们把这两个函数移动到我们程序的最顶层:

document.querySelector(‘form’).onsubmit = formSubmit function formSubmit (submitEvent) { var name = document.querySelector(‘input’).value request({ uri: “http://example.com/upload”, body: name, method: “POST” }, postResponse) } function postResponse (err, response, body) { var statusMessage = document.querySelector(‘.status’) if (err) return statusMessage.value = err statusMessage.value = body } 注意一下,函数声明在这里被移动到了文件最底部,这个要感谢 function hoisting. (2)模块化 这个是最重要的部分:任何人都有能力创建模块。引用 Isaac Schlueter (来源于node.js项目)的话说: Write small modules that each do one thing, and assemble them into other modules that do a bigger thing. You can’t get into callback hell if you don’t go there. “编写一个个小的模块,每个模块完成一件事情,然后把他们组装起来,去完成一个更大的事情,回调地狱这个坑,你不去往那走,你是不会陷进去的”。 让我们从上面的代码中提取模板代码,然后通过拆分到一组文件中的方式,将这些模板代码组装成module。我会展示一个module的格式,这种格式既可以用于浏览器的代码,也可以用在服务端。 这个是一个新的文件,叫做“formuploader.js”,里面包含两个从前面代码中提取的两个函数: module.exports.submit = formSubmit function formSubmit (submitEvent) { var name = document.querySelector(‘input’).value request({ uri: “http://example.com/upload”, body: name, method: “POST” }, postResponse) } function postResponse (err, response, body) { var statusMessage = document.querySelector(‘.status’) if (err) return statusMessage.value = err statusMessage.value = body } 其中“module.exports”部分是一个node.js模块系统的一个例子,electron 和 浏览器 使用browserify 。我非常喜欢这种模块化,因为它可以工作在任何地方,而且非常简单,并且不需要复杂的配置文件或者脚本。 现在我们有了“formuploader.js”(并且已经作为一个脚本,在页面加载完成之后载入到了页面中),我们只需要“require”这个模块并且使用它!这个是一个我们的程序中具体代码的样子: var formUploader = require(‘formuploader’) document.querySelector(‘form’).onsubmit = formUploader.submit 这样,我们的程序仅仅需要两行代码,并且有如下的好处: 1)对于一个新的开发者来说,更加容易理解了 —— 他们不用深陷于“被迫通读全部“formuploader”函数”。 2)“formuploader”可以用于其他地方,不用重复编写代码,并且可以轻松地分享到github或者npm上去。 (3)处理每一个独立的异常 错误(error)有很多种类型:程序员犯的语法错误(往往在第一次尝试运行程序的时候会被发现);程序员犯的运行时错误(程序可以运行但是里面有一个bug会把事情弄糟);其他情况下的平台错误比如无效的文件权限,硬件驱动失效、网络连接异常等问题。这个部分主要针对最后一类错误(error)。 前面两条原则可以让你的代码具有可读性,但是这一条可以让你的代码保持稳定健壮。当你在使用回调函数(callback)的时候,讲道理你其实是在和任务打交道,这些任务都是被分发给回调函数,并且回调函数会在后台执行,然后这个任务要么执行成功,要么由于失败而终止。任何有经验的开发者都会告诉你:你永远不会知道错误是谁么时候会发生,你只有去假定他们会一直出现。 目前在回调函数中处理错误最流行的方式是Node.js风格:所有的回调函数的第一个参数永远是给“error”保留着。 var fs = require(‘fs’)

fs.readFile(‘/Does/not/exist’, handleFile)

function handleFile (error, file) { if (error) return console.error(‘Uhoh, there was an error’, error) // otherwise, continue on and use file in your code } 在第一个参数中使用“error”是一个简单方便的鼓励记得处理错误的一个方式。如果把这个参数放到第二的位置,你在写代码的时候往往会容易忽略第二个“error”参数,而只关注第一个参数,比如“function handleFile(file){}”。 Code linters(检查代码的小工具)也可以通过配置,实现提醒你要处理这些回调函数错误。最简单的一个小工具就是 standard。这个工具你仅仅只需要在你的代码文件的路径中执行 “$ standard”命令,它就会把你每一个没有进行错误处理的回调函数标记出来。 四、总结 1、不要嵌套函数。给这些函数进行命名,并且放到你的程序的最顶层。 2、使用 function hoisting(函数提升)机制将你的函数移到文件的末尾。 3、在每一个回调函数中去处理每一个错误。可以使用一个代码检查工具去帮你完成这个事情。 4、创建可以服用的函数,并且把他们放置在一个模块中,这样可以提高代码可读性。把代码分割成一个个小的部分,可以帮助你更好的处理error,测试,强迫你去为你的代码创建一个稳定的、文档完善的公共API模块,而且有助于代码的重构。 最重要的避免回调地狱的方面就是,移出你的函数,这样程序的流程可以更容易理解,新手也就不用去啃每一个函数究竟是干什么的。 从现在开始,你首先就可以把函数移到文件的底部,然后逐渐地把函数移到另一个文件中并且使用类似“require(‘./photo-helpers.js’)”的方式去关联,最终,把他们放进一个独立的模块比如“require(‘image-resize’)”. 下面是一些创建模块的一些原则: 1、通过把一些经常重复使用的代码封装成一个函数; 2、当你的函数(或者一组具有类似主题功能的函数)足够大的时候,移动到另一个文件,并且通过“module.exports“的方式去发布,你可以使用类似“require(‘./photo-helpers.js’)”的方式去关联这个文件。 3、如果你的代码可以用于很多个项目的时候,你需要提供“readme”文件、测试以及“package.json”文件,并且把他们发布到github和npm中。 4、一个优秀的模块是很小的而且只聚焦于一个问题; 5、JavaScript的模块中的一个独立的文件行数不应该超过150行; 5、在整个JavaScript的文件结构组织中,一个模块不应该拥有超过一层的嵌套文件夹。如果这种情况发生了,那么意味着整个模块要做的事情有点过多了。 6、让更有经验的程序员给你演示下好的模块构建的方式,直到你了解究竟什么是优秀的模块。如果有一个模块,你需要花不止几分钟的时间去了解它是干嘛的,那么这个模块并不是一个多么好

阅读全文

Search

Recent posts

This blog is maintained by 夏泽民

Get in touch with me at 465474307@qq.com

Subscribe to our mailing list

* indicates required