Code Monkey home page Code Monkey logo

clearives.github.io's People

Contributors

clearives avatar traviscibot avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

clearives.github.io's Issues

sublime . eslintignore can not work

this is the correct way to do this:
edit /Users/zengxianghui/Library/Application Support/Sublime Text 3/Packages/User/SublimeLinter.sublime-settings

"eslint": {
  "@disable": true,
  "args": [],
  "excludes": [
    "**/node_modules/**",
    "**/build/*.js",
    "**/config/*.js",
    "**/server/**"
  ]
}

koa中加版本号

1、koa中加版本号

app.use(koaManifestRev({
  manifest: path.join(__dirname, env === 'pro' ? 'build': '', 'rev-manifest.json'),
  prepend: '/'
}));

注意这代码必须加在路由前,不然不生效

BASE64加/解密

BASE64加/解密

js-base64实现

在我们React项目中,我们接受到BASE64的字符串,我们需要解密,我们想到的是第三方模块js-base64

实现方式

/*"use strict";*/
const BASE64 = require('js-base64').Base64;

function formatStatus (status) {
  var decoder = BASE64.decode(status);
  var resultObj = JSON.parse(decoder);
  return resultObj;
}

let result = formatStatus('e2E6MSxiOjJ9')   // {a:1,b:2}

node实现

Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。

实现方式

/*"use strict";*/
let obj = {a:1,b:2};
//  编码
let base64Str = new Buffer(obj).toString('base64');
//  解码
let result = new Buffer(base64Str, 'base64').toString(); // {a:1,b:2}

css3动画transform后链接不能点击跳转,点击事件也无效

css3的动画 animation属性,如果动画里面需要动画的元素是transform,那么动画的这个元素及其子元素是不能运行点击事件,如果有a标签,也是不能跳转链接的。是transform属性改变了元素的层级,也就是z-index属性。

解决办法

在兄弟节点上加一个隐藏的元素,所有属性一样,通过这个元素控制事件

Node.js 中的模块循环依赖

Node.js 中的模块循环依赖

modules_cycles

When there are circular require() calls, a module might not have finished executing when it is returned.

当有循环的require调用时,模块返回时可能尚未完成它的执行

// a.js
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
// b.js
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
// main.js
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);

result:
result.png

When main.js loads a.js, then a.js in turn loads b.js. At that point, b.js tries to load a.js. In order to prevent an infinite loop, an unfinished copy of the a.js exports object is returned to the b.js module. b.js then finishes loading, and its exports object is provided to the a.js module.

翻译:当 main.js 加载 a.js 时, a.js 又加载 b.js。 此时, b.js 会尝试去加载 a.js。 为了防止无限的循环,会返回一个 a.js 的 exports 对象的 未完成的副本 给 b.js 模块。 然后 b.js 完成加载,并将 exports 对象提供给 a.js 模块。
简单的说就算,为了防止模块载入的死循环,Node.js在模块第一次载入后会把它的结果进行缓存,下一次再对它进行载入的时候会直接从缓存中取出结果,所以在这种循环依赖情形下,不会有死循环。

为什么不会无限循环引用分析

执行node main.js :

  1. require('./a.js');此时会调用 self.require(), 然后会走到module._load,在_load中会判断./a.js是否被load过,当然运行到这里,./a.js还没被 load 过,所以会走完整个load流程,直到_compile。
  2. 运行./a.js,运行到 exports.done = false 的时候,给 esports 增加了一个属性。此时的 exports={done: false}。
  3. 运行require('./b.js'),同 第 1 步。
  4. 运行./b.js,到require('./a.js')。此时走到_load函数的时候发现./a.js已经被load过了,所以会直接从_cache中返回。所以此时./a.js还没有运行完,exports = {done.false},那么返回的结果就是 in b, a.done = false。
  5. ./b.js全部运行完毕,回到./a.js中,继续向下运行,此时的./b.js的 exports={done:true}, 结果自然是in main, a.done=true, b.done=true。

其它

虽然缓存解决了死循环的问题,但是大部分情况下我们不会这么写,而且这并没有解决正确导入模块的问题,所以,接下来看import,export📚。

参考

  1. modules_cycles

RN样式问题

布局相关

  1. react 宽度基于pt为单位, 可以通过Dimensions 来获取宽高,PixelRatio 获取密度。

  2. 基于flex的布局

  • view默认宽度为100%
  • 水平居中用alignItems, 垂直居中用justifyContent
  • 基于flex能够实现现有的网格系统需求,且网格能够各种嵌套无bug

3.图片布局

  • 通过Image.resizeMode来适配图片布局,包括contain, cover, stretch
  • 默认不设置模式等于cover模式
  • contain模式自适应宽高,给出高度值即可
  • cover铺满容器,但是会做截取
  • stretch铺满容器,拉伸

