静修-个人博客


  • 首页

  • 分类

  • 归档

  • 标签

  • 搜索

02-phpstorm自动加上分号

发表于 2019-06-15 | 分类于 后端-02-PhpStorm

Preferences => Keymap
搜索Complete Current Statement,设置成ctrl + 回车

01-phpstorm保存自动格式化

发表于 2019-06-15 | 分类于 后端-02-PhpStorm

1 增加自定义格式化

格式化快捷键:
CMD + ALT + L

配置等号对齐:

  1. Setting
  2. Editor
  3. Code Style
  4. PHP
  5. Wrapping and Braces
  6. 勾选Align consecutive assignments

配置key-value对齐:

  1. Setting
  2. Editor
  3. Code Style
  4. PHP
  5. Other
  6. 勾选Align key-value pairs

2 保存自动格式化

1 修改保存的默认快捷键CMD+S为“CMD+CTRL+Shift +S”

打开文件>设置,搜索“KEYMAP”并打开它;

搜索“Save All”并双击“Save All”;

修改快捷键为 “CMD+CTRL+Shift +S”。

2 选择Edit(编辑)->Macros(宏)->Start Macro Recording(开始录制宏)

快捷键键入 cmd+alt+l(格式化代码) 再键入 CMD+CTRL+Shift +S

ok,退出录制宏,保存为Format And Save

3 给录制宏加上cmd+s快捷键

在File->Settings(设置)->Keymap->Macros,找到刚才添加的“Format And Save”,右键点击“add keyboard shortcut”,录入快捷键“Ctrl+S”,点击”OK”。

参考链接

01-PHP基础

发表于 2019-06-15 | 分类于 后端-01-PHP

1 PHP 标记

<?php 和 ?>:如果文件内容是纯 PHP 代码,最好在文件末尾删除 PHP 结束标记。这可以避免在 PHP 结束标记之后万一意外加入了空格或者换行符,会导致 PHP 开始输出这些空白,而脚本中此时并无输出的意图。

2 区分大小写

在PHP中,自定义的函数名,类名,以及内置的函数,关键字是不区分大小写的,比如:

1
class,Class,CLASS,while,While,ECHO,echo,NULL,Null

都是一样的。

但是PHP中,变量的名字是区分大小写的,比如:

1
$name,$Name

就是两个不同的变量名。

3 字符串

3.1 变量解析

这里共有两种语法规则:一种简单规则,一种复杂规则。简单的语法规则是最常用和最方便的,它可以用最少的代码在一个 string 中嵌入一个变量,一个 array 的值,或一个 object 的属性。

复杂规则语法的显著标记是用花括号包围的表达式。

3.2 双引号和单引号的区别(双引号和单引号也叫定界符)

1.双引号可以解析变量,单引号不可以(能用单引号就不用双引号,因为单引号解析效率更高)

2.双引号解析所有转义符,单引号只解析\’和\这两个转义符

3.3 类型转换

3.3.1 自动类型转换(echo)

1)数值型转换成字符串:数值本身
2)布尔型转换成字符串:

  • true:1,
  • false:空字符串,
  • null:空字符串

3)数组转换成字符串:array
4)资源转换成字符串:Resource id #数字 资源句柄
5) 对象不能直接转换成字符串

3.3.2 临时转换

string() strval()

3.3.3 永久转换

settype() gettype() 设置变量类型,永久转换

变量解析
PHP进阶篇-字符串操作

4 Array 数组

4.1

1
2
3
4
array(  
key => value
, ...
)

// 键(key)可是是一个整数 integer 或字符串 string
// 值(value)可以是任意类型的值

如果在数组定义中多个单元都使用了同一个键名,则只使用了最后一个,之前的都被覆盖了。

4.2 用方括号的语法新建/修改

  • 可以通过明示地设定其中的值来修改一个已有数组。
  • 也可以省略键名,在这种情况下给变量名加上一对空的方括号([])。

15-docker安装nginx和phpfpm

发表于 2019-04-07 | 分类于 前端-13-Linux

docker 安装 nginx

1
docker pull nginx
1
docker run -d -p 8080:80 -v /Users/wujingxiu/Workspace/2019/docker/nginx/www:/usr/share/nginx/html -v /Users/wujingxiu/Workspace/2019/docker/nginx/conf.d:/etc/nginx/conf.d --name my-nginx nginx

