Quantcast
Channel: CNode:Node.js专业中文社区
Viewing all 14821 articles
Browse latest View live

JavaScript30秒, 从入门到放弃之Array(三)

$
0
0

原文链接:JavaScript30秒, 从入门到放弃之Array(三)

水平有限,欢迎批评指正

flattenDepth

Flattens an array up to the specified depth.

Use recursion, decrementing depth by 1 for each level of depth. Use Array.reduce() and Array.concat() to merge elements or arrays. Base case, for depth equal to 1 stops recursion. Omit the second element, depth to flatten only to a depth of 1 (single flatten).

const flattenDepth = (arr, depth = 1) =>
  depth != 1
    ? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v), [])
    : arr.reduce((a, v) => a.concat(v), []);

把一个数组按指定深度进行摊平。

使用递归方法,对于任意级别的深度depth,每次递归depth1。使用Array.reduce()Array.concat()来合并元素们或者数组们。直到depth递减到1时停止递归。省略第二个参数depth时,按深度depth1计(即单层摊平)。

➜  code cat flattenDepth.js
const flattenDepth = (arr, depth = 1) =>
    depth != 1 ?
    arr.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v), []) :
    arr.reduce((a, v) => a.concat(v), []);

console.log(flattenDepth([1, [2], 3, 4]));
console.log(flattenDepth([1, [2, [5]], 3, 4]));
➜  code node flattenDepth.js
[ 1, 2, 3, 4 ]
[ 1, 2, [ 5 ], 3, 4 ]

根据depth来决定处理流程,depth存在且不等于1则进行递归:

arr.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v), [])

用了reduce()去处理循环时的每一个值,同时用concat把所有递归结果拼接成新数组返回。循环过程中,对值进行数组判断Array.isArray(v),是数组,flattenDepth(v, depth - 1)深度减1继续递归直到depth1为止;不是数组,直接返回该值v,供concat拼接。

否则,直接循环去拼接该值返回:

arr.reduce((a, v) => a.concat(v), []);

groupBy

Groups the elements of an array based on the given function.

Use Array.map() to map the values of an array to a function or property name. Use Array.reduce() to create an object, where the keys are produced from the mapped results.

const groupBy = (arr, func) =>
 arr.map(typeof func === 'function' ? func : val => val[func]).reduce((acc, val, i) => {
   acc[val] = (acc[val] || []).concat(arr[i]);
   return acc;
 }, {});

按照指定的方法对数组元素进行分组归类。

使用Array.map()对所有数组元素调用指定方法或者调用返回该元素的属性值的方法。使用Array.reduce()创建一个对象,对象的键是map生成的结果,值是符合该键的所有数组元素组成的数组。

➜  code cat groupBy.js
const groupBy = (arr, func) =>
    arr.map(typeof func === 'function' ? func : val => val[func]).
reduce((acc, val, i) => {
    acc[val] = (acc[val] || []).concat(arr[i]);
    return acc;
}, {});

console.log(groupBy([6.1, 4.2, 6.3], Math.floor));
console.log(groupBy(['one', 'two', 'three'], 'length'));
➜  code node groupBy.js
{ '4': [ 4.2 ], '6': [ 6.1, 6.3 ] }
{ '3': [ 'one', 'two' ], '5': [ 'three' ] }

代码拆分:

  1. map
arr.map(typeof func === 'function' ? func : val => val[func])

对第二个参数func的类型进行判断,若是function,则对数组arr所有元素调用该方法,返回一个新的数组。如:

const arr = [1, 2, 3, 4];
arr.map(x => x * x); // [1, 4, 9, 16]

否则,调用返回该元素对应func属性值方法:

const arr = ['one', 'two', 'three'];
const func = 'length';
arr.map(val => val[func]); // [3, 3, 5]
  1. reduce
reduce((acc, val, i) => {
  acc[val] = (acc[val] || []).concat(arr[i]);
  return acc;
}, {})

accreduce过程中累积的结果,valreduce的主体(即前边map的结果数组)每次循环时数组元素的值,i则是主体数组循环时对应的索引。

第一个循环时acc的初始值是一个空对象{},循环过程中先判断是否已经有以val为键的值,如果还没有,创建一个空数组把此时对应索引i的数组值arr[i]拼接,作为以val为键的值;否则,直接拼接arr[i]。即是acc[val] = (acc[val] || []).concat(arr[i])做的事。每次循环都返回acc对象,直到循环结束,生成分类结果。

连起来就是说先对数组arr元素进行mapmap结果作为键,所有map结果相同的数组元素arr[i]归到一个数组中作为该键的值。最终返回一个分好类的对象。

head

Returns the head of a list.

Use arr[0] to return the first element of the passed array.

const head = arr => arr[0];

返回数组第一个元素。