4.定位

  • 定位相对于父元素,父元素不用设置position也行
  • padding 设置在Text元素上的时候会存在bug。所有padding变成了marginBottom

5.文本元素

  • 文字必须放在Text元素里边
  • Text元素可以相互嵌套,且存在样式继承关系
  • numberOfLines 需要放在最外层的Text元素上,且虽然截取了文字但是还是会占用空间

我一直不理解闭包(直到有人这样跟我解释)

翻译文章信息

正文

123

正如标题所述,JavaScript中的闭包对我来说一直很迷惑。我读过很多篇文章,工作中也使用过闭包,有时候我甚至不知道自己有用到闭包。

最近我参与了一个关于解释闭包的交流,最终点醒了我。下面我将采用这种方式在这篇文章中来解释闭包,让我赞扬Codesmith的优秀人才及他们的JavaScript The Hard Parts系列。

开始之前

在你理解闭包之前,有一些概念很重要。其中之一是执行上下文(execution context)。
这篇文章是对执行上下文的一个很好的入门,下面引用它:

当代码在JavaScript中运行时,它的执行环境非常重要,执行环境为以下情况的一种:
全局代码 —— 代码首次载人执行的默认环境
函数代码 —— 每次代码执行进入函数体内
(…)
(…)让我们把执行上下文当做每次代码执行的环境/作用域

换句话说,每当我们启动程序,我们从全局执行上下文开始。一些变量在全局上下文下声明,我们称之为全局变量。当程序调用函数时,会发生什么呢?主要是以下几个步骤:

  1. JavaScript创建一个新的执行上下文,一个函数执行上下文。
  2. 该执行上下文有其自己的变量集,这些变量将是该执行上下文的本地变量。
  3. 新的执行上下文进入(push)到执行栈上(execution stack),将执行栈视为一种机制来跟踪程序在执行中的位置。

函数什么时候结束?当遇到return语句或遇到结束括号时}。当函数结束时,会发生以下情况:

  1. 当前执行上下文退出执行栈(pops)。
  2. 函数将返回值发送回调用上下文,这个调用上下文是调用此函数的执行上下文,它可以是全局执行上下文或其它执行上下文,由调用执行上下文来处理该点的返回值,返回的值可以是对象,数组,函数,布尔值,任何值,如果函数没有return,将返回undefined。
  3. 当前执行上下文销毁,这很重要,我们在当前执行上下文定义的变量将被删除,他们不能再被使用,这就是我们为什么称之为局部变量。

一个很经典的例子

在我们开始讲闭包前,让我们看一下下面这段代码。这看起来非常简单,任何读这篇文章的人都可能确切知道它的作用。

let a = 3;
function addTwo(x) {
  let ret = x + 2;
  return ret;
}
let b = addTwo(a);
console.log(b);

为了理解JavaScript引擎是如何工作的,下面让我们详细分析它。

  1. 在第1行,我们在全局执行上下文中声明一个新变量a,并为其赋值为3。
  2. 接下来它变得棘手。第2到第5行在一起。这里发生了什么?我们在全局执行上下文中声明了一个名为addTwo的新变量。我们分配给它的是什么?函数定义。两个括号{}之间的任何内容都分配给addTwo。函数内部的代码不会被评估,也不会执行,只是存储到变量中以备将来使用。
  3. 现在我们来到第6行。它看起来很简单,但这里有很多需要拆解的。首先,我们在全局执行上下文中声明一个新变量,并将其标记为b。声明变量后,它的值为undefined。
  4. 接下来,仍然在第6行,我们看到一个赋值运算符。我们正准备为变量b分配一个新值。接下来我们看到一个被调用的函数。当你看到一个变量后跟圆括号(...)时,这就是调用函数的信号。说回来,每个函数都会返回一些东西(值,对象或undefined)。从函数返回的任何内容都将分配给变量b。
  5. 首先我们需要调用addTwo函数,JavaScript将在其全局执行上下文中查找名为addTwo的变量。oh,发现了,它定义在2-5行,变量addTwo是一个函数定义,a是通过参数传递到函数中去的,我们在全局环境中寻找a,发现a为3,我们将3赋值给函数的参数(argument),准备执行函数。
  6. 现在执行上下文将切换,创建一个新的执行上下文,我们将其命名为“addTwo执行上下文”。执行上下文被推送到调用堆栈。我们在当前执行上下文中做的第一件事是什么?
  7. 你可能会想说,“在当前执行上下文中声明了一个新的变量ret”。这不是答案。正确答案是,我们首先需要查看函数的参数。在当前执行上下文中声明了一个新变量x。并且由于值3作为参数传递,因此变量x被赋值为3。
  8. 下一步,当前上下文定义变量ret,并赋值为undefined。
  9. 第三行是个表达式,寻找x,我们先在本地上下文寻找,找到,为3,第二个操作输是2,表达式的结果是5,5被分配给ret。
  10. return ret,本地上下文中查找,ret为5,函数返回值为5,次数函数结束。
  11. 第4-5行。函数结束。本地执行上下文被销毁。变量x和ret被销毁。它们不再存在。上下文弹出调用堆栈,返回值返回到调用上下文。这里调用上下文是全局执行上下文。
  12. 我们将上面返回的值5分配给b,此时我们还在第6行。
  13. 第7行,变量b会在控制台打印出来,输出5。