在 conf.d 文件夹里新建 default.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
server {
listen 80;
server_name localhost;

#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

docker 安装 php-fpm

1
docker run -d -p 9000:9000 --name my-phpfpm -v /Users/wujingxiu/Workspace/2019/docker/nginx/www:/var/www/html bitnami/php-fpm

在default.conf添加

1
2
3
4
5
6
7
8
9
10
location ~ \.php(.*)$ {
root /var/www/html/;
fastcgi_pass 172.17.0.3:9000; #php容器的IP地址
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}

参考文章
docker部署MySQL+PHP-FPM+Nginx服务
Nginx+Php-fpm运行原理详解
Docker 从容器中拷贝文件到宿主机中

02-你不知道的js上册

发表于 2019-04-07 | 分类于 前端-15-读书笔记

第一部分

1 作用域

作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。赋值操作符会导致LHS查询。=操作符或调用函数时传入参数的操作都会导致关联作用域的赋值操作。

JavaScript 引擎首先会在代码执行前对其进行编译,在这个过程中,像 var a = 2 这样的声明会被分解成两个独立的步骤:

  1. 首先,var a 在其作用域中声明新变量。这会在最开始的阶段,也就是代码执行前进行。
  2. 接下来,a = 2 会查询(LHS 查询)变量 a 并对其进行赋值。

LHS 和 RHS 查询都会在当前执行作用域中开始,如果有需要(也就是说它们没有找到所需的标识符),就会向上级作用域继续查找目标标识符,这样每次上升一级作用域(一层楼),最后抵达全局作用域(顶层),无论找到或没找到都将停止。

不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式地创建一个全局变量(非严格模式下),该变量使用 LHS 引用的目标作为标识符,或者抛出 ReferenceError 异常(严格模式下)。

2.1 词法作用域

简单地说,词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)

2.2 欺骗词法

2.2.1 eval

在严格模式的程序中,eval(..) 在运行时有其自己的词法作用域,意味着其
中的声明无法修改所在的作用域

1
2
3
4
5
6
function foo(str) {
"use strict";
eval( str );
console.log( a ); // ReferenceError: a is not defined
}
foo( "var a = 2" )

JavaScript 中 还 有 其 他 一 些 功 能 效 果 和 eval(..) 很 相 似。setTimeout(..) 和
setInterval(..) 的第一个参数可以是字符串,字符串的内容可以被解释为一段动态生成的
函数代码。这些功能已经过时且并不被提倡。不要使用它们!

new Function(..) 函数的行为也很类似,最后一个参数可以接受代码字符串,并将其转
化为动态生成的函数(前面的参数是这个新生成的函数的形参)。这种构建函数的语法比
eval(..) 略微安全一些,但也要尽量避免使用

避免使用eval和Funciton构造器来避免双重求值(在js代码中执行另一段代码,此时首先会以正常方式求值,然后在执行过程中对包含字符串中的代码发起另一个求值运算)带来的性能消耗。给setTimeout和setInterval传递函数而不是字符串作为参数。

2.2.2 with

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo(obj) {
with (obj) {
a = 2;
}
}
var o1 = {
a: 3
};
var o2 = {
b: 3
};
foo( o1 );
console.log( o1.a ); // 2
foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2——不好,a 被泄漏到全局作用域上了!

3 函数作用域和块作用域

  1. 函数是 JavaScript 中最常见的作用域单元。本质上,声明在一个函数内部的变量或函数会在所处的作用域中“隐藏”起来,这是有意为之的良好软件的设计原则。

  2. 但函数不是唯一的作用域单元。块作用域指的是变量和函数不仅可以属于所处的作用域,也可以属于某个代码块(通常指 { .. } 内部)。

  3. 在严格模式的程序中,eval(..) 在运行时有其自己的词法作用域,意味着其 中的声明无法修改所在的作用域。

  4. 我们在第 2 章讨论过 with关键字。它不仅是一个难于理解的结构,同时也是块作用域的一个例子(块作用域的一种形式),用 with 从对象中创建出的作用域仅在 with声明中而非外部作用域中有效

  5. 从 ES3 开始,try/catch 结构在 catch 分句中具有块作用域。

  6. 在 ES6 中引入了 let 关键字(var关键字的表亲),用来在任意代码块中声明变量。if(..) { let a = 2; } 会声明一个劫持了 if 的 { .. }块的变量,并且将变量添加到这个块中。

  7. 除了 let 以外,ES6 还引入了const,同样可以用来创建块作用域变量,但其值是固定的(常量)。之后任何试图修改值的操作都会引起错误。

有些人认为块作用域不应该完全作为函数作用域的替代方案。两种功能应该同时存在,开发者可以并且也应该根据需要选择使用何种作用域,创造可读、可维护的优良代码。

4 变量提升

函数声明和变量声明都会被提升。但是一个值得注意的细节(这个细节可以出现在有多个“重复”声明的代码中)是函数会首先被提升,然后才是变量。

5 闭包

5.1 简单的模块化

大多数模块依赖加载器 / 管理器本质上都是将这种模块定义封装进一个友好的 API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i = 0; i < deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply(impl, deps);
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();

