Code Monkey home page Code Monkey logo

blog's People

Contributors

damoncy avatar

Stargazers

 avatar

Watchers

 avatar

blog's Issues

二叉树查找

二叉树

二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。

 class BinaryTree {
      constructor() {
        this.root = null;
      }
      node(key) {
        return {
          key: key,
          right: null,
          left: null
        }
      }

      insert(parentNode, key) {
        if (this.root === null) {
          const node = this.node(key);
          return this.root = node;
        }
        // 右子树
        if (key > parentNode.key) {
          if (parentNode.right === null) {
            const node = this.node(key);
            parentNode.right = node;
          } else {
            this.insert(parentNode.right, key);
          }
        } else {
          // 左子树
          if(parentNode.left === null) {
            const node = this.node(key);
            parentNode.left = node;
          } else {
            this.insert(parentNode.left, key);
          }
        }
      }

      getRoot() {
        return this.root;
      }
      
      // 前序遍历
      preTraverse(node, collect) {
        if (node.key !== null) {
          collect.push(node.key);
          if (node.left) {
            this.preTraverse(node.left, collect)
          }
          if(node.right) {
            this.preTraverse(node.right, collect);
          }
        }
      }

      // 中序遍历
      inorderTraverse(node, collect) {
        if (node.left !== null) {
          this.inorderTraverse(node.left, collect)
        }
        if(node.key !==null ) {
          collect.push(node.key);
        }

        if(node.right) {
          this.inorderTraverse(node.right, collect)
        }
      }

      // 后续遍历
      postTraverse(node, collect) {
        if (node.left) {
          this.postTraverse(node.left, collect);
        }
        if(node.right) {
          this.postTraverse(node.right, collect);
        }
        if(node.key) {
          collect.push(node.key);
        }
      }

      // 获取最小值
      minNode(node){
        if(node.left !== null) {
          this.minNode(node.left)
        } else {
          console.log('最小值:', node.key);
        }
      }

      // 获取最大值
      maxNode(node) {
        if(node.right !== null) {
          this.maxNode(node.right)
        } else {
          console.log('最大值:', node.key);
        }
      }

      // 查找指定值
      searchNode(node, key, collect) {
        if(node === null) {
          return false
        }
        if(key < node.key) {
          collect.push(node.key);

          this.searchNode(node.left, key, collect);
        } else if (key > node.key){
          collect.push(node.key);

          this.searchNode(node.right, key, collect)
        } else {
          collect.push(node.key);
        }
      }
    }
    const binaryTrees = new BinaryTree()
    const nodes = [8, 3, 10, 1, 5, 14, 4, 6, 13];
    nodes.forEach(key => {
      // (为了方便理解)每次将新数据插入到 最新的树(binaryTress.getRoot())中
      binaryTrees.insert(binaryTrees.getRoot(), key)
    });
    // 树
    console.log(`当前树:`, binaryTrees.getRoot())
    // 前序遍历
    const preMap = [];
    binaryTrees.preTraverse(binaryTrees.getRoot(), preMap);
    console.log(`前序遍历: ${preMap}`)
    // 中序遍历
    const inorderMap = [];
    binaryTrees.preTraverse(binaryTrees.getRoot(), inorderMap);
    console.log(`中序遍历: ${inorderMap}`)
    // 后续遍历
    const postMap = [];
    binaryTrees.postTraverse(binaryTrees.getRoot(), postMap);
    console.log(`后序遍历: ${postMap}`)

    // 最小值
    binaryTrees.minNode(binaryTrees.getRoot())
    // 最大值
    binaryTrees.maxNode(binaryTrees.getRoot())

    // 查找指定值
    const searchCollect = [];
    const targetKey = 4;
    binaryTrees.searchNode(binaryTrees.getRoot(), targetKey, searchCollect);
    // 查找路径中最后一个值为指定值,则存在
    console.log(`查找指定值: ${targetKey},结果:${searchCollect.unshift() === targetKey ? '存在': '不存在'},查询路径:${searchCollect}`)

详细测试代码

docker

通过docker搭建虚拟机(容器)

两个基本概念容器与镜像:

  • 容器(container)
    • 解释: 容器类似于虚拟机,容器中运行着一个完整的系统。可以在容器中安装你想要的的一切,比如 node、npm install 等
  • 镜像(image)
    • 解释:镜像是个文件,类似于安装包。我们需要这个安装包安装容器。

安装docker

容器是安装在docker环境中的。所以需要先安装docker。

注意:如果是windows电脑,需要购买支持虚拟化的版本,如Win10专业版,Win10家庭版是不行的。

生成一个container(类似虚拟机)过程

三步得到一个虚拟机container

  • 1、Dockerfile配置文件(定制化一个镜像)
  • 2、根据dockerfile打包生成镜像(即container的安装包)
  • 3、根据镜像运行container(即根据安装包安装虚拟机)

任务: 安装一个有node的“虚拟机“

我们安装上述三个步骤来完成:

1、Dockerfile配置文件(定制化一个镜像)

# Dockerfile文件 内部使用的是shell命令
# 使用指定版的node镜像
FROM node:10