使用arr[0]返回指定数组arr的第一个元素。

➜  code cat head.js
const head = arr => arr[0];

console.log(head([1, 2, 3]));
➜  code node head.js
1

initial

Returns all the elements of an array except the last one.

Use arr.slice(0,-1) to return all but the last element of the array.

const initial = arr => arr.slice(0, -1);

返回除数组最后一个元素外的所有元素组成的新数组。

使用arr.slice(0, -1)返回数组除最后一个元素外的所有元素。

➜  code cat initial.js
const initial = arr => arr.slice(0, -1);

console.log(initial([1, 2, 3]));
➜  code node initial.js
[ 1, 2 ]

arr.slice(0, -1)立竿见影,实在没啥可说。

initialize2DArray

Initializes a 2D array of given width and height and value.

Use Array.map() to generate h rows where each is a new array of size w initialize with value. If the value is not provided, default to null.

const initialize2DArray = (w, h, val = null) =>
 Array(h)
   .fill()
   .map(() => Array(w).fill(val));

初始化一个给定宽(列)、高(行)和值的二维数组。

使用Array.map()来生成一个h行的数组。每一行含有w个值为指定值的元素。如果未指定任何值,数组的默认值是null

➜  code cat initialize2DArray.js
const initialize2DArray = (w, h, val = null) => Array(h).fill().map(() => Array(w).fill(val));

console.log(initialize2DArray(2, 2, 0));
➜  code node initialize2DArray.js
[ [ 0, 0 ], [ 0, 0 ] ]

Array(h).fill()先创建一个含有h个元素的数组并将它们全部默认填充为undefined。然后在生成的数组基础上,每个数组元素调用一个生成w个元素的数组且每个位置的值都填充为val方法。这样就生成了hw列的二维数组。

initializeArrayWithRange

Initializes an array containing the numbers in the specified range where start and end are inclusive.

Use Array((end + 1) - start) to create an array of the desired length, Array.map() to fill with the desired values in a range. You can omit start to use a default value of 0.

const initializeArrayWithRange = (end, start = 0) =>
 Array.from({ length: end + 1 - start }).map((v, i) => i + start);

初始化一个包含startend的有序数值的数组。

使用Array((end + 1) - start)生成所期望的数组,使用Array.map()去填充所期望的有序的数值。若省略start,则start默认值为0

➜  code cat initializeArrayWithRange.js
const initializeArrayWithRange = (end, start = 0) =>
    Array.from({
        length: end + 1 - start
    }).map((v, i) => i + start);

console.log(initializeArrayWithRange(5));
console.log(initializeArrayWithRange(7, 3));
➜  code node initializeArrayWithRange.js
[ 0, 1, 2, 3, 4, 5 ]
[ 3, 4, 5, 6, 7 ]

{length: end + 1 - start}生成的数组长度是end + 1 -start,而(v, i) => i + starti0开始循环,直到length - 1为止。而i + start则表示元素值从0 + start开始递增。

initializeArrayWithValues

Initializes and fills an array with the specified values.

Use Array(n) to create an array of the desired length, fill(v) to fill it with the desired values. You can omit value to use a default value of 0.

const initializeArrayWithValues = (n, value = 0) => Array(n).fill(value);

初始化一个数组并全部填充指定值。

Array(n)生成期望长度的数组,fill(v)填充期望的值。若省略第二个参数value,则value默认为0

➜  code cat initializeArrayWithValues.js
const initializeArrayWithValues = (n, value = 0) => Array(n).fill(value);

console.log(initializeArrayWithValues(5, 2));
➜  code node initializeArrayWithValues.js
[ 2, 2, 2, 2, 2 ]

Array(n).fill(value)一步到位,没啥可说的了。

intersection

Returns a list of elements that exist in both arrays.

Create a Set from b, then use Array.filter() on a to only keep values contained in b.

const intersection = (a, b) => {
  const s = new Set(b);
  return a.filter(x => s.has(x));
};

返回两个数组的交集。

首先创建一个b数组的集合,然后使用Array.filter()去筛选出a数组中存在于b数组中的元素。

➜  code cat intersection.js
const intersection = (a, b) => {
    const s = new Set(b);
    return a.filter(x => s.has(x));
};

console.log(intersection([1, 2, 3], [4, 3, 2]));
➜  code node intersection.js
[ 2, 3 ]

const s = new Set(b)先用集合把b数组去重,然后a.filter(x => s.has(x))过滤数组a,返回所有存在于s集合中元素组成的数组。

last

Returns the last element in an array.

Use arr.length - 1 to compute the index of the last element of the given array and returning it.

const last = arr => arr[arr.length - 1];

返回数组的最后一个元素。

arr.length - 1去计算一个数组最后一个元素的索引值并返回其对应的数组元素。