下面展示了如何使用它来定义模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
MyModules.define("bar", [], function() {
function hello(who) {
return "Let me introduce: " + who;
}
return {
hello: hello
};
});
MyModules.define("foo", ["bar"], function(bar) {
var hungry = "hippo";
function awesome() {
console.log(bar.hello(hungry).toUpperCase());
}
return {
awesome: awesome
};
});
var bar = MyModules.get("bar");
var foo = MyModules.get("foo");
console.log(bar.hello("hippo")); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO

5.2 小结

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。

模块有两个主要特征:

(1)为创建内部作用域而调用了一个包装函数;

(2)包装函数的返回值必须至少包括一个对内部函数的引用,这样就会创建涵盖整个包装函数内部作用域闭包。

6 动态作用域

需要明确的是,事实上JavaScript并不具有动态作用域。它只有词法作用域,简单明了。但是 this 机制某种程度上很像动态作用域

主要区别:词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的。(this 也是!)词法作用域关注函数在何处声明,而动态作用域关注函数从何调用。

第二部分

1 this

最重要的是要分析调用栈(就是为了到达当前执行位置所调用的所有函数)。我们关心的调用位置就在当前正在执行的函数的前一个调用中。

2.2 绑定规则

2.2.1 默认绑定

首先要介绍的是最常用的函数调用类型:独立函数调用。可以把这条规则看作是无法应用其他规则时的默认规则。

如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定到 undefined:

2.2.2 隐式绑定

另一条需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含,不过这种说法可能会造成一些误导。

隐式丢失

一个最常见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象或者 undefined上,取决于是否是严格模式

1
2
3
4
5
6
7
8
9
10
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"

虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定

一种更微妙、更常见并且更出乎意料的情况发生在传入回调函数时

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo() {
console.log(this.a);
}
function doFoo(fn) {
// fn 其实引用的是 foo
fn(); // <-- 调用位置!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a 是全局对象的属性
doFoo(obj.foo); // "oops, global

2.2.3 显式绑定

  • call/apply
  • 硬绑定:bind

call实现:

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
const args = [...arguments].slice(1)
const result = context.fn(...args)
delete context.fn
return result
}

apply 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
let result
// 处理参数和 call 有区别
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}

bind实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}

2.2.4 new绑定

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行 [[ 原型 ]] 连接。
  3. 这个新对象会绑定到函数调用的 this。
  4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象
1
2
3
4
5
6
7
function create() {
let obj = {}
let Con = [].shift.call(arguments)
obj.__proto__ = Con.prototype
let result = Con.apply(obj, arguments)
return result instanceof Object ? result : obj
}

2.3 判断this

  1. 函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。

    var bar = new foo()

  2. 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是指定的对象。

    var bar = foo.call(obj2)

  3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象

    var bar = obj1.foo()

  4. 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。

    var bar = foo()

2.4 绑定例外

2.4.1 被忽略的this

如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:

更安全的this

一种“更安全”的做法是传入一个特殊的对象,把this绑定到这个对象不会对你的程序产生任何副作用。

在 JavaScript 中创建一个空对象最简单的方法都是 Object.create(null)。Object.create(null) 和 {} 很 像, 但 是 并 不 会 创 建 Object.prototype 这个委托,所以它比 {}“更空”:

2.4.2 间接引用

