ryli / blog Goto Github PK
View Code? Open in Web Editor NEW💡
💡
介绍两个 node 写的程序,可以通过 npm i -g saikou-cli
安装来感受一下。
首先进入开发目录,开始创建项目.
# 创建目录
mkdir baka-cli
cd baka-cli
# 初始化项目
npm init -y
完成后会多一个 package.json
文件。
修改 package.json
文件,添加 bin
属性,属性值为执行的 js 入口文件。
// 如果和包名相同且只有一个命令可以直接写路径字符串
{
"bin": "./index.js",
}
// 对象形式可以自定义名称,也可以一次定义多个命令
{
"bin": {
"baka": "./baka.js",
"senkun": "./senkun.js"
},
}
package.json
文件里的 description
和 version
属性也是程序需要使用的。
创建 index.js
文件。
在文件的第一行添加以下代码,告诉命令行,找到本命令后用 node
来执行本文件。
#!/usr/bin/env node
然后再加入一些简单的代码。
// 第一个命令
main()
function main() {
console.log('hello world!')
}
之后就可以开始本地测试啦。
# 使用如下命令让系统识别我们的程序
npm link
# 测试
baka
# expected output: hello world!
看到输出 hello world!
,最简单的命令就完成了。
正常使用的命令都有一些支持的参数如 -h
, -v
等,简单来实现一下。
// 获取信息
const { name, version, description } = require('./package.json')
const appName = name.replace('-cli', '')
// app 支持的参数列表
const app = {
'h': showHelp,
'v': showVersion,
}
main()
// 修改 main 方法
function main() {
// 获取参数
// 有效参数从第三个开始,前两个是 node 和 index.js
const args = process.argv.slice(2)
// 获取操作符,去掉 '-'
const operation = args[0].substr(1)
// 执行程序
app[operation]()
}
function showHelp() {
const help = `${name} ${description}
usage: ${appName} [options] [arguments]
options:
-h: show help
-v: show version
`
console.log(help)
}
function showVersion() {
console.log(version)
}
保存代码后可以测试一下。
简单完成一个 todoList 功能。
创建 todo.json
文件来保存数据。
再次修改代码。
const fs = require('fs')
const path = require('path')
const { name, version, description } = require('./package.json')
const todoList = require('./todo.json')
const appName = name.replace('-cli', '')
const app = {
'c': create,
'd': done,
'r': remove,
's': reset,
'l': listAll,
'h': showHelp,
'v': showVersion,
}
main()
function main() {
// 有效参数从第三个开始
const args = process.argv.slice(2)
// 默认输出列表
if (!args.length) {
listAll()
return
}
// 获取操作符,支持简写和单词
const operation = args[0].length === 2 ? args[0].substr(1) : args[0].substr(2, 1)
// 参数错误则展示帮助
if (!args[0].startsWith('-') || !operation || !Object.keys(app).includes(operation)) {
showHelp()
return
}
app[operation](args[1])
}
function create(job) {
const index = todoList.todo.indexOf(job)
const doneIndex = todoList.done.indexOf(job)
if (index === -1 && doneIndex === -1) todoList.todo.push(job)
save()
}
function done(job) {
let index = todoList.todo.indexOf(job)
if (index > -1) todoList.todo.splice(index, 1)
index = todoList.done.indexOf(job)
if (index === -1) todoList.done.push(job)
save()
}
function remove(job) {
let index = todoList.todo.indexOf(job)
if (index > -1) todoList.todo.splice(index, 1)
index = todoList.done.indexOf(job)
if (index > -1) todoList.done.splice(index, 1)
save()
}
function reset() {
todoList.todo = []
todoList.done = []
save()
}
function listAll() {
const todo = todoList.todo.length ? '☐ ' + todoList.todo.join('\n ☐ ') : ''
const done = todoList.done.length ? '✔ ' + todoList.done.join('\n ✔ ') : ''
const info = `Todo:
----------
${todo}
Done:
----------
${done}
`
console.log(info)
}
function showHelp() {
const help = `${name} ${description}
usage: ${appName} [options] [arguments]
options:
-c, --create: create job
-d, --done: mark that the job is done
-r, --remove: remove job
-s, --reset: reset the list
-l, --list: list all job
-h, --help: show help
-v, --version: show version
`
console.log(help)
}
function showVersion() {
console.log(version)
}
function save() {
const file = path.resolve(__dirname, './todo.json')
const data = JSON.stringify(todoList)
fs.writeFile(file, data, 'utf8', (err) => {
if (err) {
console.error(err)
} else {
listAll()
}
})
}
看一下效果。
建议添加一个 README.md
文件,来简单说明一下本程序。
touch README.md
echo '# baka-cli' > README.md
可以通过以下 npm 包来轻松的用 node 开发命令行程序。
完。
有如下数据结构:
const source = [
{ id1: 1, label1: '1-1', id2: 1, label2: '2-1', id3: 1, label3: '3-1' },
{ id1: 1, label1: '1-1', id2: 2, label2: '2-2', id3: 2, label3: '3-2' },
{ id1: 1, label1: '1-1', id2: 2, label2: '2-2', id3: 3, label3: '3-3' },
{ id1: 2, label1: '1-2', id2: 1, label2: '2-1', id3: 1, label3: '3-1' },
{ id1: 2, label1: '1-2', id2: 1, label2: '2-1', id3: 2, label3: '3-2' },
{ id1: 2, label1: '1-2', id2: 1, label2: '2-1', id3: 3, label3: '3-3' },
{ id1: 2, label1: '1-2', id2: 2, label2: '2-2', id3: 1, label3: '3-1' },
{ id1: 2, label1: '1-2', id2: 2, label2: '2-2', id3: 2, label3: '3-2' },
{ id1: 2, label1: '1-2', id2: 2, label2: '2-2', id3: 3, label3: '3-3' },
{ id1: 2, label1: '1-2', id2: 2, label2: '2-2', id3: 4, label3: '3-4' },
]
要求得到:
[
{
"id": 1,
"label": "1-1",
"children": [
{
"id": 1,
"label": "2-1",
"children": [
{
"id": 1,
"label": "3-1"
}
]
},
{
"id": 2,
"label": "2-2",
"children": [
{
"id": 2,
"label": "3-2"
},
{
"id": 3,
"label": "3-3"
}
]
}
]
},
{
"id": 2,
"label": "1-2",
"children": [
{
"id": 1,
"label": "2-1",
"children": [
{
"id": 1,
"label": "3-1"
},
{
"id": 2,
"label": "3-2"
},
{
"id": 3,
"label": "3-3"
}
]
},
{
"id": 2,
"label": "2-2",
"children": [
{
"id": 1,
"label": "3-1"
},
{
"id": 2,
"label": "3-2"
},
{
"id": 3,
"label": "3-3"
},
{
"id": 4,
"label": "3-4"
}
]
}
]
}
]
要求把原始对象组成的数组,转换成层级结构的数组。比较典型的应用是【省-市-区】这样的数据,比如一份省市区的 Excel 文件,使用 node 的一些 csv 的包,简单处理就可以得到最初的数据结构。然后转换成前端渲染时需要的结构,即题目要求的形式。
下面我的解题方法:
// 思路是判断数据是否已存在,不存在则新建元素。需要注意的是,每次要返回已创建或者找到的父元素
function convert(source) {
const target = []
// 生成基本的数据结构
const createNode = (id, label) => ({ id, label })
// 创建 root 对象
const createRoot = (node, list) => {
const isExistList = list.filter(item => item.id === node.id)
if (!isExistList[0]) list.push(node)
// 返回创建或已存在的数据
return isExistList[0] || node
}
// 插入数据到父节点
const insertNode = (node, parent) => {
if (!parent.children) parent.children = []
return createRoot(node, parent.children)
}
// 开始处理
source.map(row => {
const keys = Object.keys(row)
let root
// 每次 +2,跳过 label 列
for (let i = 0; i < keys.length; i += 2) {
// 创建本次遍历的数据
const node = createNode(row[keys[i]], row[keys[i + 1]])
if (i === 0) {
root = createRoot(node, target)
} else {
root = insertNode(node, root)
}
}
})
return target
}
// 测试方法
console.log(JSON.stringify(convert(source), ' ', 4))
另外还有 H 同学的解题方法:
function convert(source) {
const output = []
source.forEach(row => {
const keys = Object.keys(row)
// 记录节点在节点树上的完整路径
const path = [0]
for (let i = 0; i < keys.length;) {
const id = row[keys[i++]]
const label = row[keys[i++]]
const targetIndex = id
path.push(targetIndex)
path.reduce((parentNode, node, index) => {
const isCurrentLastNode = path.length - 1 === index
if (isCurrentLastNode) {
if (!parentNode[node]) parentNode[node] = [ label, []]
}
return parentNode[node][1]
}, [[null, output]])
}
})
// [string, arr] => {id: index, label: string, children: arr}
const result = output.reduce(function mapper(acc, v, i) {
if (v) {
const [label, children] = v
acc.push({
id: i,
label,
children: children.reduce(mapper, [])
})
}
return acc
}, [])
// print(output, 1)
}
hello world!
当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 __proto__
)指向它的构造函数的原型对象( prototype
)。该原型对象也有一个自己的原型对象( __proto__
) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
const o = {}
// --> true
Object.getPrototypeOf(o) === Object.prototype
Object.getPrototypeOf(Object.prototype) === null
o.__proto__ === Object.getPrototypeOf(o)
o.__proto__ === Object.prototype
o.__proto__ === o.constructor.prototype
o.__proto__.__proto__ === null
几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
遵循ECMAScript标准,someObject.[[Prototype]] 符号是用于指向 someObject 的原型。从 ECMAScript 6 开始,[[Prototype]] 可以通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性
__proto__
。
但它不应该与构造函数 func 的 prototype 属性相混淆。被构造函数创建的实例对象的 [[prototype]] 指向 func 的 prototype 属性。Object.prototype 属性表示 Object 的原型对象。
// o 这个对象继承了 Object.prototype 上面的所有属性
// 原型链如下:
// o ---> Object.prototype ---> null
var o = {a: 1};
// a ---> Array.prototype ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// f ---> Function.prototype ---> Object.prototype ---> null
function f(){
return 1;
}
在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符
来作用这个函数时,它就可以被称为构造方法(构造函数)。
function Graph() {
this.vertices = [];
this.edges = [];
}
Graph.prototype = {
addVertex: function(v){
this.vertices.push(v);
}
};
var g = new Graph();
// g 是生成的对象,他的自身属性有 'vertices' 和 'edges'。
// 在 g 被实例化时,g.[[Prototype]] 指向了 Graph.prototype。
new关键字与构造函数一起使用来创建对象。
下面看看例子:
function Employee(name, position, yearHired) {
this.name = name;
this.position = position;
this.yearHired = yearHired;
};
const emp = new Employee("Marko Polo", "Software Developer", 2017);
new关键字做了4件事:
根据上面描述的,它将首先创建一个空对象{},然后它将this值赋给这个空对象this={},并向这个对象添加属性。因为我们没有显式的return语句,所以它会自动为我们返回this。
var a = {a: 1};
// a ---> Object.prototype ---> null
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)
var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null
var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype
class 只是一个语法糖,JavaScript 仍然基于原型。
class Foo {
constructor(name) {
this.name = name
}
}
// 当执行
var o = new Foo();
// 实际上执行的是(或者类似这样):
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);
// 当执行
o.someProp
// 它会检查 o 是否具有 someProp 属性。如果没有,它会查找 Object.getPrototypeOf(o).someProp
// 如果仍旧没有,它会继续查找 Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp
hasOwnProperty 是 JavaScript 中一个处理属性并且不会遍历原型链的方法。(另一种这样的方法:Object.keys())
function A 有一个叫做 prototype 的特殊属性。该特殊属性可与 JavaScript 的 new 操作符一起使用。对原型对象的引用被复制到新实例的内部 [[Prototype]] 属性。例如,当执行 var a1 = new A(); 时,JavaScript(在内存中创建对象之后,和在运行函数 A() 把 this 指向对象之前)设置 a1.[[Prototype]] = A.prototype;
执行 a1.doSomething() 相当于执行 Object.getPrototypeOf(a1).doSomething.call(a1)
简而言之, prototype 是用于类的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致。
function A(a){
this.varA = a;
}
// 重点:类型被定义在 .prototype 中
A.prototype = {
// A.prototype.varA 总是会被 this.varA 遮蔽,如果varA并不是在每个实例中都被初始化,那这样做将是有效果的
varA : null,
doSomething : function(){
// ...
}
}
function B(a, b){
A.call(this, a);
this.varB = b;
}
// 重点:用 Object.create() 来继承
B.prototype = Object.create(A.prototype, {
varB : {
value: null,
enumerable: true,
configurable: true,
writable: true
},
doSomething : {
value: function(){ // override
A.prototype.doSomething.apply(this, arguments);
// call super
// ...
},
enumerable: true,
configurable: true,
writable: true
}
});
B.prototype.constructor = B;
var b = new B();
b.doSomething();
prototype 是用于类的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致.
Object.getPrototypeOf(fn) === fn.__proto__ === Function.prototype
// true
Object.prototype.__proto__ === null
Object.constructor.__proto__ === Object.constructor.prototype
Object.__proto__ === Object.constructor.prototype
({}).__proto__ === Object.prototype
Array.__proto__ === Object.constructor.prototype
Array.prototype.__proto__ === Object.prototype
Function.__proto__ === Object.constructor.prototype
Array.prototype本身就是一个数组,并且它的长度为0
console.log(Array.prototype)
// [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
Array.prototype.length === 0
编程有三等境界 (狼叔):
- 测试驱动开发,在写代码前先写测试
- 是断点调试,可以直观的跟踪代码执行逻辑、调用栈
- 打日志,简单略显低级
本文准备介绍 VSCode 和 Node 的调试方式。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.