My Little World

learn and share


  • 首页

  • 分类

  • 标签

  • 归档

  • 关于
My Little World

搭建博客步骤

发表于 2017-03-12

搭建

如果全局安装express 使用npm install -g express 语句安装后,无法使用express命令
则先执行npm install express-generator -g
express在4.0之后,需要安装the executable(执行器express-generator)才能执行express命令
express –view=ejs microblog //在当前文件夹创建文件夹microblog,并在里面部署express
cd microblog //进入microblog
npm install //安装nodejs包
DEBUG=microblog:* npm start //运行
npm install -g supervisor //安装supervisor,监视代码改动
supervisor ./bin/www //运行,会提示输入rs重新开始,输入’rs’回车即可,
//这里就不用每次改完都去手动中断进程再重新开始

路由初步配置

匹配

打开app.js文件
var index = require(‘./routes/index’); //引入不同访问路径的回调函数所在文件
app.use(‘/‘, index); //为回调函数配置访问路径

如果要建立分支路径,可以在回调函数文件中配置相应分支路径的回调函数
假如我想访问http://localhost:3000/users/111 和 http://localhost:3000/users/id/111
首先在app.js中配置

1
2
var users = require('./routes/users');
app.use('/users', users);

然后配置routes路径下的users.js

1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/:id', function(req, res, next) {
res.send('USER:'+req.params.id);
});

router.get('/id/:id', function(req, res, next) {
res.send('USERID:'+req.params.id);
});

module.exports = router;

注意路径参数id的获取,在app.js配置时是访问路径的共有前缀,在users.js中获取的是分支前缀
深入链接

控制权转移

express 处理路由规则时,会优先匹配先定义的陆游规则,后面的规则被屏蔽
通过调用回调函数的第三个参数next,可以将控制权转移到后面的规则
即执行完先定义的规则后会继续执行后面定义的规则
node1

手动安装片段视图

由于网站中多页面head和footer相同,所以要建立模板文件,然后让其他页面文件来来填充基于这种处理方法
express 4.x需要手动安装partials
npm install express-partials
在app.js中注册
var partials = require(‘express-partials’);
app.use(partials());

引入bootstrap 和 JQuery

从http://twitter.github.com/bootstrap/下载bootstrap.zip,
将 img 目录复制到工程 public 目录下,
将 bootstrap.css、bootstrap-responsive.css 复制到 public/stylesheets 中,
将 bootstrap.js 复制到 public/javascripts 目录中,
然后从http://jquery.com/下载一份最新版的 jquery.js 也放入 public/javascripts 目录中。

界面

用.ejs文件当html文件,
ejs 的标签系统只有以下3种标签。
 <% code %>: JavaScript 代码。
 <%= code %>:显示替换过 HTML 特殊字符的内容。
 <%- code %>:显示原始 HTML 内容。
新建layout.ejs 做模板文件 ,调整index.ejs内容做首页填充内容,访问 http://localhost:3000/ 查看效果

MongoDB 数据库

安装

选用 MongoDB 作为网站的数据库系统
在http://dl.mongodb.org/dl/win32/x86_64 可下载安装包
遇到该步骤时,
mongodb.png
选择custom可以自定义安装路径
安装完成后,在bin所在的文件夹,与bin同级建立data文件夹,然后cmd到bin文件夹,执行
mongod –dbpath data文件夹路径(例:D:\MongoDB\data)
执行完最后一行出现:
2017-03-12T13:16:29.286+0800 I NETWORK [thread1] waiting for connections on por
t 27017
浏览器输入 http://localhost:27017/ 可以看到显示信息为
It looks like you are trying to access MongoDB over HTTP on the native driver port.
说明安装完成

使用

修改package.json,dependencies中增加 “mongodb”: “>= 0.9.9”
npm install //更新依赖模块
在与app.js同级目录下新建settings.js,内容如下:

1
2
3
4
5
module.exports = {
cookieSecret: 'microblogYooHannah',
db: 'microblog',
host: 'localhost',
};

新建models文件夹,并在里面新建文件db.js,内容如下:

1
2
3
4
5
var settings = require('../settings'),
Db = require('mongodb').Db,
Connection = require('mongodb').Connection,
Server = require('mongodb').Server;
module.exports = new Db(settings.db, new Server(settings.host, Connection.DEFAULT_PORT, {}), {safe: true});

修改package.json,dependencies中增加 “connect-mongo”: “>= 0.1.7”
npm install更新依赖模块
修改app.js

1
2
3
4
5
6
7
8
9
var MongoStore = require('connect-mongo');
var settings = require('./settings');

app.use(express.session({
secret: settings.cookieSecret,
store: new MongoStore({
db: settings.db
})
}));

由于session不再绑定在express上,这里会报错,所以要单独安装session
npm install express-session
修改app.js

1
2
3
4
5
6
7
8
9
10
11
var session    = require('express-session');
var MongoStore = require('connect-mongo')(session);
var settings = require('./settings');


app.use(session({
secret: settings.cookieSecret,
store: new MongoStore({
url: 'mongodb://localhost/db'//链接数据库地址
})
}));

如出现错误:
MongoError: failed to connect to server [localhost:27017] on first connect
可能是没有运行刚才安装的MongoDB,这时要回到cmd,执行
mongod –dbpath data文件夹路径(例:D:\MongoDB\data)
如出现以下提醒:
express-session deprecated undefined resave option;
express-session deprecated undefined saveUninitialized option;
则补充上这两个配置参数