1
2
3
4
5
6
7
8
function foo() {
console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2

赋值表达式 p.foo = o.foo 的返回值是目标函数的引用,因此调用位置是 foo() 而不是p.foo() 或者 o.foo()。根据我们之前说过的,这里会应用默认绑定。

91-babel转义import

2.4.3 软绑定

硬绑定会大大降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改 this。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (!Function.prototype.softBind) {
Function.prototype.softBind = function(obj) {
var fn = this;
// 捕获所有 curried 参数
var curried = [].slice.call(arguments, 1);
var bound = function() {
return fn.apply(
!this || this === (window || global) ? obj : this,
curried.concat.apply(curried, arguments)
);
};
bound.prototype = Object.create(fn.prototype);
return bound;
};
}

2.5 箭头函数

ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这其实和 ES6 之前代码中的 self = this 机制一样。

“箭头函数”的this,总是指向定义时所在的对象,而不是运行时所在的对象。
参考链接

3.2 类型

  • string
  • number
  • boolean
  • null
  • undefined
  • object
  • symbol

内置对象

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

在必要时语言会自动把字符串字面量转换成一个 String 对象,也就是说你并不需要显式创建一个对象。JavaScript 社区中的大多数人都认为能使用文字形式时就不要使用构造形式。

1
2
3
var strPrimitive = "I am a string"
console.log( strPrimitive.length ); // 13
console.log( strPrimitive.charAt( 3 ) ); // "m"

使用以上两种方法,我们都可以直接在字符串字面量上访问属性或者方法,之所以可以这样做,是因为引擎自动把字面量转换成 String 对象,所以可以访问属性和方法。

3.3 内容

.a 语法通常被称为“属性访问”,[“a”] 语法通常被称为“键访问”。

这两种语法的主要区别在于 . 操作符要求属性名满足标识符的命名规范,而 [“..”] 语法可以接受任意 UTF-8/Unicode字符串作为属性名。举例来说,如果要引用名称为”SuperFun!” 的属性,那就必须使用 [“Super-Fun!”] 语法访问,因为 Super-Fun!并不是一个有效的标识符属性名

此外,由于 [“..”] 语法使用字符串来访问属性,所以可以在程序中构造这个字符串,

1
2
3
4
5
6
7
8
9
var myObject = {
a:2
};
var idx;
if (wantA) {
idx = "a";
}
// 之后
console.log( myObject[idx] ); // 2

在对象中,属性名永远都是字符串。如果你使用string(字面量)以外的其他值作为属性名,那它首先会被转换为一个字符串。即使是数字也不例外,虽然在数组下标中使用的的确是数字,但是在对象属性名中数字会被转换成字符串,所以当心不要搞混对象和数组中数字的用法:

1
2
3
4
5
6
7
var myObject = { };
myObject[true] = "foo";
myObject[3] = "bar";
myObject[myObject] = "baz";
myObject["true"]; // "foo"
myObject["3"]; // "bar"
myObject["[object Object]"]; // "baz"

3.3.1 可计算属性名

ES6 增加了可计算属性名,可以在文字形式中使用 [] 包裹一个表达式来当作属性名

3.3.10 存在性

1
2
3
4
5
6
7
var myObject = {
a:2
};
("a" in myObject); // true
("b" in myObject); // false
myObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "b" ); // false

in 操作符会检查属性是否在对象及其 [[Prototype]] 原型链中(参见第 5 章)。
hasOwnProperty(..) 只会检查属性是否在 myObject 对象中,不会检查 [[Prototype]] 链

5 原型

5.1 属性设置和屏蔽

给一个对象设置属性并不仅仅是添加一个新属性或者修改已有的属性值。
现在我们完整地讲解一下这个过程:

1
myObject.foo = "bar";
  1. 如果 myObject 对象中包含名为 foo的普通数据访问属性,这条赋值语句只会修改已有的属性值。
  2. 如果 foo 不是直接存在于 myObject 中,[[Prototype]] 链就会被遍历,类似 [[Get]] 操作。如果原型链上找不到 foo,foo 就会被直接添加到 myObject 上。
  3. 如果 foo 存在于原型链上层,赋值语句 myObject.foo = “bar” 的行为就会有些不同(而且可能很出人意料)。

    在于原型链上层时 myObject.foo = “bar” 会出现的三种情况。

    • 如果在 [[Prototype]] 链上层存在名为 foo 的普通数据访问属性(参见第 3 章)并且没有被标记为只读(writable:false),那就会直接在 myObject 中添加一个名为 foo的新属性,它是屏蔽属性
    • 如果在 [[Prototype]] 链上层存在foo,但是它被标记为只读(writable:false),那么无法修改已有属性或者在myObject上创建屏蔽属性。如果运行在严格模式下,代码会抛出一个错误。否则,这条赋值语句会被忽略。总之,不会发生屏蔽
    • 如果在 [[Prototype]] 链上层存在 foo 并且它是一个 setter(参见第 3 章),那就一定会调用这个setter。foo不会被添加到(或者说屏蔽于)myObject,也不会重新定义foo 这个 setter。

      大多数开发者都认为如果向 [[Prototype]] 链上层已经存在的属性([[Put]])赋值,就一定会触发屏蔽,但是如你所见,三种情况中只有一种(第一种)是这样的。

      如果你希望在第二种和第三种情况下也屏蔽 foo,那就不能使用 =操作符来赋值,而是使用 Object.defineProperty(..)(参见第 3 章)来向 myObject 添加 foo。

  4. 如果属性名 foo 既出现在 myObject 中也出现在 myObject 的 [[Prototype]] 链上层,那么就会发生屏蔽。myObject 中包含的 foo 属性会屏蔽原型链上层的所有 foo属性,因为myObject.foo 总是会选择原型链中最底层的 foo 属性。

隐式屏蔽

有些情况下会隐式产生屏蔽,一定要当心。思考下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
var anotherObject = {
a:2
};
var myObject = Object.create( anotherObject );
anotherObject.a; // 2
myObject.a; // 2
anotherObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "a" ); // false
myObject.a++; // 隐式屏蔽!
anotherObject.a; // 2
myObject.a; // 3
myObject.hasOwnProperty( "a" ); // true