对于一个非常简单的程序来说,这是一个非常冗长的解释,我们甚至还没有触及闭包。我相信会到达那里。但首先我们需要再绕一两次。

词法作用域

我们需要了解词法作用域的一些东西。我们来看一下下面的例子。

let val1 = 2;
function multiplyThis(n) {
  let ret = n * val1;
  return ret;
}
let multiplied = multiplyThis(6);
console.log("example of scope:", multiplied);

这里想表达的是我们在本地执行上下文中有变量,在全局执行上下文中有变量。 JavaScript的一个复杂之处在于它如何查找变量。如果它在本地执行上下文中找不到变量,它将在其调用上下文中查找它。如果没有在其调用上下文中找到它。反复地,直到它查看全局执行上下文。 (如果它没有找到它,它是undefined)。按照上面的例子,它将阐明它。如果您了解作用域的工作原理,则可以跳过此步骤。

这里我就不分析了,太长了,参考上面的就差不多知道了。

在这个例子中,我们只需要记住一个,函数可以访问在其调用上下文中定义的变量。这种情况我们称之为词法作用域。

一个函数返回另一个函数

第一个例子中函数返回的是一个数字,我们前面说过,函数可以返回任意值,下面让我们看个例子,函数返回函数,这对理解闭包很重要,下面例子我们开始分析:

let val = 7;
function createAdder() {
  function addNumbers(a, b) {
    let ret = a + b;
    return ret;
  }
  return addNumbers;
}
let adder = createAdder();
let sum = adder(val, 8);
console.log("example of function returning a function: ", sum);

分析步骤为自己参照理解,不是直接翻译:

函数执行返回前,分析同第一个例子,来到return,createAdder上下文推出执行堆栈,变量addNumbers已经消失,但是函数addNumbers还存在,它被函数返回并且分配给变量adder了。下面就是在全局上下文下操作了,所以结果输出的是15。

翻译继续:

正如预期的那样,控制台将打印15。我想在这里说明几点。首先,函数定义可以存储在变量中,函数定义对程序是不可见的,直到被调用。其次,每次调用函数时,(临时)创建本地执行上下文。当函数完成时,执行上下文消失。函数在遇到return或者}括号时完成。

最后,闭包

看看下一个代码,并试着弄清楚会发生什么。

function createCounter() {
  let counter = 0;
  const myFunction = function() {
    counter = counter + 1;
    return counter;
  };
  return myFunction;
}
const increment = createCounter();
const c1 = increment();
const c2 = increment();
const c3 = increment();
console.log("example increment", c1, c2, c3);

初步分析(非直译):

  1. 执行createCounter函数时,返回了一个函数myFunction,此时createCounter执行上下文销毁,但是myFunction依然存在,当前执行上下文指向全局。此时counte为undefined。
  2. 第一次执行increment,执行表达式counter + 1,也就是undefined + 1,所以c1为1,此时counter为1。
  3. 第二次执行increment,执行表达式counter + 1,也就是1 + 1,所以c2为2,此时counter为2。
  4. 同理,c3就是3了。所以结果输出1,2,3。

翻译继续:

不知道为何,increment函数记住了counter,它是怎么工作的呢?

是否counter存在于全局上下文中,console.log(counter),发现并没有,所以不是全局的,所以上面分析是错误的。

也许,当你调用increment时,它会以某种方式返回到它创建的函数(createCounter)?怎么会这样呢?变量increment包含函数定义,而不是它来自何处。所以那不是它。

所以必须有另一种机制。闭包。我们终于分析到了它。

下面是它的工作原理。每当声明一个新函数并将其赋值给变量时,都会存储函数定义以及闭包。闭包包含创建函数时范围内的所有变量。它类似于背包。功能定义附带一个小背包。在它的包中,它存储了创建函数定义时作用域内的所有变量。