# 程序运行的端口
EXPOSE 8888

2、根据Dockerfile配置生成镜像(安装包)

执行命令:

docker image build ./ -t hello-docker:1.0.0

解释:docker image build ./ -t hello-docker:1.0.0基于 ./当前目录打包一个镜像,镜像的名字叫 hello-docker,镜像的版本是1.0.0,该命令会根据当前目录下的Dockerfile生成镜像

命令执行完成后会看到如下的信息:

![image-20191218221304757](/Users/wuxi/Library/Application Support/typora-user-images/image-20191218221304757.png)

根据镜像运行container(即根据安装包安装虚拟机)

查看我们当前创建的镜像:(所有镜像通过docker统一管理的)

docker image -a // -a 展示所有

![image-20191218222139509](/Users/wuxi/Library/Application Support/typora-user-images/image-20191218222139509.png)

运行镜像:

docker run -t -i 9378fafcdb31 /bin/bash 

![image-20191218223827758](/Users/wuxi/Library/Application Support/typora-user-images/image-20191218223827758.png)

docker run -t -i 9378fafcdb31 /bin/bash 执行了

/bin/bash 启动了容器的/bin/bash,此时通过bash shell 执行命令。

npm-delegates

delegates

介绍

delegates是由TJ大神开发的,可以方便快捷的使用设计模式中的委托模式,即外层暴露对象将请求委托给内部对象进行处理。

使用

基本委托方法:

  • getter:外部对象可以直接访问内部对象的值
  • setter: 外部对象可以直接修改内部对象的值
  • access: 包含getter和setter的功能
  • method: 外部对象可以直接调用内部对象的值
const delegates = require('delegates');

const animal = {
  cat: {
    name: '花花',
    age: 1,
    address: '北京',
    weight: 1,
    say() {
      console.log('你好 花花');
    }
  }
}

delegates(animal, 'cat')
  .getter('name')
  .setter('age')
  .access('address')
  .method('say')
  .fluent('weight')

 
// getter 只能获取  
console.log(animal.name); // 花花

// getter 不能被设置属性
animal.name = '花花2'; 
console.log(animal.name); // 赋值 花花2 不成功,仍然输出:花花

// setter
console.log(animal.age) // undefined  setter只能赋值操作,所以读取值的时候为undefined

// access
animal.address = '重庆'; // 使用了access方法,既能 赋值也能 读取值(赋值animal.address时代理到了animal.cat.address)
console.log(animal.address); // 重庆
console.log(animal.cat.address); // 重庆

// method
animal.say(); // 你好 花花 (相当于执行了 animal.cat.say方法)
animal.cat.say(); // 你好 花花

// fluent方法,可以通过函数的方式设置属性值
console.log(animal.weight(2).weight()) // 2
console.log(animal.weight) // [Function] fluent代理成了一个方法
console.log(animal.cat.weight) // 2

console.log(animal);
/*{ cat: 
   { name: '花花',
     age: 1,
     address: '重庆',
     weight: 2,
     say: [Function: say] },
  name: [Getter],
  age: [Setter],
  address: [Getter/Setter],
  say: [Function],
  weight: [Function] }*/

示例结合源代码分析

1、getter

实际使用的是对象原型的 __defineGetter__方法

delegates(animal, 'cat')
  .getter('name')

// 相当于
Delegator.prototype.getter = function(name){

  animal.__defineGetter__(name, function(){
    return this.cat[name];
  });
};

通过__defineGetter__在animal上定义getter方法,相当于在animal上添加了一个getter方法,通过getter方法代理去访问animal.cat上的属性

/*
  此时
  animal = {
  	cat: {
  	  name: '花花'
  	},
  	name: [Getter], // 在外层添加getter方法,实际访问的还是内部的cat.name
  }
*/

现在一般都不使用 __defineGetter__去定义getter方法,可以直接通过get定义,如下方式

/*
  此时
  animal = {
  	cat: {
  	  name: '花花'
  	},
  	get name() {
  		return this.cat.name // 
  	},
  }
*/

2、setter

delegates(animal, 'cat')
   .setter('age')

// 相当于
Delegator.prototype.setter = function(name){

  animal.__defineSetter__(name, function(val){
    return this.cat[name] = val;
  });
};

使用 对象的原型方法 __defineSetter__与getter类似

3、access

相当于同时给代理对象添加getter、setter方法代理访问目标对象

Delegator.prototype.access = function(name){
  return this.getter(name).setter(name); // 在放回的this上再添加setter方法
};

4、fluent

给代理对象添加方法(方法为目标属性的名)(与getter setter不同)

delegates(animal, 'cat')
	.fluent('weight')

Delegator.prototype.fluent = function (name) {
	var proto = this.proto;
        var target = this.target;
        this.fluents.push(name);
       proto[name] = function(val){
           if ('undefined' != typeof val) {
           this[target][name] = val;
           return this;
        } else {
      return this[target][name];
    }
  };

  return this;
};