1
2
3
4
app.use(session({
resave: false, //添加 resave 选项
saveUninitialized: true, //添加 saveUninitialized 选项
}));

注册页面

在view中新建reg.ejs,内容如下;

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
<form class="form-horizontal" method="post">
<fieldset>
<legend>用户注册</legend>
<div class="control-group">
<label class="control-label" for="username">用户名</label>
<div class="controls">
<input type="text" class="input-xlarge" id="username" name="username">
<p class="help-block">你的账户名称,用于登录和显示。 </p>
</div>
</div>
<div class="control-group">
<label class="control-label" for="password">口令</label>
<div class="controls">
<input type="password" class="input-xlarge" id="password" name="password">
</div>
</div>
<div class="control-group">
<label class="control-label" for="password-repeat">重复输入口令</label>
<div class="controls">
<input type="password" class="input-xlarge" id="password-repeat" name="password-repeat">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">注册</button>
</div>
</fieldset>
</form>

在app.js和routes/index中增加访问路径和响应函数

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
45
46
47
48
49
50
51
52
53
//app.js
var router = require('./routes/index');//路由
var app = express();
app.use('/', router);
app.use('/reg',router );

//routes/index.js
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: '首页' });
});

router.get('/reg', function(req, res, next) {
res.render('reg', { title: '用户注册' });
});

router.post('/reg', function(req, res) {
//检验用户两次输入的口令是否一致
if (req.body['password-repeat'] != req.body['password']) {
req.flash('error', '两次输入的口令不一致');
return res.redirect('/reg');
}
//生成口令的散列值
var md5 = crypto.createHash('md5');
var password = md5.update(req.body.password).digest('base64');
var newUser = new User({
name: req.body.username,
password: password,
});
//检查用户名是否已经存在
User.get(newUser.name, function(err, user) {
if (user)
err = 'Username already exists.';
if (err) {
req.flash('error', err);
return res.redirect('/reg');
}
//如果不存在则新增用户
newUser.save(function(err) {
if (err) {
req.flash('error', err);
return res.redirect('/reg');
}
req.session.user = newUser;
req.flash('success', '注册成功');
res.redirect('/');
});
});
});
module.exports = router;

响应函数中的User是用户模型,在models中创建user.js,再引入到routes/index
响应函数会出现
1.title is not defined的问题
解决办法就是将views/layout.ejs的title换成locals.title
2.req.flash会报错说req.flash is not a function,则
npm install connect-flash
app.js修改:
var flash = require(‘connect-flash’);
app.use(flash());
这样所有的req就可以使用flash()函数
3.crypto没有被定义
npm install crypto
routes/index.js引入
var crypto = require(‘crypto’);

视图交互

实现不同登录状态下页面呈现不同内容的功能,创建动态视图助手
app.js

1
2
3
4
5
6
7
8
9
10
11
app.use(function(req,res,next){
res.locals.user=req.session.user;

var err = req.flash('error');
var success = req.flash('success');

res.locals.error = err.length ? err : null;
res.locals.success = success.length ? success : null;

next();
});

放在路由配置前面
接下来,修改 layout.ejs中的导航栏部分,为已登入用户和未登入用户显示不同的信息

1
2
3
4
5
6
7
8
9
<ul class="nav">
<li class="active"><a href="/">首页</a></li>
<% if (!user) { %>
<li><a href="/login">登入</a></li>
<li><a href="/reg">注册</a></li>
<% } else { %>
<li><a href="/logout">登出</a></li>
<% } %>
</ul>

在 container 中,<%- body %>之前加入:

1
2
3
4
5
6
7
8
9
10
<% if (success) { %>
<div class="alert alert-success">
<%= success %>
</div>
<% } %>
<% if (error) { %>
<div class="alert alert-error">
<%= error %>
</div>
<% } %>

它的功能是页面通知。

登录登出操作,状态判断;建立微博模型,处理微博发表操作

同样建立连接路由,写响应函数

处理首页登录前后状态

省略步骤可参考书籍
代码详见

My Little World

同步/异步和使用

发表于 2017-03-11

同步/异步

同步读取一个文件

1
2
3
4
5
6
7
8
9
//readfile.js
var fs = require('fs');
var data = fs.readFileSync('file.txt','utf-8');
console.log(data);
console.log('end.');
//执行
$node readfile.js
contents of the file.
end.

异步读取一个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//readfile.js
var fs = require('fs');
fs.readFile('file.txt','utf-8',function(err,data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
})
console.log('end.');
//执行
$node readfile.js
end.
contents of the file.

使用

node的REPL模式

即输入代码回车立即执行的模式
在command prompt中直接输入node进入该模式,连续两次Ctrl+C可退出

监视代码改动

npm install -g supervisor //安装supervisor
supervisor app.js //代替node app.js,修改代码后自动终止进程并重启

包

把文件夹封装为一个模块,即所谓的包,包通常是一些模块的集合,在模块的基础上提供了更高层的抽象,相当于一些固定接口的函数库
包安装:作为工程运行,通过本地安装;如果要在命令行下使用,则使用全局模式安装

My Little World

js理解

发表于 2017-03-11

作用域

不通过花括号来界定,通过函数来定义,

1
2
3
4
if(true){
var somevar = "value";
}
console.log(somevar);//value

在访问未定义的变量或定义了但没有初始化的变量时都会获得undefined