再次分析(非直译):

  1. 执行createCounter函数时,定义了一个新的变量myFunction,这个变量在当前上下文是公开的,这个变量的内容是另一个函数的定义,同时我们还创建一个闭包并将其作为函数定义的一部分包含在内。闭包包含当前作用域的变量,这个例子里,就是变量counter,它的值为0。
  2. 第一次执行increment,执行表达式counter + 1,0 + 1,所以c1为1,此时counter为1。
  3. 第二次执行increment,执行表达式counter + 1,也就是1 + 1,所以c2为2,此时counter为2。
  4. 同理,c3就是3了。所以结果输出1,2,3。

所以现在我们了解这是如何工作的。要记住的关键是当声明一个函数时,它包含一个函数定义和一个闭包。闭包是函数创建时作用域内所有变量的集合。

您也许会问,任何函数是否都有闭包,甚至是在全局作用域内创建的函数?答案是对的。在全局作用域中创建的函数也会创建一个闭包。但由于这些函数是在全局作用域内创建的,因此它们可以访问全局作用域内的所有变量,所以闭包的概念并没有什么意义。

当函数返回一个函数时,就是让闭包的概念变得更加相关。返回的函数可以访问不在全局作用域内的变量,但它们仅存在于其闭包中。

结尾

我们要记住通过背包来打比方的方式形容闭包。当一个函数被创建并传递或从另一个函数返回时,它带有一个背包。并且在背包中是声明函数时作用域内的所有变量。

纯属个人翻译,经验不足,如有出入或问题欢迎指正,谢谢。

mobx 使用小记

mobx 使用

observable

将 JS 基本数据类型、引用类型、普通对象、类实例、数组和映射,转换为可观察数据

action

用来修改 observable 的数据的动作,只有 action 和 runInAction 才能修改 observable

@action 和@action.bound

  • @action 保留原始函数的绑定和原型包含。 如果原始函数未绑定,则结果不会,反之亦然。 如果原始函数不在原型中,结果将不会,反之亦然。
  • @action.bound 将始终生成一个绑定的函数,该函数位于原型中。

runInAction

用来在异步的时候执行修改 observable 的数据的动作。例如网络请求后修改数据,这种模式的优势是它鼓励你不要到处写 action,而是在整个过程结束时尽可能多地对所有状态进行修改

computed

根据现有的 observable 的值或其它计算值衍生出的值。只有在 view 使用了 computed 的值, computed 才会执行计算

参考

多异步协作

多异步协作

异步操作

我们通过定义一个count来监听,当所有的异步返回时,我们统一处理res。

let count = 0;
let res = {};
const done = function(key, val) {
    res[key] = val;
    count ++;
    if(count === 3) {
        render(res);
    }
};
fs.readFile(template_path, 'utf8', (err, tp) => {
    done('tp', tp);
}); 
db.query(sql, (err, data) => {
    done('data', data);
});
$.get(demo.json, (err, res) => {
    done('res', res);
});

哨兵变量

var after = function(times, callback) {
    var count = 0,res = {};
    return function(key, val) {
        res[key] = val;
        count ++;
        if(count == times) {
            callback(res);
        }
    }
}

Promise.all使用

let promise1 = new Promise(function (resolve) {
    resolve(1);
});
let promise2 = new Promise(function (resolve) {
    resolve(2);
});
let promise3 = new Promise(function (resolve) {
    resolve(3);
});

Promise.all([promise1, promise2, promise3]).then(function (res) {
    console.log(res);
});

Promise.all自己实现

Promise.prototype.all = function (promises) {
    let results = [];
    let promiseCount = 0;
    let promisesLength = promises.length;
    return new Promise(function (resolve, reject) {
        for (let val of promises) {
            Promise.resolve(val).then(function (res) {
                promiseCount++;
                results[i] = res;
                if (promiseCount === promisesLength) {
                    return resolve(results);
                }
            }, function (err) {
                return reject(err);
            });
        }
    });
};

执行上下文中的2道题

第1题

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

这端代码输出的答案是:local scope;为什么呢?
执行过程伪代码:

- [全局]EC
 - [local scope]EC
  - [f]EC
f函数执行能访问到local scope,所以输出local scope

第2题

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

这端代码输出的答案是:local scope;为什么呢?
执行过程伪代码:

- [全局]EC
 - [local scope]EC
  - 闭包
执行到闭包时,[local scope]EC已出栈,所以闭包内this指向window,但是我们知道闭包能访问上层变量,也就是[local scope]EC里的变量,所以打印出来的也是local scope

vue学习过程中的问题汇总

vue2中箭头函数的使用注意

注意,

  • 不应该使用箭头函数来定义 method 函数
  • 不应该使用箭头函数来定义计算属性函数
  • 不应该使用箭头函数来定义 watcher 函数
  • 不能使用箭头函数来定义一个生命周期方法
  • 不应该对 data 属性使用箭头函数
  • 不应该使用箭头函数来定义 watcher 函数