/*
animal = {
    cat:  {
	weight: 1
    },
   weight(val) {
     if ('undefined' != typeof val)  {
            this.cat.weight = val;
            return this;
       } else {
          return this.cat.weight;
       }
    }
   }
*/

child_process创建子进程

创建子进程

child_process创建子进程

  • 异步方式:

    • spawn

    • exec

    • execFile

    • fork

  • 同步方式

    • spawnSync
    • execSync
    • execFileSync

spawn方法

child_process.spawn(command[, args][, options])

command: 要执行的指令
args:    传递参数
options: 配置项
const {spawn} = require('child_process');
const child = spawn('ls');

执行时控制台并没输出相关信息,这是为什么呢?

  • 子进程有自己的stdio流(stdin, stdout, stderr),在执行子进程时,当前进程是看不到子进程的输出信息。
  • 控制台的输出与当前进程的stdio绑定的。(所以当前进程中的控制台不能输出子进程的内容,不在一个进程)

子进程与当前进程建立关联

1、如果希望看到输出信息,可以通过子进程的stdout与当前进程的stdout之间建立管道实现。

child.stdout.pipe(process.stdout)

2、可以通过监听事件的方式(子进程的stdio流实现了EventEmitter API,所以可以通过监听事件的方式)

// 监听子进程stdio流
child.stdout.on('data', function(data) {
	process.stdout.write(data);
})

3、还可以通过子进程和当前进程公用stdio流的方式实现

const child = spawn('ls', {
	stdio: 'inhrite'
})

stdio选项用户配置父进程和子进程之间建立的管道,由于stdio管道有三个(stdin、stdout、stderr)因此stdio的三个可能的值是数组的简写。

  • pipe 相当于: ['pipe', 'pipe', 'pipe'] 默认值
  • ignore 相当于:['ingore', 'ingore', 'ingore']
  • inhrite 相当于: [process.stdin, process.stdout, process.stderr] (相当于直接使用父进程stdio)

exec方法

如果我们执行 spawn('ls -a') 就会报错,因为ls -a相当于创建了连个spawn命令方式。可以使用 exec创建子shell,所以可以直接执行shell管道命令。spawn采用流的方式来输出命令的执行结果,而exec是将命令的执行结果缓存起来统一放在回调函数的参数里,因此exec只适用于命令执行结果数据小的情况

spawn也可以通过配置shell option的方式来创建子shell进而支持管道命令:

const { spawn } = require('child_process');
const child = spawn('ls -l | wc -l', {
  shell: true
})
child.stdout.pipe(process.stdout);

Options参数说明:

  • cwd 更改命令的执行目录

  • env用于指定子进程的环境变量(如不指定的化,默认获取当前进程的环境变量)

    // test.js
    // 指定env环境变量,会覆盖默认的环境变量
    spawn('echo $NODE_TEST $NODE_ENV', {
      shell: true,
      stdio: 'inhrit',
      cwd: '/usr',
      env: {
        NODE_TEST: 'abc'
      }
    })
    
    // 执行
    $: NODE_ENV=123 node test.js
    // 输出结果 abc
  • detachead 用于将子进程与父进程断开连接

execFile

execFile与exec不同,execFile通常用于执行文件,而且并不会创建子shell环境

fork

fork方法是spawn方法的一个特例,fork用于执行js文件创建Node.js子进程。而fork方式创建的子进程与父进程之间建立了IPC通信管道,因此子进程和父进程之间可以通过send的方式发送消息。

(fork方式创建的子进程与父进程是完全独立的,它拥有单独的内存,单独的V8实例,因此不推荐创建很多的Node.js子进程)

fork方式的父子进程之间的通信:

// parent.js
const { fork } = require('child_process')
const forked = fork('child.js');

forked.on('message', (msg) => {
  console.log('Message from child', msg);
})
forked.send({ name: 'damon' })
// child.js
process.on('message', (msg) => {
  console.log('Message from parent:', msg);
})

let counter = 0;
setInterval(() => {
  process.send({ counter: counter++ });
}, 1000);
node parent.js

// 输出结果
Message from parent: { hello: 'world' }
Message from child { counter: 0 }
Message from child { counter: 1 }
Message from child { counter: 2 }
Message from child { counter: 3 }
Message from child { counter: 4 }
Message from child { counter: 5 }

监听进程事件

前述的几种方式创建的子进程都是实现了EventEmitter, 因此可以对进程进行监听

常用的事件:

  • close: 当子进程的stdio流关闭的时候触发,

  • exit :子进程结束时触发

  • error:

    • 无法创建子进程

    • 无法结束进程

    • 给进程发送消息失败

      当代码执行出错的时候,error事件并不会触发,exit事件会触发,code为非0的异常退出码

  • message

close与exit事件的回调函数有两个参数code和signal, code代表子进程最终的退出码。如果子进程是由于接收到signal信号终止的话,signal会记录子进程接受的signal值。

const { exec } = require('child_process');
const child = exec('ls -l', {
  timeout: 300
});
child.on('exit', function(code, signal) {
  console.log(code);
  console.log(signal);
});

// 正常退出  输出结果
0
null

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.