6 行为委托

如果在第一个对象上没有找到需要的属性或者方法引用,引擎就会继续在 [[Prototype]]关联的对象上进行查找。同理,如果在后者中也没有找到需要的引用就会继续查找它的[[Prototype]],以此类推。这一系列对象的链接被称为“原型链”。

Js中Prototype、proto、Constructor、Object、Function关系介绍 ,JS原型

01-egg小试

发表于 2019-03-28 | 分类于 前端-16-Node

1 eslint规则修改

在win10上面开发,报了一条eslint规则报错

在.eslintrc文件 rules 里面 配置 "linebreak-style": [0 ,"error", "windows"], //允许windows开发环境

此外,在mac平台下,vscode的eslint自动校验不起作用,安装npm i eslint-plugin-html -D即可

2 如何在vscode调试代码

官方提供的eggjs插件在vscode中debug配置好像有点问题,启动不起来,百度搜了一个配置如下:

按F5启动即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Egg",
"type": "node",
"request": "launch",
"cwd": "${workspaceRoot}",
"runtimeExecutable": "npm",
"windows": { "runtimeExecutable": "npm.cmd" },
"runtimeArgs": [ "run", "debug" ],
"console": "integratedTerminal",
"protocol": "auto",
"restart": true,
"port": 9999
}
]
}

3 使用egg-redis如何存储其他类型的数据

偷懒请使用 JSON.stringify ~,另外Redis 现在也支持 JSON 格式了。

1
await app.redis.set('foo', { aaaa: 123, bbbb: '123' });

4 token鉴权

参考

中间件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
'use strict'
const fs = require('fs')
const path = require('path')
const jwt = require('jsonwebtoken') //引入jsonwebtoken

module.exports = (options, app) => {
return async function userInterceptor(ctx, next) {
let authToken = ctx.header.authorization // 获取header里的authorization
if (authToken) {
const res = verifyToken(authToken) // 解密获取的Token
if (!(res.id && res.username)) {
ctx.body = { code: 402, msg: '登录状态已过期' }
} else{
await next()
}
} else {
ctx.body = { code: 401, msg: '请登陆后再进行操作' }
}
}
}

// 解密,验证
function verifyToken(token) {
let res = ''
try {
const result = jwt.verify(token, 'myfirsteggdemo') || {}
const { exp } = result,
current = Math.floor(Date.now() / 1000)
if (current <= exp) res = result.data || {}
} catch (e) {
console.log(e)
}
return res
}

生成token

1
2
3
4
5
const loginToken=function(data, expires = 7200) {
const exp = Math.floor(Date.now() / 1000) + expires
const token = jwt.sign({ data, exp }, 'myfirsteggdemo')
return {token,expire:exp}
}

6 egg-validate验证参数

6.1 application/x-www-form-urlencoded

一般传参content-type为application/x-www-form-urlencoded,这样参数值为数字时,传到后台是字符类型,需要做个转换

1
2
3
4
// config/config.default.js
exports.validate = {
convert: true,
};
1
2
3
4
5
6
7
this.ctx.validate({
type: { type: 'string' },
img_address: { type: 'string' },
img_link: { type: 'string' },
img_title: { type: 'string' },
order: { type: 'number' }, // 本来传过来是字符,现在设置type为number,这里会默认转换为数字再做验证
});

6.2 application/x-www-form-urlencoded由于并不能很好地解析嵌套数据的类型,后面content-type设置为’application/json’,postman测试请用raw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
this.ctx.validate({
title: { type: 'string' },
date: { type: 'number' },
introduce: { type: 'string' },
cover_img: { type: 'string' },
source: { type: 'string' },
content: { type: 'string' },
label: {
type: 'array',
itemType: 'string',
default: [],
required: false,
},
});

7 数据库操作

由于egg-mysql提供的功能较为简单,egg提供了另外一种方案Sequelize,

安装:

1
npm install --save egg-sequelize mysql2

在 config/plugin.js 中引入 egg-sequelize 插件

1
2
3
4
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};

在 config/config.default.js 中编写 sequelize 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
config.sequelize = {
dialect: 'mysql',
host: '127.0.0.1',
port: 3306,
username: '用户名',
password: '数据库密码',
database: 'learn',
define: {
underscored: true, // 字段以下划线(_)来分割(默认是驼峰命名风格),underscored:true 只适用于createAt updateAt字段(这两个字段是Sequelize自动添加的),其他字段没有作用。
freezeTableName: true, // 禁止sequelize更改表名为复数
timestamps: false, // 默认情况下,Sequelize会将createdAt和updatedAt属性添加到您的模型中,个人选择在此关掉
},
hooks: {
beforeDefine: attributes => {
Object.keys(attributes).forEach(key => {
if (typeof attributes[key] !== 'function') {
attributes[key].field = decamelize(key);
}
});
},
},
timezone: '+08:00', // sequelize默认时区要调整为东八区
};