➜  code cat last.js
const last = arr => arr[arr.length - 1];

console.log(last([1, 2, 3]));
➜  code node last.js
3

mapObject

Maps the values of an array to an object using a function, where the key-value pairs consist of the original value as the key and the mapped value.

Use an anonymous inner function scope to declare an undefined memory space, using closures to store a return value. Use a new Array to store the array with a map of the function over its data set and a comma operator to return a second step, without needing to move from one context to another (due to closures and order of operations).

const mapObject = (arr, fn) =>
  (a => (
    (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {})
  ))();

把一个数组调用指定的方法后生成一个对象,对象的键是数组的元素值,对象的值是该元素值调用指定方法后生成的结果

使用内部匿名函数定义一个不污染全局变量的命名空间并用闭包存储返回值。使用一个新的数组来存储一个包含arr数组和arr数组去map指定方法后生成的数组。并在前面数组的基础上借助,运算符去一步步的生成所需要的对象。避免了上下文环境context来回转换(得益于闭包和运算顺序)。(这块不是太懂)

➜  code cat mapObject.js
const mapObject = (arr, fn) =>
    (a => (
        (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {})
    ))();

const squareIt = arr => mapObject(arr, a => a * a);
console.log(squareIt([1, 2, 3]));
➜  code node mapObject.js
{ '1': 1, '2': 4, '3': 9 }

逗号运算符会返回最后一个运算表达式运算的结果,如:

const i = 0
i + 1, i // i = 1

即先进行i + 1运算,为1,然后返回i1

以上代码含有两个逗号运算表达式:

((acc[val] = a[1][ind]), acc)

即先做(acc[val] = a[1][ind])这个运算,然后返回acc

第二个是:

(a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {})

即在a = [arr, arr.map(fn)]定义了a的基础上去运用后面的reduce方法,最后返回reduce方法的结果。

可以看出,先定义了一个长度为2的数组a,索引0对应值为数组arr,索引1对应值为数组arr调用fn后的map结果数组。然后去reduce生成以arr元素值为对象键(即val,也即a[0]数组遍历过程中的元素值),以对应该元素值调用fn后的结果值(即a[1][ind])为对象值的对象。这个对象就是最终想要的结果。

一个时间处理库:now.js,觉得还行的话,点个赞再走呗。。。

微信公众号:JavaScript30秒, 从入门到放弃之Array(三)


vue2+node+express+mongodb 初学上手全栈小项目

$
0
0

简介:本文源码:Github

之前刚入门vue并做好了一个简而全的纯vue2全家桶的项目,数据都是本地 json 模拟请求的;详情请移步这里:vue-proj-demo

为了真正做到数据库的真实存取,于是又开始入门了 node+express+mongoose 、并以此来为之前的vue页面写后台数据接口。

代码目录说明:

|--vue-node-proj
    |--client                   //前端vue页面:http://gjincai.github.io/tags/vue/
    |--s1_serverNodeBegin       //《Node入门》学习练习代码,地址:https://www.nodebeginner.org/index-zh-cn.html
    |--s2_serverExpress         //express入门学习练习
    |--s3_Mongodb               //mongodb入门学习练习:http://gjincai.github.io/tags/mongodb/
    |--s4_mongoose              //mongoose入门学习练习:http://gjincai.github.io/tags/mongodb/
    |--s5_server                //express与mongoose整合,实现注册登录的数据在mongodb的存取
    |--server               //前端client页面的正式后台:
        |--api.js               //所有接口
        |--db.js                //数据库初始化、Schema数据模型
        |--index.js             //后台服务启动入口
        |--initCarts.json       //首次连接数据库,购物车数据的初始化
        |--initGoods.json       //首次连接数据库,所有商品数据的初始化
        |--package.json         //安装依赖:express,mongoose

项目说明:

前端:client目录;主要技术:vue-cli + vue2 + vue-router2 + vuex2 + axios + es6 + sass + eslint

后台:server目录;主要技术:express+mongoose

(前后端分离,路由跳转在前端通过 vue-router 控制,后台只负责数据接口)

项目运行:

环境配置:

node.js 与 express 入门:

学习练习代码:参考本项目中的文件夹 vue-node-proj/s1_serverNodeBeginvue-node-proj/s2_serverExpress

mongodb的安装与配置、mongoose的基本使用:

blog学习笔记:http://gjincai.github.io/categories/mongodb/

学习练习代码:参考本项目中的文件夹 vue-node-proj/s3_Mongodbvue-node-proj/s3_Mongodb

运行顺序:

新建命令行窗口1,开启本地mongodb服务:

mongod

新建命令行窗口2,开启本地后台node服务器:

cd vue-node-proj/server
cnpm install --save
node index.js