这些情况下自动绑定 this 上下文到实例中,所以不能使用箭头函数。
(例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

Atom中设置vue模板

安装Packages > Language Vue
打开插件代码可以配置默认vue模板

".text.html.vue":
  "Vue Component":
    "prefix": "template"
    "body": """
      <template>
        $3
      </template>

      <script>
      export default {

      }
      </script>

      <style lang="${2:less}">
      </style>

    """

新建vue文件,输入template,按下Tab键即可生成默认模板

react向组件传递函数方式

react向组件传递函数方式

bind

constructor(props) {
super(props);
this.state = {
  value: 0
};
this.handleClick2 = this.handleClick1.bind(this);
}

箭头函数

handleClick3 = () => {
  console.log(this);
};

直接传递

handleClick1() {
  console.log(this);
}
<button onClick={this.handleClick1}>click 1</button>
<button onClick={this.handleClick2}>click 2</button>
<button onClick={this.handleClick3}>click 3</button>

点击三个按钮输出结果如下图:
GkrOxI.png

module.exports和exports的区别

module.exports和exports的区别

Node模块允许你从被引入文件中选择要暴露给程序的函数和变量。如果模块返回的函数或变 量不止一个,那它可以通过设定exports对象的属性来指明它们。但如果模块只返回一个函数或变量,则可以设定module.exports属性。

module和exports

// demo.js
console.log(module)
console.log(exports)

// 输出
Module {
  id: '.',
  exports: {},
  parent: null,
  filename:
   '.../demo1.js',
  loaded: false,
  children: [],
  paths:
   [ ... ] }
{}

module.exports和exports一开始都是一个空对象{},本质上这两个指向的是同一块内存,

require

require引入的对象本质上是module.exports。这就产生了一个问题,当 module.exports和exports指向的不是同一块内存时,exports的内容就会失效。

最终在程序里导出的是module.exports。exports只是对module.exports的一个全 局引用,最初被定义为一个可以添加属性的空对象。如果把exports设定为别的,就打破了module.exports和exports之间的 引用关系。可是因为真正导出的是module.exports,那样exports就不能用了,因为 它不再指向module.exports了。如果你想维持那个链接,可以像下面这样让 module.exports再次引用exports。

// person.js
module.exports = {name: 'clearives'}
exports = {name: 'Mr、z'}


// demo.js

const person = require('./person')
console.log(person) // {name: 'clearives'}


module.exports = exports = {...};

参考:
Node.js实战

异步和事件循环

异步和事件循环

JavaScript是单线程

JS引擎中负责解释和执行JavaScript代码的线程只有一个,但是实际上还存在其他的线程。例如:处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程(例如在Node.js中)等等,可以理解为任务队列

JavaScript同步与异步

同步

同步,也就是说按照顺序去做

console.log('node01')
console.log('node02')

// 输出:node01,node02

异步

异步,也就是说不完全按照顺序去做

console.log('node01')
setTimeout(function () {
  console.log('node02');
},100)
console.log('node03')

// 输出:node01,node03,node02

异步实现理解:

  • 1、主线程发起一个异步任务,工作线程接收任务
  • 2、主线程可以继续执行后面的代码,同时工作线程执行异步任务
  • 3、工作线程完成任务后,通知主线程
  • 4、主线程收到通知后,执行相关操作,也就是我们所谓的回调函数

任务队列

一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都关联着一个用以处理这个消息的函数。在事件循环期间的某个时刻,运行时从最先进入队列的消息开始处理队列中的消息。为此,这个消息会被移出队列,并作为输入参数调用与之关联的函数。正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧(函数调用形成了一个栈帧)。函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)。

事件循环

主线程从"任务队列"中读取消息事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)

微任务(Microtasks)、宏任务(macrotasks)

  • macrotasks: 整体代码script setTimeout setInterval setImmediate I/O UI渲染
  • microtasks: Promise process.nextTick Object.observe MutationObserver

JS异步机制,遇到宏任务,先执行宏任务,宏任务执行结束,如果有可执行的微任务,执行微任务至结束,再开始新的宏任务,如果没有执行的微任务,直接开始新的宏任务。

参考:微任务、宏任务与Event-Loop

小程序学习&Taro开发遇见的问题收集

小程序学习&Taro 开发遇见的问题收集

button 获取用户信息登录

原生小程序使用方法:

<button type="primary" open-type="getUserInfo" bindgetuserinfo="getUserInfo">
  获取用户信息
</button>
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">
  获取手机号
</button>

Taro 使用方法:

<button
  id="login-btn"
  openType="getUserInfo"
  lang="zh_CN"
  onGetUserInfo="{this.getUserInfo}"
  type="primary"
>
  微信用户快速登录
</button>
<button openType="getPhoneNumber" onGetPhoneNumber="{this.getPhoneNumber}">
  获取手机号
</button>