由于mysql推荐命名为小写字母+下划线,接口使用驼峰命名法,需要做一个转换,官方并没有提供配置,这里找了issue上提供的一个方法,加上hooks

使用:

1
2
3
4
5
6
const User = app.model.define('user', {
...
userAge: { //由于加上hook设置后,这里会自动加上field对应user-age
type: INTEGER,
},
});

8 centos7部署部分

请参考我另外几篇笔记

  • centos7安装nvm
  • centos7安装git
  • centos7安装mysql

参考链接
segmentfault-Sequelize 字段下划线转驼峰
sequelize issues

01-图解http

发表于 2019-03-28 | 分类于 前端-15-读书笔记

1.1 3 项 WWW 构建技术分别是:

  • 把SGML(StandardGeneralizedMarkupLanguage,标准通用标记语言)作为页面的文本标记语言的HTML(HyperTextMarkupLanguage,超文本标记语言);
  • 作为文档传递协议的HTTP;
  • 指定文档所在地址的URL(UniformResourceLocator,统一资源定位符)。

1.2 TCP/IP 的分层管理

  • 应用层:FTP、 DNS、HTTP
  • 传输层:TCP(传输控制协议)和 UDP(用户数据报
    协议)
  • 网络层
  • 数据链路层

image

利用 TCP/IP 协议族进行网络通信时,会通过分层顺序与对信。发送端从应用层往下走,接收端则往应用层往

1.3 IP 协议的作用

IP 协议的作用是把各种数据包传送给对方。而要保证确实传送到对方
那里,则需要满足各类条件。其中两个重要的条件是:

  • IP 地址:节点被分配到的地址,

  • MAC
    地址(Media Access Control Address):指网卡所属的固定
    地址。

1.4 TCP三次握手

发送端首先发送一个带 SYN 标志的数据包给对方。接收端收到后,
回传一个带有 SYN/ACK 标志的数据包以示传达确认信息。最后,发送端再回传一个带 ACK 标志的数据包,代表“握手”结束。

image

2.1 持久连接

为解决上述 TCP 连接的问题,HTTP/1.1 和一部分的 HTTP/1.0 想出了持久连接(HTTP Persistent Connections,也称为 HTTP keep-alive 或HTTP connection reuse)的方法。持久连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。

5.1 通信数据转发程序代理

  • 是否使用缓存:缓存代理
  • 是否会修改报文:透明代理、非透明代理

6.1 HTTP 请求报文

image

6.2 HTTP 响应报文

在响应中,HTTP 报文由 HTTP 版本、状态码(数字和原因短语)、HTTP 首部字段 3 部分构成
image

6.3 HTTP 首部字段根据实际用途被分为以下 4 种类型

  • 通用首部字段(General Header Fields):

    请求报文和响应报文两方都会使用的

  • 请求首部字段(Request Header Fields)

    从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息。

  • 响应首部字段(Response Header Fields)

    从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。

  • 实体首部字段(Entity Header Fields)

    针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。

6.4 Cache-Control中no-cache和no-store区别

从字面意思上很容易把 no-cache 误解成为不缓存,但事实上 no-cache 代表不缓
存过期的资源,缓存会向源服务器进行有效期确认后处理资源,也许称为 do-notserve-from-cache-without-revalidation 更合适。no-store 才是真正地不进行缓存,请
读者注意区别理解。

6.4 请求首部字段

  • Host

    请求被发送至服务器时,请求中的主机名会用IP地址直接替换解决。但如果这时,相同的 IP地址下部署运行着多个域名,那么服务器就会无法理解究竟是哪个域名对应的请求。因此,就需要使用首部字段Host来明确指出请求的主机名。若服务器未设定主机名,那直接发送一个空值即可。

  • If-xxx

    形如 If-xxx 这种样式的请求首部字段,都可称为条件请求。服务器接收到附带条件的请求后,只有判断指定条件为真时,才会执行请求

  • If-Match

    请求首部 If-Match 的使用表示这是一个条件请求。在请求方法为 GET 和 HEAD 的情况下,服务器仅在请求的资源满足此首部列出的 ETag 之一时才会返回资源。而对于 PUT 或其他非安全方法来说,只有在满足条件的情况下才可以将资源上传

  • If-Modified-Since

    If-Modified-Since 用于确认代理或客户端拥有的本地资源的有效性。获取资源的更新日期时间,可通过确认首部字段 Last-Modified 来确定