新建命令行窗口3,开启本地前端vue的dev模式:

cd vue-node-proj/client
cnpm install --save
npm run dev --color

然后在浏览器打开:

localhost:8080

相关学习笔记

express+mongoose 实现简易后台数据接口

效果呈现:

一个nodejs构建的简单的社区

醒来或者吃饱又是一年

$
0
0

终章

本文首发WX公众号【一名打字员】

距离6月26日凌晨12点半的时候写下序篇,到现在已经是2017年12月21日了,电脑上时钟刚好跳到12点30分,心情与外面阳光一样正好。

这一年,偶尔听起民谣时也会晃一晃神儿,想想以前喜欢听的歌手的歌,打开播放器,才听一半就按下停止键,只是因为带入不起来,再没当初那份感慨。

这一年,文化产业的时代。有人说这是戏子当道的时代,怪罪媒体平台,害得人们丢失了信仰存在。

这一年,生活上过的不温不火,工作上忙忙碌碌,思想上浑浑噩噩。每天看着别人如何生活,自己却不知怎么去活。

想想还有几个小时就要结束2017了,难免有点小激动,不知道是想着赶紧结束这毫无作为的一年还是想赶快迎来新的一年好大干一番手脚。往事如过眼云烟,一幕幕在脑海中划过,想起现实中生活的窘迫,又不免心头有点失落。生活如同一块巨石,压的我们每一个人都喘不出气来,我们不是巨人举不起巨石,但是我们也要死撑下来,因为这是我们的时代。

插一句

小谱君很快就会和大家见面了,这是一个很小众的吉他谱网站。整个框架采用微服务的方式,后端采用java+nodejs拆分提供,前端使用egg脚手架进行搭建,有兴趣的朋友可以加微信号【wslongchen】进群交流。上线倒计时网站

1673099227-5a4879ff1da82_articlex.png

vue项目部署问题

$
0
0

对于用一个项目,我在windows或者mac的时候都能使用npm run dev进行启动,并且可以打包,但是到centos的时候,我发现npm run dev的时候就会报错了,image.png

要如何解决?有处理过这个的大神吗?机器环境已经安装了node 8.9.3 和 npm5.5.1 ,cnpm也安装了, 已经把项目中对应的package.json依赖全部安装

koa-jwt在ios端验证问题

$
0
0

这个问题困扰我很多天了,我测试过iOS浏览器可以设置cookie

image.pngimage.png客户端登陆成功后设置cookie,然后使用window.location.href = ‘/’ 跳转首页服务端jwt进行token验证,在pc,安卓都没问题,跳转请求头都会带上cookie但是在iOS端的所有浏览器都没有cookie,浏览器已经开启允许cookie和网站跟踪,没有cookie就会导致验证失败

这是在pc和安卓的请求头

image.png

这是在iOS端的请求头

image.png

为何yarn global list 无法显示已经全局安装的软件包?

$
0
0

npm list -g显示的包很多包的,yarn global list输出却是空的,为何会这样呢?

问一个引入模块的问题

$
0
0

有的引入模块的写法后面有个(),有的后面括号里面还跟个参数 为什么要这样写? 比如引入express不是直接引入就行吗? 为什么要后面跟一个小括号的写法,他们这种写法的意义何在?还有引入socket.io后面为什么要跟一个(http),我测试了一下 貌似不加这个 socket.io就用不了

菜农的问.png


分享一个自用的获取拼音首字母的库

$
0
0

github地址

这是一个量级(~20kb)的Web端获取拼音首字母的库.大部分字都可以测试通过…

前言

在做某个项目的时候要用到了,现在把这个库开源出来。灵感魔改自pinyinjs

hhh

安装

npm install pyfl --save

使用

import pyfl from 'pyfl';

pyfl('喵'); // M

pyfl('好笑吗跟傻子一样整天就知道哈哈哈哈哈哈哈')); // HXMGSZYYZTJZDHHHHHHH

pyfl('罤夶繙着洗'); // TBFZX

pyfl('Pure'); // Pure

pyfl('Made by ❤'); // Made by ❤

作者

pyfl© NBSAW, Released under the MIT License.

Leoric,又一个号称借鉴 Active Record 的 ORM 轮子

$
0
0

Leoric是一个类似 Active Record 的 JavaScript 的 ORM 模块,从一个内部版本中重构出的方案。先前在 reddit.com/r/node发过一则广告帖,要回答的第一个问题应该都是差不多的,就是“Leoric 和市面上现有的 JavaScript ORM 模块相比优势在哪?”

Leoric 非常新,在性能方面测试还不充分,就目前而言,它的优势在于它的 API 非常简单而且强大。例如在编写查询条件时,遇到 ||复合条件的时候,用 mongoose 或者 knex 可能需要这样写:

Table.find({ $or: [ { foo: null }, { foo: values } ] })
// or with knex
Table.where({ foo: null }).orWhere({ foo: values })

用 Leoric 只需:

Table.find('foo = null or foo = ?', values)

如果还要考虑伪删除(不真正 DELETE 记录,而是使用 deleted_at字段标记),在 mongoose 或者 knex 里是:

// MongoDB-like
Table.find({ $and: [ { $or: [ { foo: null }, { foo: values } ] }, { deletedAt: null }]
// formatted
Table.find({
  $and: [
    { 
      $or: [ 
        { foo: null },
        { foo: values }
      ]
    }
  ]
})
// or with knex
Table.where(function() {
  this.where({ foo: null }).orWhere({ foo: values })
}).andWhere({ deletedAt: null })

在 Leoric 里则是:

Table.find('(foo = null or foo = ?) and deletedAt is null', values)

另一个可以体现 Leoric 和其他 ORM 模块区别的地方是关联关系的配置与使用,以 Objection.js 为例,要设置一个 belongsTo关联:

class Animal extends Model {
  static relationMappings = {
    owner: {
      relation: Model.BelongsToOneRelation,
      modelClass: Person,
      join: {
        from: 'animal.ownerId',
        to: 'person.id'
      }
    }
  }
}

在 Leoric 里则是:

class Animal extends Bone {
  static describe() {
    this.belongsTo('owner', { Model: 'Person' })
  }
}

Leoric 是我在 2017 年交出的两份 Node.js 答卷之一,希望能给各位 Web developer 带来帮助。欢迎阅读官方介绍文档、以及 Github

想过深入了解Node.js Buffer 么?一探究竟吧 !

$
0
0

background

你是否像我一样在用Node.js 时候,遇到 Buffer ,Streambinary data时候就发懵?感觉这些是Node.js大佬们或者是npm上开发package的大神才会需要搞的懂的东西?

确实,这些个术语看起来有点吓人,特别是当你没有汇编基础就从事web Node.js开发的时候.

还有比较操蛋的就是现在许多教程将node.js核心部分的讲解以及为他们存在的原由直接跳过,直接教你如用package去开发web应用。甚至有些更臭不要脸的告诉你你不需要太深究这些内容你只需要会用就可以,因为你根本就不会直接和他们打交道。

当然,如果你只是想成为一个普通的node开发者的话,你确实不需要这些。

但是,如果这些神秘的技术使你感到好奇,且好奇心驱使着你探索不止于现状想要成为一个node大神的话,那么你就要深入理解像Buffer类似的node的核心功能模块。这就是我要写这篇文章的目的,就是来帮助你更好的理解这些核心功能,让我们的node.js技术更上一层楼。

在介绍Buffer之前,看下官网的对这部分的描述。。。

​ … mechanism for reading or manipulating streams of binary data. The Buffer class was introduced as part of the Node.js API to make it possible to interact with octet streams in the context of things like TCP streams and file system operations.

嗯哼,除非你能理解上面这段话的所有词的意思。这都是写官方的术语。让我们试着把意思给简化下,描述的更明白点,别被这些花里胡哨的东西给带懵了。从这段描述里,我们可以非常靠谱的说:

Buffer这个存在的意义就是用来操作binary data(二进制数据)streams(流)的 。

怎么样描述够简单了吧?但是,Buffer,binary data,streams这些词……。好吧,我们从后向前一个个的解释。 ## Binary data, 究竟是什么?

你可能已经知道计算机是用二进制形式来存储和表示数据的。二进制就简单的0和1的集合。如下是的5个不同的二进制数据,5个不同的0,1集合:

10 ,01,001,1110,00101011

在二进制里的每一位(1或者0)都叫Bit(比特位)。

为了存储或表示一块数据,计算机需要把对应的数据转成二进制表示形式。例如,计算机要想存储数字12,得把12转成它对应的二进制表示形式 1100

那么计算机是如何知道这种转化呢?好吧,这是纯数学计算了,就是我们在基础数学里面学过的二进制数字计算。计算机是能理解这种数学计算的。

但是我们平时用到的可不止数字啊。还有字符串,图像,视频。那么计算机是如何用二进制表示这些内容的呢?拿字符串来举个例子。计算机是如何把字符串“L” 用二进制形式表示的呢?首先计算机会把字符串L转成数字表示的L.然我们看看它是怎么做到的?

打开浏览器控制台,粘贴如下的代码并按*“回车”."L".charCodeAt(0)。看出现了什么?是76么?对76就是L的对应的字符编码。那么计算机是如何知道什么数字代表每一个字符呢?计算机咋就知道76就是表示的L*?

## Character Sets

Character Sets 定义好了数字和字符之间的对应关系。而且已经有了很多不同的定义规则。最常用的就是UnicodeASCII。JavaScript就很好的诠释了Unicode 。在Unicode 编码里76就是代表了L

由此我们可知计算机是如何用数字表示字符的了。现在就算几也要相应的把76转成二进制表示形式。你可能认为就是把76转成基本的2进制数字嘛,其实并没有那么快。

## Character Encoding

就像那些规则里定义了哪些数字表示了哪些字符一样,这些规则也定义了这些数字应该如何用二进制表示。特别是,用多少比特位来表示这些数字呢?这就叫Character Encoding(字符编码)了。

UTF-8就是其中之一。UTF-8定义了字符用**字节(byte)**来编码。字节就是8比特位(8个1或0)。也就是8个0或者1来表示二进制的任何字符。

为了更易于理解,之前提到的 12用二进制表示就是 1100。当用UTF-8来表示12的时候却是用8比特位,不足8比特位时计算机就会在左侧增加比特位补齐到8位。因此12UTF-8编码的存储结果就是00001100。怎么样,有点感觉了么?

所以呢,76最终存储为01001100

这就是计算机如何用二进制存储字符串或者字符的了,朋友们。

类似的,计算机也定义了如何转化,编码以二进制形式存储图像和视频的了。这里的重点是,计算机将所有数据类型存储在二进制文件中,这就是所谓的二进制数据。

如果你特别对字符编码的幕后原理感兴趣的话,你可能喜欢这个篇文章。 这篇文章里做了详细的介绍.

现在我们也了解了二进制了,那么之前提到的**二进制流(streams of binary data)**是啥呢?

## Stream

Stream在Node.js里就是简单讲就是即将在空间里被移动的一系列数据。完整的概念就是,你要处理许多数据,但是又无需去等待这些数据在此之前全部已经准备好。

就是说把一个大的数据切割成一个个小块(chunk)来发送。原始的Buffer(“二进制streams…在上下文环境… 文件系统”)定义就可以简单理解为文件系统中的被移动的二进制数据。例如吧text1文件中的内容移到text2中。

但是Buffer又是如何帮我们和二进制流数据打交道的呢?顺便说下Buffer准确的来讲是啥呢?

## Buffer

我们已经知道了数据流就是从一点到另一个点移动的数据。那么他们是怎么动的呢?

通常来说,这些移动的数据要么是等待被处理的或者是被读出来或者其他什么决定。但是一定时间里系统处理的数据是有上限和下限的。因此如果数据到达的速率比处理的速率快的话,这些剩下的数据就要去等待进程按序处理。

还有一种情况呢,如果进程处理的速度比数据达到的速度要快的话,之前早到的数据就得等到凑够了一定量的数据才能被送到处理进程里。

这个*“等待区域”就是Buffer*.通常是在计算机RAM中的一个物理地址,里面的数据是在数据流期间暂时收集起来的,被发送给进程处理。

我们可以吧整个的streamBuffer的进程比喻成公共汽车站。有的公共汽车站就是必须要等到乘客到达一定数量或者到了发车时间的时候才可以开车离开。乘客可能是不同时间以不同速度达到车站的,乘客和车站都无法控制乘客的到站时刻。

一种情况是,早到的乘客就得等着车站按照车站的规则发车。还有就是车已经发动或者离开之后才到的乘客就得等下一辆车。

不论哪种情况,都会有等候的地方。这对Node.js来说就是Buffer。Node.js无法控制数据的流的传输速度和数据的到达时间,它只能决定何时能把数据发送出去。如果时机不到,就得把输入存到bufffer(RAM中的一个很小的物理地址)里,直到合适的时间把数据发到处理进程中去。

常见的案例Buffer就是在线视频。如果你的网速足够快的话,视频流的速度就足够快到能充满buffer,发送给处理进程,然后充满下一个,以此类推,直到这个**流(Stream)**处理完毕。

如果你的网速慢的话,处理完已经达到的数据之后,播放器就会出现个loading,这就是说在收集更多的数据或者是等着的数据到达一定数量。只有当Buffer填满了才会交给播放器来处理。播放器才会出内容。在播放的时候,相继的会收到有更多数据,存在Buffer里等着。

如果播放器已经处理完了或者播放之前的内容,这个buffer还没有充满,就会再次出现loading,等着收集更多数据来处理。

这就是Buffer!

在最初的Buffer定义里,只是告诉我们z哎buffer里,我们能操作移动的二进制数据。我们可能会有哪些方式处理底层的二进制数据呢?在Node.js实现的Buffer提供了完整可行的清单。

## Interacting with a Buffer

创建自己的buffer都有可能。Node.js除了在Stream中自动创建Buffer之外呢,我们可以自己造自己的Buffer.牛逼不牛逼不?

根据不同的需求,有多种创造方式。

// Create an empty buffer of size 10. 
// A buffer that only can accommodate 10 bytes.
const buf1 = Buffer.alloc(10);
// Create a buffer with content
const buf2 = Buffer.from("hello buffer");

一旦Buffer建完了,就可以和它交互了。

// Examine the structure of a buffer
buf1.toJSON()
// { type: 'Buffer', data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }
// an empty buffer
buf2.toJSON()
// { type: 'Buffer',
     data: [ 
       104, 101, 108, 108, 111, 32, 98, 117, 102, 102, 101, 114 
     ] 
   }
// the toJSON() method presents the data as the Unicode Code Points of the characters
// Examine the size of a buffer
buf1.length // 10
buf2.length // 12. Auto-assigned based on the initial content when created.
// Write to a buffer
buf1.write("Buffer really rocks!") 

// Decode a buffer
buf1.toString() // 'Buffer rea'
//oops, because buf1 is created to contain only 10 bytes, it couldn't accommodate the rest of the characters
// Compare two buffers

可以和Buffer做更多的操作。在 在官网文档里有更多使用的方法。

最后,给你留一个小小的挑战:去读node.js 核心库之一 zlib.js源代码,看看它是如何借助Buffer强大功能处理二进制数据流的。这些都是gzip文件,当你阅读的时候,记得把所得所感写在评论里与我们分享下。

我希望这篇文章能帮助你更好的理解node.js里面的Buffer.

如果你觉得我做的不错的话,可以帮我在node.js社区里广为扩算下,让更多人看到,更多的人能有个更好的理解。

如果你对此有任何不同的理解,请在评论中指出。

原文摘自 freamcodeCamp

动态变化高度的元素的子元素的height:inherit 不起作用

$
0
0
<html>
	<head>
		<meta charset="UTF-8">
		<style type="text/css">
			.box{ position:relative; height:100px; width:120px; margin-left:10px; float:left; }
			.bar{ height:0; width:100%; background:white; position: absolute; } 
			.bar a{
			display:block;
			height:inherit;
			}
		</style>
	</head>
	<body>
		<div>
			<div class="box">
				<div class="bar"> <a href=""> Create </a> 
				</div>
				<img src="http://p0kqyo0p7.bkt.clouddn.com/14.png?imageView2/1/w/120/h/100" />
			</div>
			<div class="box">
				<div class="bar"> <a href=""> Create </a> 
				</div>
				<img src="http://p0kqyo0p7.bkt.clouddn.com/14.png?imageView2/1/w/120/h/100" />
			</div>
			<div class="box">
				<div class="bar"> <a style=";height:inherit">Create</a> 
				</div>
				<img src="http://p0kqyo0p7.bkt.clouddn.com/14.png?imageView2/1/w/120/h/100" />
			</div>
		</div>
		<script src="https://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
		<script type="text/javascript">
			$(document).ready(function(){
				  
		 $('.box').each(function(){
		var box=this;
			$(this).hover(
		function (){
		 
		  $(box).find('.bar').animate({
		            'height' : '25px'
		        },'fast');
		},
			 function (){
		  $(box).find('.bar').animate({
		            'height' : '0px'
		        },'fast');
		}
		);
			 
		 
				   
			   });
		});
		</script>
	</body>

</html>

期望.bar与 .bar a 的高度同步变化

[fixed] base64 转 png 图片,前后内容有丢失

Parcel 构建 Vue 脚手架 &创建手写数字识别 demo

$
0
0

演示地址

项目地址

演示效果

C$T(VBJHZV)U}_F1(9N7%9D.png

功能说明

  • Parcel 构建 Vue 脚手架并实现手写数字识别效果(鼠标在左侧方框写入数字,点击识别即可)

运行环境

  • node8
  • python3

确保全局安装 parcel-bundler,如果没安装请运行 npm install -g parcel-bundler;确保安装 python3 以及 tensorflow、flask、flask_cors、numpy 模块,python 模块可以通过 pip install 安装。windows 安装python 推荐安装 Anaconda。

本地配置

  • npm install
  • parcel index.html
  • python api.py

线上配置

运行

  • npm install
  • parcel build index.html

nginx 配置

server {
        listen 8080;

        server_name _;

        root /var/www/html/numerical-recognition/dist/; // 项目入口地址

        index index.html index.htm index.php index.nginx-debian.html;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                # try_files $uri $uri/ =404;
                try_files $uri $uri/ /index.html;
        }
}