open-type 改为 openType,bindgetuserinfo 改为 onGetUserInfo,其它类似

checkSession

Taro.checkSession({
  success(res) {
    console.log("success", res);
  },
  fail(res) {
    console.log("fail", res);
  }
})
  .then(data => console.log(data))
  .catch(() => {
    console.log("wx.login");
  });

getSetting

getSetting 获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
20191101152227.png

注意:getPhoneNumber 获取手机号权限不会出现在这里

getUserInfo

在用户未授权过的情况下调用此接口,将不再出现授权弹窗,会直接进入 fail 回调(详见《公告》)。在用户已授权的情况下调用此接口,可成功获取用户信息。授权采用 button

picker-view

当我们使用 picker-view 时会有一个 onChange 事件,也就是我们滚动了,我们能从 e.detail.value 中拿到联动的取值,当我们控制改变时,受控部分需要重制到自己的第一个,我们必须手动去修改值。

// 参考省市联动
cityChange(e) {
  const pickerValue = e.detail.value
  const { provinces, citys, value } = this.state
  const provinceNum = pickerValue[0]
  const cityNum = pickerValue[1]
  const countyNum = pickerValue[2]
  // 如果省份选择项和之前不一样,表示滑动了省份,此时市默认是省的第一组数据,
  if (value[0] != provinceNum) {
    const id = provinces[provinceNum].id
    this.setState({
      value: [provinceNum, 0, 0],
      citys: address.citys[id],
      areas: address.areas[address.citys[id][0].id]
    })
  } else if (value[1] != cityNum) {
    // 滑动选择了第二项数据,即市,此时区显示省市对应的第一组数据
    const id = citys[cityNum].id
    console.log(id)
    this.setState({
      value: [provinceNum, cityNum, 0],
      areas: address.areas[citys[cityNum].id]
    })
  } else {
    // 滑动选择了区
    this.setState({
      value: [provinceNum, cityNum, countyNum]
    })
  }
}

参考:picker-view

koa知识梳理

koa知识梳理

实现 Async 方法

要在 node < 7.6 版本的 Koa 中使用 async 方法, 我们推荐使用 babel's require hook.

require('babel-register');
// 应用的其余 require 需要被放到 hook 后面
const app = require('./app');

要解析和编译 async 方法, 你至少应该有 transform-async-to-generator 或 transform-async-to-module-method 插件.

实现 import & export default

使用babel-plugin-add-module-exports

"plugins": [
  "add-module-exports"
]

koa中间件级联

通过一系列功能直接传递控制,直到一个返回,Koa 调用“下游”,然后控制流回“上游”。

const Koa = require('koa');
const app = new Koa();

// logger

app.use(async (ctx, next) => {
  console.log(1)
  await next();
  console.log(5)
  const rt = ctx.response.get('X-Response-Time');
  console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});

// x-response-time

app.use(async (ctx, next) => {
  console.log(2)
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  console.log(4)
  ctx.set('X-Response-Time', `${ms}ms`);
});

// response

app.use(async ctx => {
  console.log(3)
  ctx.body = 'Hello World';
});

app.listen(3000);

上面例子打印的顺序是12345。

语法糖 app.listen(...)

const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);

app.use & 中间件(middleware)

export default async (ctx, next) => {
  const start = new Date();
  await next();
  const ms = new Date() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
}

这是一个logger中间件,app.use()用来加载中间件,每个中间件默认接受两个参数,第一个参数是 Context 对象,第二个参数是next函数。只要调用next函数,就可以把执行权转交给下一个中间件。
这里我们再回过头看上面的输出12345会引出中间件栈的说法,此处参考阮一峰老师的文章。

多个中间件会形成一个栈结构(middle stack),以"先进后出"(first-in-last-out)的顺序执行。

  1. 最外层的中间件首先执行。
  2. 调用next函数,把执行权交给下一个中间件。
  3. ...
  4. 最内层的中间件最后执行。
  5. 执行结束后,把执行权交回上一层的中间件。
  6. ...
  7. 最外层的中间件收回执行权之后,执行next函数后面的代码。

中间件包括同步和异步,异步的必须加上async await

错误处理

如果代码运行过程中发生错误,我们需要把错误信息返回给用户。HTTP 协定约定这时要返回500状态码。Koa 提供了ctx.throw()方法,用来抛出错误,ctx.throw(500)就是抛出500错误。

const code2Token = async (ctx, next) => {
  const request = ctx.request.body;
  const code = request.code;
  let res = await axios.get(`${wx.loginUrl}?appid=${wx.appId}&secret=${wx.appSecret}&js_code=${code}&grant_type=authorization_code`)
  if (res.data.errcode && res.data.errcode !== 0) {
    ctx.throw(412, res.data.errmsg) // 抛异常
  }

  let user = await WxUserService.getUserByOpenid(res.data.openid)

  if (!user) {
    user = await WxUserService.createUserByOpenid(res.data.openid)
  }

  let token = jwtUtil.sign({_id: user.openid})
  ctx.body = Resp({
    data: {
      openid: user.openid,
      token
    }
  })
};