6.5 响应首部字段

  • etag

    Etag是属于HTTP 1.1属性,它是由服务器生成返回给前端,当你第一次发起HTTP请求时,服务器会返回一个Etag,并在你第二次发起同一个请求时,客户端会同时发送一个If-None-Match,而它的值就是Etag的值(此处由发起请求的客户端来设置)。然后,服务器会比对这个客服端发送过来的Etag是否与服务器的相同,

    如果相同,就将If-None-Match的值设为false,返回状态为304,客户端继续使用本地缓存,不解析服务器返回的数据(这种场景服务器也不返回数据,因为服务器的数据没有变化嘛)如果不相同,就将If-None-Match的值设为true,返回状态为200,客户端重新解析服务器返回的数据

6.6 实体首部字段

  • Allow

    首部字段 Allow 用于通知客户端能够支持 Request-URI 指定资源的所有 HTTP 方法。当服务器接收到不支持的 HTTP 方法时,会以状态码405 Method Not Allowed 作为响应返回。与此同时,还会把所有能支持的 HTTP 方法写入首部字段 Allow 后返回。

  • Content-Encoding

    主要采用以下 4 种内容编码的方式。

    • gzip
    • compress
    • deflate
    • identity
  • Expires

    当首部字段 Cache-Control 有指定 max-age 指令时,比起首部字段 Expires,会优先处理 max-age 指令。

6.7 为 Cookie 服务的首部字段

首部字段名 说明 首部类型
Set-Cookie 开始状态管理所使用的Cookie信息 响应首部字段
Cookie 服务器接收到的Cookie信息 请求首部字段

7.1 HTTP 的缺点

  • 通信使用明文(不加密),内容可能会被窃听
  • 不验证通信方的身份,因此有可能遭遇伪装
  • 无法证明报文的完整性,所以有可能已遭篡改:(中间人攻击)

7.2 HTTP+ 加密 + 认证 + 完整性保护=HTTPS

通常,HTTP 直接和 TCP 通信。当使用 SSL时,则演变成先和 SSL通信,再由 SSL和 TCP 通信了。简言之,所谓 HTTPS,其实就是身披SSL协议这层外壳的 HTTP

image

SSL是独立于 HTTP 的协议,所以不光是 HTTP 协议,其他运行在应用层的 SMTP 和 Telnet 等协议均可配合 SSL协议使用。可以说 SSL是当今世界上应用最为广泛的网络安全技术

7.3 相互交换密钥的公开密钥加密技术

  • 共享密钥加密的困境:在互联网上转发密钥时,如果通信被监听那么密钥就可会落入攻击者之手,同时也就失去了加密的意义
  • 使用两把密钥的公开密钥加密:

    公开密钥加密使用一对非对称的密钥。一把叫做私有密钥(private key),另一把叫做公开密钥(public key)。顾名思义,私有密钥不能让其他任何人知道,而公开密钥则可以随意发布,任何人都可以获得

    使用公开密钥加密方式,发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。利用这种方式,不需要发送用来解密的私有密钥,也不必担心密钥被攻击者窃听而盗走

7.4 HTTPS 采用混合加密机制

HTTPS 采用共享密钥加密和公开密钥加密两者并用的混合加密机制。若密钥能够实现安全交换,那么有可能会考虑仅使用公开密钥加密来通信。但是公开密钥加密与共享密钥加密相比,其处理速度要慢。

所以应充分利用两者各自的优势,将多种方法组合起来用于通信。在交换密钥环节使用公开密钥加密方式,之后的建立通信交换报文阶段则使用共享密钥加密方式。

8 补充

白话TCP为什么需要进行三次握手
浅析HTTP/2的多路复用

14-centos7安装mysql

发表于 2019-03-28 | 分类于 前端-13-Linux

1.下载mysql源安装包

1
wget http://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm

2.安装mysql源

1
yum localinstall mysql57-community-release-el7-8.noarch.rpm

3.检查mysql源是否安装成功

1
yum repolist enabled | grep "mysql.*-community.*"

5.安装MySQL

1
yum install mysql-community-server

6.启动MySQL服务

1
systemctl start mysqld

7.开机启动

1
2
systemctl enable mysqld
systemctl daemon-reload

8.修改root本地登录密码

1)查看mysql密码

1
shell> grep 'temporary password' /var/log/mysqld.log

2)连接mysql

1
shell> mysql -uroot -p

3)修改密码[注意:后面的分号一定要跟上]

1
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass4!';

或者:

1
mysql> set password for 'root'@'localhost'=password('MyNewPass4!');

9.添加远程登录用户

1
grant all privileges  on *.* to root@'%' identified by "密码";

更新

1
flush privileges;