supervisor 进程守护

python 服务由 Supervisor 启动并维护,设置参数如下:

[program:numerical-recognition]
process_name=%(program_name)s
command=python3 /var/www/html/numerical-recognition/api.py
autostart=true
autorestart=true
user=root
numprocs=1
redirect_stderr=false
stdout_logfile=/var/log/supervisor/numerical-recognition.log

如果启动失败,可能需要执行:unlink /run/supervisor.sock

  • supervisord -c /etc/supervisor/supervisord.conf //起服务,注意 supervisor 配置文件所在目录
  • supervisord shutdown //关闭服务
  • supervisord reload //重启服务

项目布局

.
├── dist                                        // 项目打包路径
├── mnist                                       // 训练模型
│   ├── data                                    // 权重和偏置变量的存储路径
│   ├── convolutional.js                        // 卷积神经网络训练模型
│   ├── model.py                                // 变量和模型的创建
│   └── regression.py                           // 前馈神经网络训练模型
├── src                                         // 前端目录
│   ├── router
│   │   └── index.js                            // 路由配置
│   ├── App.vue                                 // 页面入口文件
│   ├── main.js                                 // 程序入口文件,加载各种公共组件
│   ├── canvas.js                               // 手写数字生成
│   └── MainPage.vue                            // 首页
├── index.html                                  // 入口 html 文件
├── api.py                                      // api 配置文件
├── .babelrc                                    // 设置转码的规则
└── postcss.config.js                           // 转换 CSS 
.