涉及到的node模块相关内容

path

node-path.png

path.dirname(path)

该方法返回 path 的目录名

path.join([...paths])

该方法使用平台特定的分隔符作为定界符将所有给定的 path 片段连接在一起,然后规范化生成的路径,零长度的 path 片段会被忽略。 如果连接的路径字符串是零长度的字符串,则返回 '.',表示当前工作目录。

path.resolve([...paths])

该方法将路径或路径片段的序列解析为绝对路径。

basename(文件名)、extname(扩展名)、dirname(文件所在文件夹名称)

http

http.createServer

http模块主要用于搭建HTTP服务。使用Node搭建HTTP服务器非常简单。

const http = require('http');
const PORT = 3001;
const server = http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.write("Hello World");
  res.end();
})
server.listen(PORT, function() {
  console.log(`the server is started at port ${PORT}`)
})

events

const Emitter = require('events');
/** module.exports = class Application extends Emitter {...} 导出了koa类 **/ 
/** application.js 继承了 Emitter,所有它也包含了异步事件的处理能力,后面可以看到koa的错误处理会使用到Emitter提供的事件模型方法。 **/

JS循环遍历小结

常用的循环遍历方法有for循环,for..in,forEach,map等等...

数组遍历

for

for循环是最基本的遍历。

forEach

forEach() 方法对数组的每个元素执行一次提供的函数。

let arr = [1,2,3,4]

let _arr = arr.forEach((item) => {
  console.log(item) // 1, 2, 3, 4
})

console.log(_arr) // undefined

map

map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果,不改变原数组。

let arr = [1,2,3,4]

let _arr = arr.map((item, i) => {
  console.log(i)  // 0, 1, 2, 3
  return  item + 1
})

console.log(_arr) // [ 2, 3, 4, 5 ]

filter、includes、find、findIndex这几个都是筛选数组的方法,下面一一分析

filter

filter() 方法创建一个新数组,不改变原数组

let arr = [1,2,3,4]

let _arr = arr.filter((item, i) => {
  return  item > 2
})

console.log(_arr) // [3, 4]

includes

includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。

find

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined,不改变原数组,不创建新数组,只返回满足条件的第一个值。

let arr = [1,2,3,4]

let _arr = arr.find((item, i) => {
  return  item > 2
})

console.log(_arr) // 3

findIndex

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。

some

some() 方法测试是否至少有一个元素可以通过被提供的函数方法。该方法返回一个Boolean类型的值,不改变原数组。

every

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

若收到一个空数组,此方法在一切情况下都会返回 true。

对象遍历

Object.keys()

Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。如果对象的键-值都不可枚举,那么将返回由键组成的数组。

let obj = {
  a: 1,
  b: 2
}

let _obj = Object.keys(obj)

console.log(_obj) // ["a", "b"]

for..in

for..in方法循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。

Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。

let obj = {
  a: 1,
  b: 2
}

let _obj = Object.keys(obj)

console.log(_obj) // ["a", "b"]

for(var i in obj) {
  console.log(i,":",obj[i]);  // a: 1, b: 2
}

let arr = [1, 3, 5]

console.log(Object.getOwnPropertyNames(obj))  // ["a", "b"]
console.log(Object.getOwnPropertyNames(arr))  // ["0", "1", "2", "length"]

JS跳出循环的三种方法(break, return, continue)

break

break 语句中止当前循环,switch语句或label 语句,并把程序控制流转到紧接着被中止语句后面的语句。

continue

continue 语句结束当前(或标签)的循环语句的本次迭代,并继续执行循环的下一次迭代

i = 0;
n = 0;
while (i < 5) {
   i++;
   if (i === 3) {
      continue;
   }
   n += i;
}

与 break 语句的区别在于, continue 并不会终止循环的迭代,而是:

  • 在 while 循环中,控制流跳转回条件判断;
  • 在 for 循环中,控制流跳转到更新语句。

return

return语句终止函数的执行,并返回一个指定的值给函数调用者。

function counter() {
  for (var count = 1; ; count++) {  // 无限循环
    console.log(count + "A"); // 执行5次
      if (count === 5) {          
        return;
      }
      console.log(count + "B");  // 执行4次
    }
  console.log(count + "C");  // 永远不会执行
}

counter();

参考资料:
developer.mozilla.org

Atom配置全局eslint

➜ アメージング😁  ~ npm install -g eslint
➜ アメージング😁  ~ eslint init  //生成配置文件