1
2
3
4
5
var scope = "1111";
var f = function(){
console.log(scope); //undefined
var scope = 'f';
}

作用域的嵌套关系在定义时确定,不在调用时确定

1
2
3
4
5
6
7
8
9
10
var scope = "top";
var f1 = function(){
console.log(scope);
};
f1();//top
var f2 = function(){
var scope = 'f2';
f1();
};
f2(); //top

全局作用域是个对象,这个对象叫全局对象,nodejs是global对象,浏览器是window对象,包括
在最外层定义的变量
全局对象的属性
未用var定义直接赋值的变量

闭包

由函数(环境)及其封闭的自由变量组成的集合体,
可以想象成一个容器,里面包括一个函数和它用到的变量,每当想使用一次这个容器里的函数,就将这个容器复制一次给调用者:
1.不通调用者得到的容器相互隔离,没有关系,
2.被复制到调用者的函数,会自己形成一个独立的运行环境,每次调用完会根据变量性质,更新变量值,不会释放变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var generateClosuer = function() {
var count = 0;
var get = function(){
count++;
return count;
}
return get;
}
var counter1 = generateClosuer();
var counter2 = generateClosuer();
console.log(counter1()); //1
console.log(counter1()); //2
console.log(counter2()); //1
console.log(counter1()); //3

对象

上下文对象即this指针,即被调用函数所处的环境,
上下文对象的作用是在一个函数内部引用调用它的对象本身。

1
2
3
4
5
6
7
8
function User(name,url){
this.name = name;
this.url = url;
this.diaplay = function(){
console.log(this.name);
}
}
var someuser = new User('byvoid','http://www.byvoid.com');

创建someuser对象时,构造函数的this指调用它的someuser
函数类型的变量指向这个函数实体的一个引用,在引用之间赋值不会对对象产生复制行为。
我们可以通过函数的任何一个引用调用这个函数,但该函数的上下文即this不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var someuser = {
name:'byvoid',
func: function(){
console.log(this.name);
}
};
var foo = {
name:'foobar'
};
someuser.func();//byvoid
foo.func = someuser.func; //引用someuser.func这个函数实体,其实就是someuser.func的代码内容
foo.func();//foobar this是foo

name = 'global';
func = someuser.func;
func();//global

call/apply

让其他对象调用本对象的方法
A对象名.函数属性名.call(B对象名,函数参数表(逗号隔开)); //B调用A的方法
A对象名.函数属性名.apply(B对象名,函数参数数组);//B调用A的方法

bind

永久绑定函数的上下文,使其无论被谁调用,上下文都是固定的
A对象名.函数属性名.bind(B对象名,函数参数表(逗号隔开));
bind方法返回上下文为B对象的A对象的函数
如果在使用bind时,绑定了参数表,则在调用返回函数时,只需要传入未绑定的参数即可
注意不能重复绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var someuser = {
name:'byvoid',
func:function(){
console.log(this.name);
}
};
var foo = {
name:'foobar'
};
func = someuser.func.bind(foo);
func();//foobar

func2 = func.bind(someuser);
func2();//foobar;

原型

构造函数创建属性
除非必须用构造函数闭包,否则尽量用原型定义成员函数
原型定义的成员是多个实例共享的,因此尽量在构造函数内定义一般成员尤其是对象或数组
原型链

1
2
3
4
5
6
7
8
9
10
11
function Foo(){
}
object.prototype.name = 'my object';
Foo.prototype.name = 'Bar';
var obj = new object();
var foo = new Foo();
console.log(obj.name);//my object
console.log(foo.name);//Bar
console.log(foo.__proto__.name);//Bar
console.log(foo.__proto__.__proto__.name);//my object
console.log(foo.__proto__.constructor.prototype.name);//Bar

typeof