参考

这种系统我该用什么来做啊


错误处理的5种情况总结

$
0
0
  1. 子函数出错后,将错误返回(node中第一个参数是error)或抛出给父函数。必要时,可以添加些上下文信息,如调用参数,错误细节。
  2. 子函数内重试。多用于因不确定因素报错,可重试解决。注意限定超时或重试次数。如经典的指数退避策略。
  3. 父函数处理或记录错误,退出程序。
  4. 父函数处理或记录错误,继续运行。
  5. 罕见情况下,忽略错误。会看起来很奇怪,建议加必要的注释。

tips:

  1. 通常的日志库,有自动添加一致格式时间戳功能、前缀等功能
  2. 日志尽量保持一行,方便grep等处理

总结自《The Go Programming Language》5.4节

egg动态增加路由问题

$
0
0

请问egg有办法动态增加路由吗 求大神指路 目前我的是从数据库直接读取 然后遍历 后面用户动态增加之后每次都需要重新编译才生效。 求更好的方案

koa-tree-router 高性能的 koa 路由

$
0
0

最近有个非常火的框架叫Fastify。它自称自己高性能的杀手锏之一就是使用了他们自己研发的路由find-my-way。这个路由说是利用基数树的数据结构,查找时间为 O(k),比基于正则的路由要快很多。