最后退出搞定!

1
exit

参考链接:
博客园
博客园

90-js去重对象

发表于 2019-03-28 | 分类于 前端-02-js基础复习
1
2
3
4
5
6
7
8
let person = [
{id: 0, name: "小明"},
{id: 1, name: "小张"},
{id: 2, name: "小李"},
{id: 3, name: "小孙"},
{id: 1, name: "小周"},
{id: 2, name: "小陈"},
];

1 向前取值,忽略后面重复的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function fn1(arr){
var obj={}
var re=arr.reduce((cur,next)=>{
if(!obj[next.id]){
obj[next.id]=true
cur.push(next)
}
return cur
},[])

return re
}

console.log(fn1(person));

结果

1
2
3
4
[ { id: 0, name: '小明' },
{ id: 1, name: '小张' },
{ id: 2, name: '小李' },
{ id: 3, name: '小孙' } ]

2 往后取值,忽略前面重复的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function fn2(arr){
var re=[]
for (let i = 0; i < arr.length; i++) {
for(let j=i+1;j<arr.length;j++){
if(arr[i]['id']==arr[j]['id']){
i++
j=i
}
}
re.push(arr[i])
}
return re
}

console.log(fn2(person));

结果:

1
2
3
4
[ { id: 0, name: '小明' },
{ id: 3, name: '小孙' },
{ id: 1, name: '小周' },
{ id: 2, name: '小陈' } ]

88-排序

发表于 2019-03-28 | 分类于 前端-02-js基础复习

选择排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var arr=[1,2,4,5,6,4,3,2]

function fn(arr){
for(let i=0;i<arr.length;i++){
var minIndex=i
for(let j=i+1;j<arr.length;j++){
if(arr[minIndex]>arr[j]){
minIndex=j
}
}
[arr[i],arr[minIndex]]=[arr[minIndex],arr[i]]
}
return arr
}
console.log(fn(arr));

插入排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function fn(arr){
for (let i = 1; i < arr.length; i++) {
for (let j = i; j > 0; j--) {
if(arr[j]<arr[j-1]){
[arr[j-1],arr[j]]=[arr[j],arr[j-1]]
}else{
break
}
}
}
return arr
}

console.log(fn([9,1,4,2,7,4]));

二分查找

// 非递归算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function binary_search(arr, key) {
var low = 0,
high = arr.length - 1;
while(low <= high){
var mid = parseInt((high + low) / 2);
if(key == arr[mid]){
return mid;
}else if(key > arr[mid]){
low = mid + 1;
}else if(key < arr[mid]){
high = mid -1;
}else{
return -1;
}
}
};
var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
var result = binary_search(arr,10);
alert(result); // 9 返回目标元素的索引值

希尔排序

希尔排序其实大体思路很简单,就是将数组(长度为len)分成间隔为t1的若干数组.进行插入排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let arr = [2,5,10,7,10,32,90,9,11,1,0,10];

function fn(arr){
for(let gap=Math.floor(arr.length/2);gap>0;gap=Math.floor(gap/2)){
for (let i = gap; i < arr.length; i++) {
for (let j = i; j >0; j-=gap) {
if(arr[j]<arr[j-gap]){
[arr[j-gap],arr[j]]=[arr[j],arr[j-gap]]
}else{
break;
}
}
}
}
return arr
}

console.log(fn(arr));

归并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function mergeSort(arr) {  //采用自上而下的递归方法
var len = arr.length;
if(len < 2) {
return arr;
}
var middle = Math.floor(len / 2),
left = arr.slice(0, middle),
right = arr.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right){
var result = [];
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}

while (left.length)
result.push(left.shift());

while (right.length)
result.push(right.shift());
return result;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(mergeSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

堆排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function heapSort(arr){
var len=arr.length
buildMaxHeap(arr,len)

for(var i=arr.length-1;i>0;i--){
[arr[0],arr[i]]=[arr[i],arr[0]]
len--;
heapify(arr,0,len)
}

return arr
}

// 建立大根堆
function buildMaxHeap(arr,len){
for (var i = Math.floor(len/2); i >= 0; i--) {
heapify(arr, i,len);
}
}

// 堆调整
function heapify(arr,i,len){
var left=2*i+1;
var right=2*i+2;
var largest=i;

if(left<len && arr[left]>arr[largest]){
largest=left
}

if(right<len && arr[right]>arr[largest]){
largest=right
}

if(largest!=i){
[arr[i],arr[largest]]=[arr[largest],arr[i]]
heapify(arr,largest,len)
}
}

var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(heapSort(arr));
1…345…33
静修

静修

322 日志
19 分类
19 标签
© 2019 静修
本站访客数:
由 Hexo 强力驱动
主题 - NexT.Pisces