下面是atom里面配置,安装Linter Eslint插件,

/usr/local/lib/node_modules  //node_modules路径
~/.eslintrc   //eslintrc path

这里要注意,如果你安装了nvm,node_modules路径配置可能不一样,或者使用nvm use system切换到默认node机制即可。

Swiper API Issues

当设置自动切换并且有prev,next按钮的时候我们要保持点了按钮之后依然能保持autoplay

autoplayDisableOnInteraction: true    //默认为false

默认激活的 slide为第一个,要设置中间项为激活项

centeredSlides: true  //默认为false

测试

测试标题

  • 列表
    -列表

JS继承对比

JS继承

JS的继承方式有很多种,最理想的继承方式是寄生组合式继承。

组合继承

function Person(name) {
    this.name = name;
}
Person.prototype.sayHi = function() {
    console.log(this.name + '-' + '-hi');
}

function SubPerson(name, age) {
    Person.call(this, name);
    this.age = age;
}

SubPerson.prototype = new Person();
SubPerson.prototype.constrcutor = SubPerson;

var sp = new SubPerson('clearives', 18);
console.log(sp);
sp.sayHi();

组合继承(构造函数和原型的组合)会调用两次父类构造函数的代码

寄生组合式继承

function inheritPrototype(SubPerson, Person){ 
  var protoType = Object.create(Person.prototype);
  protoType.constructor = SubPerson;
  SubPerson.prototype = protoType;
}
function Person(name) {
    this.name = name;
}
Person.prototype.sayHi = function() {
    console.log(this.name + '-' + '-hi')
}

function SubPerson(name, age) {
    Person.call(this, name);
    this.age = age;
}

inheritPrototype(SubPerson, Person);

var sp = new SubPerson('clearives', 18);
console.log(sp);
sp.sayHi();

这样避免了调用两次父类的构造函数,为其创建多余的属性。

其它

function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
// 这个函数其实就是Object.create的简单实现

koa日志管理【koa-log4】

koa日志管理【koa-log4】

安装

npm i koa-log4 --save

使用

配置文件

import path from 'path'
const logPath = path.resolve(__dirname, "../../logs/")
const config = {
  appenders: {
    access: {
      type: 'dateFile',
      pattern: '-yyyy-MM-dd.log',
      filename: path.join(logPath, 'access.log'),
    },
    application: {
      type: 'dateFile',
      pattern: '-yyyy-MM-dd.log',
      filename: path.join(logPath, 'application.log')
    },
    out: {
      type: 'console',
    },
  },
  categories: {
    default: { appenders: [ 'out' ], level: 'info' },
    access: { appenders: [ 'access' ], level: 'info' },
    application: { appenders: [ 'application' ], level: 'WARN'}
  }
}

export default config


// use 
import config from './log4-conf'

log4js.configure(config)

暴露方法

const logger = log4js.getLogger()
const applicationLogger = log4js.getLogger('application')
const accessLogFunc = log4js.koaLogger(log4js.getLogger('access'))

logger表示常规的日志,在控制台输出,applicationLogger表示应用级别的日志,会输出在对应的log文件,accessLogFunc用app.use()加载,所有访问级别的日志会全部输出在对应的日志文件。

这里我们将方法统一挂载在global上,供其它地方使用。

import { logUtil } from '../utils/log4'
export default (ctx, next) => {
  global.logUtil = logUtil
  next()
}

日志级别

日志级别主要分为以下几种

  • trace
  • debug
  • info
  • warn
  • error
  • fatal

参考文档

log4js-node

光标快捷键

光标快捷键

ctrl键组合

ctrl+a  //光标移到行首
ctrl+b  //光标左移
ctrl+f  //光标右移
ctrl+c  //杀死当前进程
ctrl+d  //退格删除
ctrl+e  //光标移到行尾
ctrl+h  //删除光标前一个字符,同 delete 键相同
ctrl+k  //清除光标后至行尾的内容
ctrl+l  //清屏,相当于clear
ctrl+r  //搜索之前打过的命令。会有一个提示
ctrl+u  //清除光标前至行首间的所有内容
ctrl+w  //移除光标前的一个单词
ctrl+t  //交换光标位置前的两个字符
ctrl+y  //粘贴或者恢复上次的删除
ctrl+d  //删除光标所在字母;注意和backspace以及ctrl+h的区别,这2个是删除光标前的字符
ctrl+f  //光标右移
ctrl+z  //把当前进程转到后台运行,使用**fg**命令恢复。比如top -d1 然后ctrl+z ,到后台,然后fg,重新恢复

esc组合

esc+d //删除光标后的一个词
esc+f //往右跳一个词
esc+b //往左跳一个词
esc+t //交换光标位置前的两个单词。

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.