我好奇的看了一下他们的实现,居然是基于 Golang 里面一个叫做 echo 的框架的路由修改的。而 Golang 的路由实现当中,当属 julienschmidt/httprouter这个项目里面的实现性能最高,比 echo 快 3~5 倍。所以我就做了一个小的实验,基于 httprouter 修改一版基数树的路由,但结果一般:

  node version: v8.9.1, date: Tue Jan 02 2018 09:49:23 GMT+0800 (CST)
  Starting...
  3 tests completed.

  koa-router  x   515,004 ops/sec ±0.39% (92 runs sampled)
  find-my-way x 2,745,259 ops/sec ±0.27% (96 runs sampled)
  tree-router x 7,088,108 ops/sec ±0.50% (96 runs sampled)

只比 find-my-way 快了两倍多,考虑到他们的实现肯定会有为他们的框架妥协的部分,就算改进了,也不会给他们的框架总体带来多大的提升。

其实现在 Node 社区用的最多的还是 express 和 koa。express 的路由是内置的基于正则的路由,应该不会轻易改动,而 koa 是没有基于基数树的路由的,所以我把我实验性的路由改成专门适配 koa 的。

实现了基本功能之后,我决定使用意大利人开发的autocannon试试效果。

意大利炮

autocannon -c 100 -d 40 -p 10 localhost:8080/test

  • koa-router
Stat         Avg     Stdev  Max     
Latency (ms) 2.42    6.43   58      
Req/Sec      40147.2 495.89 40671   
Bytes/Sec    6.14 MB 69 kB  6.29 MB
  • koa-tree-router
Stat         Avg     Stdev  Max     
Latency (ms) 2.09    5.44   45      
Req/Sec      46221.6 718.95 46559   
Bytes/Sec    7.14 MB 152 kB 7.34 MB

可以看出 io 有明显提升。

项目地址

哪个后台模板能根据json的深度,递归的创建标签?

$
0
0

想在后台写树结构的标签,首先要循环判断,json是否还有这个属性,有的话在标签内部在创建一个标签。然后进入json.属性下,在判断是否存在这个属性,在去在标签内部在创建标签。

新手React项目:Material UI版本的cnodejs,已经上线,求star!谢谢

Viewing all 14821 articles
Browse latest View live