【广发证券nodejs应用](http://slides.com/loskael/node#/)

My Little World

深度广度优先遍历+马踏棋盘算法

发表于 2017-03-07

深度优先遍历/深度优化搜索DFS

Traversal1
右手原则:在没有碰到重复顶点的1情况下,分叉路口始终是向右手边走,没路过一个顶点就做一个记号

主要思路是右手原则,把自己想象成从一个顶点出发的小人,每次选择下一步要走的路径时,就选择右手方向的路径,并给自己即将离开的顶点做一个已经遍历过的标记,直到碰到右手选择的路径将要到达的顶点是已经遍历过的顶点,这时则判断从右手方向开始的路径的终点顶点是否已遍历,若没有遍历,则选择该路径继续往下,若碰到所有路径终点顶点都是已经遍历过的,则沿到达该顶点的路径返回上一个顶点,检查上一顶点的其他路径顶点是否已经遍历,未遍历的话,则沿该路径走下去,即碰到所有路径顶点都遍历过则返回上一顶点,以此类推,直到返回起点顶点

整个图的遍历过程算法上是一个递归的过程,遍历方式上类似于树的前序遍历
Traversal2
看蓝色的路径即顶点遍历过程
A->B->C->D->E->F->G->H (往回走,看红色路径顶点都已遍历过)->G->F->E->D->I->D->C->B->A

编程思路:
遍历图的邻接矩阵,邻接矩阵建立时注意选择是使用右手原则还是左手原则,从起点开始遍历该顶点所在行,便利到非0值且对应的列顶点未访问过就开始遍历该列顶点对应顶点所在的行,同样遍历非0未遍历过的顶点,如果该行所有顶点都遍历过则返回上一顶点继续重复遍历找未遍历的顶点,直到返回起点位置
代码链接

马踏棋盘问题

国际象棋的棋盘为8*8的方格棋盘,现将“马”放在任意指定的方格中,按照“马”走棋的规则将“马”进行移动
要求每个方格只能进入一次,最终使得“马”走遍棋盘64个方格

一个位置最多有八种选择,如下
chess
代码链接

图的广度优先遍历

主要思路:树的层序遍历
结构:队列
原则:右手原则
将复杂的图调整结构为层的形状,从最上一层结点唯一结点出发,入队列
遍历第二层,从与上一层结点相连的右手边的结点开始遍历,先将上一层相连的结点出队列,然后第二层结点入队列
以此类推,即在本层遍历时,先将与将要遍历的结点的上层结点出队列,然后将本层结点入队列,
遍历完一个上层结点的本层连接点,先将上层下一个结点出队列,在将上层下一个节点的本层链接点入队列
出队列顺序即遍历顺序
tug1
选定一个起点结点(A),依次遍历与该结点相连结点(B、F),每个结点遍历完后添加遍历标记,
再依次遍历刚刚遍历过的结点(先B后F)的相连结点(CIG、E),同样遍历完要加标记
以此类推,再遍历C的连接点D,G的连接点H
BreadthMaptraversal2
编程思路:
将每次遍历的结点入队列,每遍历完一层,出一次队列,出队列顺序即遍历顺序
代码链接

My Little World

ES6

发表于 2017-03-06

let

1.let 声明的变量在let 命令所在的代码快内有效
2.不声明直接用变量或者先用再声明都会报错
3.暂时性死区:在变量声明之前就使用变量的代码块

1
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

4.不允许在同一级别块域内重复声明一个变量
5.let 声明的变量,外层代码块不受内层代码块的影响
—-外层作用域无法读取内层作用域的变量
—-内层作用域可以定义外层作用域的同名变量
6.块级作用域内声明的函数,行为类似于var声明的变量,仅在所在的代码块内有效

const

1.声明的同时进行复制
2.作用域特征同let
3.当声明的变量为对象或者数组时,只可以改变对象属性或数组元素的值,但不可以将变量重新赋值

ES6 6种声明变量的方法:var、function、let、const、import、class
var命令和function命令声明的全局变量,依旧是顶层对象的属性;let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

解构赋值

数组

1.‘模式匹配’,左右对应相等,变量取值由位置决定
2.当右边的数据量大于左边变量数,如果是单层数组,则依次匹配,多余形成数组赋给最后一个变量
—-如果变量组成多层数组,则依次匹配,剩余的舍弃
3.当右边小于左边变量数,多余的变量的值就等于undefined
4.可以提前给变量赋默认值,默认值可以是已声明的其他变量,
–注意给变量赋‘undefined’的值的时候,如果变量原来的值不严格等于‘===’undefined,将不会被赋值为undefined

对象

1.对象的属性没有次序,变量必须与属性同名,才能取到正确的值,属性是匹配的标志
2.若变量与属性名不一致,则先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者(变量),而不是前者(属性)
3.如果在声明对象变量的时候为属性值的变量赋值,则相当于同时声明属性值的变量
4.进行套嵌赋值时,注意分清属性名和变量名,被赋值的是变量名,不是属性名
5.进行默认值赋值规则同数组,赋空不会生效
6.属性名表达式

1
2
3
4
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3

数组arr的0键对应的值是1,[arr.length - 1]就是2键,对应的值是3

函数参数

类似数组,但注意下面两种写法

1
2
3
4
5
6
7
8
function move({x = 0, y = 0} = {}) {
return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

为变量x和y赋值

1
2
3
4
5
6
7
8
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

为x,y的默认值赋值

字符串、数字、布尔

字符串:字符串被转换成数组
数字/布尔:先转换成对象

字符串方法

方法 es5/js es6 2017
Unicode表示法 只限于\u0000——\uFFFF之间的字符 只要将码点放入大括号,就能正确解读该字符
charCodeAt 只会正确返回16位的UTF-16字符的码点
codePointAt 会正确返回32位的UTF-16字符的码点
String.fromCharCode 只会识别16位的UTF-16字符
String.fromCodePoint 会会识别32位的UTF-16字符
for 不可以识别大于0xFFFF的码点
for…of 可以识别大于0xFFFF的码点
charAt 不能识别码点大于0xFFFF的字符
at 能识别码点大于0xFFFF的字符
normalize ‘\u01D1’===’\u004F\u030C’ //false ‘\u01D1’.normalize() === ‘\u004F\u030C’.normalize()// true
indexOf 确定一个字符串是否包含在另一个字符串中
includes() 返回布尔值,表示是否找到了参数字符串
startsWith 返回布尔值,表示参数字符串是否在源字符串的头部
endsWith 返回布尔值,表示参数字符串是否在源字符串的尾部
repeat(n) 返回一个新字符串,表示将原字符串重复n次。
padStart(),padEnd() padStart()用于头部补全,padEnd()用于尾部补全
模板字符串 反引号(`),保留空格和换行,变量名写在${}中

数值处理方法

方法 es5/js es6 2017
二进制和八进制表示法 在严格模式之中,八进制就不再允许使用前缀0表示 二进制0b或0B、八进制0o或0O
isFinite()/isNaN() 先调用Number()将非数值的值转为数值,再进行判断
Number.isFinite() 检查一个数值是否为有限的,非数值一律返回false。
Number.isNaN() 检查一个值是否为NaN,非数值一律返回false。
parseInt()、parseFloat() 移植到Number对象上面,行为完全保持不变。
Number.isInteger() 判断一个值是否为整数
Number.EPSILON 极小的常量,为浮点数计算设置一个误差范围
Number.isSafeInteger 判断一个整数是否落在整数范围之内
Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER 表示整数范围的上下限
Math.trunc 去除一个数的小数部分,返回整数部分
Math.sign 判断一个数到底是正数、负数、还是零
Math.cbrt 计算一个数的立方根
Math.clz32 返回一个数的32位无符号整数形式有多少个前导0
Math.imul 返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32位的带符号整数。
Math.fround 返回一个数的单精度浮点数形式
Math.hypot 返回所有参数的平方和的平方根
Math.expm1(x) 返回Math.exp(x) - 1
Math.log1p(x) 返回1 + x的自然对数
Math.log10(x) 返回以10为底的x的对数
Math.log2(x) 返回以2为底的x的对数
Math.sign() 判断一个值的正负
指数运算符(**) 指数运算符与Math.pow的实现不相同
Math.sinh(x) 返回x的双曲正弦
Math.cosh(x) 返回x的双曲余弦
Math.tanh(x) 返回x的双曲正切
Math.asinh(x) 返回x的反双曲正弦
Math.acosh(x) 返回x的反双曲余弦
Math.atanh(x) 返回x的反双曲正切

深入理解es6笔记

let/const

常量如果是对象,则对象中的值可以修改

js引擎在扫描代码发现变量声明时,要么将它们提升至作用域顶部(var),要么将声明放到TDZ(let/const),访问TDZ中的变量会触发运行时错误,只有执行过变量声明语句后,变量才会从TDZ中移出,然后可正常访问。
TDZ在块作用域中,相对于let,const所在块作用域来说。
但在let声明的作用域外对该变量使用typeof不会报错

1
2
3
4
console.log(typeof val);
if(condition) {
let value = 'blue'
}

字符串、正则表达式

在对字符串进行比较排序操作前,按照统一标准进行标准化

1
2
3
let normalized = values.map(function(text){
return text.normalize();// 默认NFC格式
})

正则表达式Y修饰符使用注意:
1.只有调用exec()和test()这些正则表达式对象的方法时才会涉及lastIndex属性,调用字符串方法,例如match(),则不会触发粘滞行为
2.对于粘滞正则表达式而言,如果使用^字符串来匹配字符串开端,只会从字符串的起始位置或多行模式的首行进行匹配。

函数

函数name属性不一定引用同名变量,他只是协助调试用的额外信息,所以不能使用name属性的值来获取对函数的引用

js函数有两个不同的内部[[Call]]和[[Construct]]
当通过new关键字调用函数时,执行的是[[Construct]]函数,他负责创建一个被称作实例的新对象
然后在执行函数将this绑定到实例上;
如果不通过new关键字调用函数,则执行[[Call]]函数,从而直接执行代码中的函数体
具有[[Construct]]方法的函数被统称为构造函数。

可以在函数内部使用new.target来判断是否在使用new 操作符调用函数,
以及判断是否被某个特定构造函数调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1.
function Person(name){
if(typeof new.target !== 'undefined'){
this.name = name;
}else {
throw new Error('必须通过new 操作符调用该函数')
}
}
2.
function Person(name){
if(typeof new.target !== 'Person'){
this.name = name;
}else {
throw new Error('必须通过new 操作符调用该函数')
}
}
function AnotherPerson(name){
Person.call(this,name)
}

var person = new Person('kely');
var anotherPerson = new AnotherPerson('kely'); // 抛出错误

如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this;
否则this会被设置为全局对象

对象

在使用===进行比较时,+0和-0得到结果相等;NaN和自身比较不相等
但可以使用Object.is()方法弥补
Object.js(+0,-0)得到false;
Object.js(NaN,NaN)得到true.

Object.assign()方法不能将提供者的访问器属性复制到接收对象中
会被转变成接收对象中的一个数据属性。

对象自有属性枚举顺序的基本规则是
1.所有数字键按升序排序
2.所有字符串键按照他们被加入对象的顺序排序
3.所有Symbol键按照他们被加入对象的顺序排序

Super引用相当于指向对象原型的指针,实际上也就是Object.getPrototypeof(this)的值

解构

获取数组副本

1
2
3
let colors = ['red','green'];

let [...cloneColor] = colors;

通过给解构参数提供默认值,将解构参数定义为可选的

1
function setCookie(name, value ,{secure,path,domain}={}){}

为解构参数指定默认值

1
2
3
4
5
6
7
8
9
10
const setCookieDefaults = {
secure: false,
path:'/',
damin:'example.com'
}
function setCookie(name,value,{
secure = setCookieDefaults.secure,
path = setCookieDefaults.path,
domain = setCookieDefaults.domain
} = setCookieDeaults)

Symbol

使用Symbol.for()可以创建可共享的Symbol
使用Symbol.keyFor()可以在Symbol全局注册表中检索共享的Symbol
使用Symbol创建的Symbol属性不能用Symbol.keyFor()去查找

Symbol类型的值不可以被转化为数字或者字符串

Object.getOwnPropertySymbol()可以返回包含所有Symbol自有属性的数组

一些特殊的Symbol值被用于暴露更多语言内部逻辑
主要使用思路就是通过这些特殊Symbol属性覆盖掉原始方法的响应方式
例如
给一个对象A定义
|属性|覆盖方法|被调用时机|
|—|—|—|
|Symbol.hasInstance|instanceOf|C instanceOf A|
|Symbol.toPrimitive|默认数字字符串转换值|A+’!’|
|Symbol.toStringTag|Object.prototype.toString(),Object.prototype.toString.call()方法时的返回值|A.toString或者Object.prototype.toString.call(A)|

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
1.
默认模式只用于==,+及Date构造函数传递一个参数时,大多数操作使用字符串、数字模式
function Temperature(degrees) {
this.degerees = degrees;
}

Temperature.prototype[Symbol.toPrimitive] = function(hint){
switch(hint) {
case 'string' : return this.degerees+'123'
case 'number': return this.degerees
case 'default': return this.degerees+' degrees'
}
}

var freezing = new Temperature(32);
console.log(freezing + '!'); // 32 degrees
console.log(freezing/2);// 16
console.log(String(freezing));// 32123

2.Symbol.toString 的使用
覆盖Object.prototype.toString
function Person(name){
this.name = name
}
Person.prototype[Symbol.toString] = 'Person'

Person.prototype.toString = function() {
return this.name
}

var me = new Person('ww');

console.log(me.toString())// ww
console.log(Object.prototype.toString.call(me)) // [object.Person]

用于覆盖正则表达式的方法

1
2
3
4
5
6
7
8
9
10
11
12
let hasLengthOf10 = {
[Symbol.match]: function(value){return ...},
[Symbol.replace]: function(value,replacement){return 。。。},
[Symbol.search]: function(value){return ...},
[Symbol.split]: function(value){return ...},
}

let str = 'hwrwrirrrk';
console.log(str.match(hasLengthOf10))
console.log(str.replace(hasLengthOf10))
console.log(str.search(hasLengthOf10))
console.log(str.split(hasLengthOf10))

Set 和 Map

Set中如果存储对象的话,是强引用,只要Set实例中引用存在,垃圾回收机制就不能释放该对象的内存空间。
WeakSet 只存储对象弱引用,并且不可以存储原始值,如果集合中的弱引用是对象的唯一引用,则会被回收并释放相应内存
所以如果只需要跟踪对象引用,应该使用WeakSet集合而不是普通的Set集合。

WeakMap集合中的键名必须是一个对象,如果使用非对象键名会报错:
集合中保存的是这些对象的弱引用,如果在弱引用之外不存在其他的强引用,
引擎的垃圾回收机制会自动回收这个对象,同时也会移除WeakMap集合中的键值对
但是只有集合的键名遵从这个规则,键名对应的值如果是一个对象,则保存的是对象的强引用,不会触发垃圾回收机制

数组方法

find和findIndex方法都接受两个参数,一个是回调函数,
另一个是可选参数,用于指定回调函数中this的值

fill 方法可以传入第二个和第三个参数用于只改变一部分值,分别指定开始索引和不包含结束索引

Proxy 和 Reflect

使用Proxy的陷阱方法,可以拦截对象的默认特性方法
对应的Reflect方法可以在proxy陷阱方法中重新使用对象的内减特性

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
let target = {
name: 'target'
}

let proxy = new Proxy(target,{
// set 写入一个属性时
// 代理目标对象target,要写入的键,要写入的值,proxy代理本身
set (trapTarget,key,value,receiver){
// 忽略不希望受到影响的已有属性
if(!trapTarget.hasOwnProperty(key)){
if(isNaN(value)){
throw new TypeError('属性必须是数字')
}
}
// 添加属性
return Reflect.set(trapTarget,key,value,receiver);
}

// 读取一个属性时
// 代理目标对象,要读的键,代理对象本身
get(trapTarget,key,receiver) {
if(!key in receiver){
throw new TypeError('属性'+key+'不存在')
}

return Reflect.get(trapTarget,key,receiver)
}

// 使用in操作符时
has(trapTarget,key){
if(key === 'value'){
return false;
} else {
return Reflect.has(trapTarget,key)
}
}

// 使用delete操作符删除对象属性时
deleteProperty(trapTarget,key){
if(key === 'value'){
return false;
} else {
return Reflect.deleteProperty(trapTarget,key)
}
}

// Object.getPrototypeOf(proxy)时
//区别
// Object.getPrototypeOf()如果传入值非对象会将参数强制转成对象
// Reflect.getPrototypeOf()如果传入非对象,则报错
getPrototypeOf(trapTarget) {
// return null
return Reflect.getPrototypeOf(trapTarget) //返回默认行为,但有上述区别
}

// Object.setPrototypeOf(proxy, {})时
// 区别
// Object.getPrototypeOf()操作失败就报错
// Reflect.setPrototypeOf()返回布尔来表示操作是否成功
setPrototypeOf(trapTarget,proto) {
// return false
return Reflect.setPrototypeOf(trapTarget,proto)
}

// Object.isExtensible(proxy)时,
// 返回布尔值,表示对象是否可扩展
// 区别
// Object.isExtensible() 传入非对象返回false
// Reflect.isExtensible() 传入非对象报错
isExtensible(trapTarget) {
// return null
return Reflect.isExtensible(trapTarget)
}

// Object.preventExtension(proxy)时,
// 阻止对象扩展,返回布尔表示操作是否成功
// 区别
// Object.preventExtension()无论参数是否为对象都返回参数
// Reflect.preventExtension()参数非对象报错,否则根据操作是否成功返回布尔值
preventExtension(trapTarget) {
// return null
return Reflect.preventExtension(trapTarget) // 会转发到底层,target也会被阻止掉
}

// Object.defineProperty(proxy)时,
// 区别
// Object.defineProperty()返回第一个参数
// Reflect.defineProperty()根据操作是否成功返回布尔值
defineProperty(trapTarget,key,descriptor) {
return Reflect.defineProperty(trapTarget,key,descriptor)
}

// Object.getOwnPropertyDescriptor(proxy)时,
// 区别
// Object.getOwnPropertyDescriptor()传入非对象转成对象
// Reflect.getOwnPropertyDescriptor()传入非对象报错
getOwnPropertyDescriptor(trapTarget,key) {
return Reflect.getOwnPropertyDescriptor(trapTarget,key)
}

// 拦截内部方法[[OwnPropertyKeys]],
// Object.keys(),Object.getOwnPropertyName() (不含Symbol属性)
// Object.assign()(字符串和Symbol属性),Object.getOwnPropertySymbols()(仅含Symbol属性)
//4个方法调用时会被拦截到,输出的数组再被以上4个方法处理返回
ownKeys(trapTarget) {
return Reflect.ownKeys(trapTarget).filter(key=>{
return typeof key !== 'string' || key[0] !== '_'
})
}

// 代理目标是一个函数才可以,函数被普通调用执行时会被拦截
// 代理目标函数,函数被调用时内部this值,传给函数的参数数组
apply(trapTarget,thisArg, argumentList) {
return Reflect.apply(trapTarget,thisArg, argumentList)
}
// 通过这样操作,可以实现不用new 调用函数,实现new调用函数的效果
apply(trapTarget,thisArg, argumentList) {
return Reflect.construct(trapTarget,thisArg, argumentList)
}

// 代理目标是一个函数才可以,函数被new调用时会被拦截
// 代理目标函数,传给函数的参数数组
construct(trapTarget,argumentList) {
return Reflect.construct(trapTarget, argumentList)
}

})

// proxy是可被插销的代理对象,调用revoke方法后,不能在通过proxy代理到target对象
let { proxy,revoke } = Proxy.revocable(target,{})
revoke()

模块

用逗号将默认的本地名称与大括号包裹的非默认值分隔开,请记住,在import语句中,默认值必须排在非默认值之前。

My Little World

js 的~~

发表于 2017-03-06

~~ 符号是一种类型转换符号,将参数转换成数据类型
~~true == 1
~~false == 0
~~”” == 0
~~[] == 0
~~undefined ==0
~~!undefined == 1
~~null == 0
~~!null == 1

应用:计算大数相加
function add(a,b) {
// 实现该函数
var res=’’, c=0;
a = a.split(‘’);
b = b.split(‘’);
while (a.length || b.length || c){
c += ~~a.pop() + ~~b.pop();//对应位相加,加上前一位进位
res = c % 10 + res;//去掉进位,累积拼接取对应位相加后的结果,
c = c>9;//本轮相加,有进位,c是1,没进位c是0
}
return res.replace(/^0+/,’’);//把开头0去掉
}

My Little World

正则表达式

发表于 2017-03-06

判断邮箱格式

1
2
var match = /.+@.+\.[a-zA-Z]{2,4}$/;
match.test(变量)

输入框input只允许输入数字

在input标签里添加:
onKeyUp=”value=value.replace(/[^\d]/g,’’)”

以上方法input 限制了仅输入数字,但更换中文输入法之后还能输入出数字之外字符
解决办法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
html:
<input type="text" class="form-control form-filter" id="inputorp_i" autocomplete="off" ng-model="min_score" placeholder="正整数">
js:
var bind_name = 'input';
   if (navigator.userAgent.indexOf("MSIE") != -1){
   bind_name = 'propertychange';
  }
   $('#inputorp_i').bind(bind_name, function(){
  clearNoNum(this);
   })
$('#inputorp_i1').bind(bind_name, function(){
  clearNoNum(this);
   })
function clearNoNum(obj)

{
//先把非数字的都替换掉,除了数字
obj.value = obj.value.replace(/[^\d]/g,"");
}

判断输入值是否是url

1
2
3
 javascript
var match = /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?$/;
match.test(变量)

常用正则表达式

My Little World

图的存储结构

发表于 2017-03-01

1.邻接矩阵

1.无向图

graph1
一个一维数组存储图中顶点信息,

顶点数组 v0 v1 v2 v3

一个二维数组(邻接矩阵)存储图中边或弧的信息
0表示不存在顶点间的边,1表示顶点间存在边

v0 v1 v2 v3
v0 0 1 1 1
v1 1 0 1 0
v2 1 1 0 1
v3 1 0 1 0

无向图构成的邻接矩阵为对称矩阵,对角线值为0
—–顶点Vi在邻接矩阵中第i行(或第i列)的元素之和为该顶点的度
—–扫描矩阵中第i行元素,元素为1的列为顶点Vi的邻接点

2.有向图

graph2
一个一维数组存储图中顶点信息,

顶点数组 v0 v1 v2 v3

坐标[Vi][Vj]的值,1表示Vi指向Vj的弧存在,0表示不存在

v0 v1 v2 v3
v0 0 0 0 1
v1 1 0 1 0
v2 1 1 0 0
v3 0 0 0 0

第Vi列的元素之和为顶点Vi的入度
第Vi行的元素之和为顶点Vi的出度

3.网

tu3
邻接矩阵各元素的值不再用0/1表示,而是用权表示
顶点间若不存在弧,则用\infty无穷表示,顶点自己到自己的权为0

v0 v1 v2 v3
v0 0 \infty \infty 18
v1 8 0 2 \infty
v2 4 \infty 0 \infty
v3 \infty \infty \infty 0

2.邻接表

无向图

顶点用一个一维数组存储
每个顶点Vi的所有邻接点构成一个线性表,由于个数不确定,用单链表来表示
tu4
tu5

有向图

把顶点当弧尾建立邻接表,邻接表长度就是该顶点出度
tu6
把顶点当弧头建的表叫逆邻接表,邻接表长度就是该顶点入度
tu7

网

在边表结点定义中再增加一个数据域来存储权值
tu8

3.十字链表(有向图)

顶点表结构

|data|firstIn|firstOut|
data:顶点数据
firstIn:第一个入边表的指针
firstOut:第一个出边表的指针

边表结点结构 表示以条边

tailVex headVex headLink tailLink

tailVex:该弧起点的顶点在顶点表的下标
headVex:该弧终点的顶点在顶点表的下标
headLink:指向headVex的弧的指针
tailLink: 从tailVex出发的弧的指针
graph2
tu9

4.邻接多重表(无向表)

关注对象是图中的表不是顶点
|iVex|iLink|jVex|jLink|
iVex和jVex是与某条边依附的两个顶点在顶点表中的下标
iLink:指向依附顶点iVex的下一条边,
jLink:指向依附顶点jVex的下一条边,
tu10

5.边集数组

由两个一维数组构成
一个是存储顶点的信息
另一个存储边的信息
边数组的每个数据元素由一条边的起点下标(begin)、终点下标(end)和权(weight)组成

tu11

顶点数组 v0 v1 v2 v3
边数组 begin end weight
edges[0] 0 3 5
edges[1] 1 0 4
edges[2] 1 2 3
edges[3] 2 0 8
My Little World

日期格式兼容

发表于 2017-02-26

IE11/chrome/360/火狐兼容日期格式兼容

$scope.date= new Date().toLocaleDateString().replace(/\//g,’-‘);
获取到的”year-month-day”格式支持/chrome/360/火狐,但到了IE11就变成”year年month月day”

var date = new Date();
var y = date.getFullYear();
var m = date.getMonth()+1;
var d = date.getDate();
var day = date.getDay();
var dateString=y+”-“+m+”-“+d;
$scope.date= dateString;

这样获取的”year-month-day”格式支持以上四种浏览器

My Little World

AngularJs自定义指令实现图片上传

发表于 2017-02-22

//定义file-Model指令的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MetronicApp.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs, ngModel) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(event){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
//附件预览
scope.file = (event.srcElement || event.target).files[0];
scope.getFile();
this.value = ''; //清除已上传的文件,否则无法连续上传相同文件
});
}
};
}
])

//在HTML使用时的代码

1
2
3
4
5
6
7
8
9
10
 <div class="form-group">
<label class="control-label col-md-3">图片上传</label>
<div class="col-md-9 fileinput">
<img src="{{detaildata.banner}}" name="img" style="width:40px;height: 40px;">
<span class="btn red btn-outline btn-file" style="margin-left: 10px;margin-top:0px;">
<span class="fileinput-new"> 重新上传 </span>
<input type="hidden"><input id="upload" type="file" file-model="myFile" name="..." accept="image/*" >
</span>
</div>
</div>

//Controller中上传图片时的js函数

1
2
3
4
5
6
7
8
$scope.getFile = function () {
var postData = {
fileName: $scope.file
};
fileReader.readAsDataUrl($rootScope.settings.url + "", postData).success( function(response) {
$scope.detaildata.banner = response;
});
};

//定义fileReader服务的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
angular.module('MetronicApp')
.factory('fileReader', ["$http", function($http){
var readAsDataURL = function (url, data) {
var fd = new FormData();
angular.forEach(data, function(val, key) {
fd.append(key, val);
});
var args = {
method: 'POST',
url: url,
data: fd,
headers: {'Content-Type': undefined},
transformRequest: angular.identity
};
return $http(args);
};
return {
readAsDataUrl: readAsDataURL
};
}])

整个上传的过程,点击“重新上传”按钮,file-model指令绑定的change事件会检测本次选择的图片与上次是否为一致,不一致则会执行file-model定义时的代码
element.bind(‘change’, function(event){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
//附件预览
scope.file = (event.srcElement || event.target).files[0]; //给Controller 中$scope.file赋值
scope.getFile(); //调用Controller 中 $scope.getFile();
this.value = ‘’; //清除已上传的文件,否则无法连续上传相同文件
});
控制器中的$scope.getFile()函数用到fileReader.readAsDataUrl函数上传图片给后台,这个函数在定义fileReader时被定义

定义指令时,若没有清除已上传文件,连续多次上传时,将导致无法连续上传相同图片的bug.

1…192021…25
YooHannah

YooHannah

246 日志
1 分类
21 标签
RSS
© 2025 YooHannah
由 Hexo 强力驱动
主题 - NexT.Pisces