MongoDB 聚合统计
mongodb • lopo1983 发表了文章 • 0 个评论 • 1230 次浏览 • 2018-09-14 14:56
db.集合名称.aggregate({管道:{表达式}})
?管道
?
管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的输入 在mongodb中,管道具有同样的作用,文档处理完毕后,通过管道进行下一次处理 常用管道
$group:将集合中的文档分组,可用于统计结果$match:过滤数据,只输出符合条件的文档$project:修改输入文档的结构,如重命名、增加、删除字段、创建计算结果$sort:将输入文档排序后输出$limit:限制聚合管道返回的文档数$skip:跳过指定数量的文档,并返回余下的文档$unwind:将数组类型的字段进行拆分
?
?
表达式
?
处理输入文档并输出
?
$sum:计算总和?
$avg:计算平均值?
$min:获取最小值?
$max:获取最大值?
$push:在结果文档中插入值到一个数组中?
$first:根据资源文档的排序获取第一个文档数据?
$last:根据资源文档的排序获取最后一个文档数据?
例子
表结构:
const compAccountDealrecordSchema = new Schema({
// billing 订单标识
numer: {
type: String
},
// 公司名称
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 消费金额
amount: {
type: Number,
default: 0.00
},
// 账户余额
balance: {
type: Number,
default: 0.00
},
// 渠道类型
channelType: {
type: String,
default: 'BALANCE'
},
// 日账单
dailyBill: {
type: Boolean,
default: false
},
// 按需扣费账单
debtDeduct: {
type: Boolean,
default: false
},
// 描述
detail: {
type: String,
default: ''
},
// 账单
detailRequest: {
type: Schema.Types.ObjectId,
ref: 'Company',
},
// 账单类型
detailType: {
type: String,
default: 'LINK'
},
// 账单链接
detailUrl: {
type: String,
default: ''
},
// 订单ID
orderId: {
type: Schema.Types.ObjectId,
ref: 'comp_account_billing_order'
},
// 账单产品详细
serviceItem: [],
// 产品类型
serviceType: {},
// 交易类型 [RECHARGE:收入,CONSUME:支出]
transactionType: {
type: String,
default: 'CONSUME'
},
// 备注
remark: {
}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});静态方法
compAccountDealrecordSchema.statics = {
getAll: async function(queryMix, _comp) {
const {
querys,
select
} = queryMix;
const models = this.find({
...!!_comp && {
_comp
}
})
.sort({
'_id': -1,
});
try {
return {
data: {
result: await models,
totalCount: await models.count()
}
};
} catch (error) {
return error
}
},
getOne: async function(_id, _comp) {
const MODELS = this.findOne({..._comp && {
_comp
},
_id
}).sort({
'_id': -1
});
try {
return {
data: await MODELS
}
} catch (error) {
return error
}
},
createOne: async function(bodys) {
return await this.create(bodys)
},
backCashAdd: async function(bodys, auth) {
const {
_comp,
serviceBillCash,
serviceBillChannel,
serviceBillRemark
} = bodys
// 生成流水
await this.create({
'_comp': _comp,
'serviceBillProduct': '-',
'serviceBillType': '充值',
'serviceBillCash': `+${serviceBillCash}`,
'serviceBillChannel': `${serviceBillChannel}`,
'serviceBillRemark': `手工入款/n${serviceBillChannel}/n${serviceBillRemark}`,
'_admin': auth.id
});
return await ctx.model.Company.cashFN(serviceBillCash, auth, 'cash', 'add', _comp);
},
seachComp: async function(name) {
return await ctx.model.Company.find({
'compName': eval(`/${name}.*/i`)
}, {
'compEmail': 1,
'compName': 1,
'compPhone': 1
})
},
// 图表数据
/**
*
* @param {*} type 订单类型
* @param {*} scope 月份计算
* @param {*} _comp
*/
getPanelChart: async function(type = 'all', classify = "line", _comp) {
const MODEL = this.aggregate([
...classify === 'pie' ? [{
$unwind: `$serviceItem`,
}] : [],
...[{
$match: {
'_comp': mongoose.Types.ObjectId(_comp),
'created': {
"$gt": new Date(subtractMoment(1, 'month'))
},
"channelType": "CASH",
...type !== 'all' && {
'serviceItem.serviceType': type
}
}
},
{
$project: {
'_comp': 1,
'amount': 1,
'created': 1,
"serviceItem": 1,
'day': {
$substr: ["$created", 0, 10]
}
}
},
{
$group: {
...classify === "line" ? {
'_id': `$day`
} : {
'_id': '$serviceItem.serviceType'
},
'count': {
$sum: classify === 'pie' ? '$serviceItem.price' : '$amount'
}
}
},
{
$project: {
...classify === "line" ? {
'days': '$_id'
} : {
'serviceType': '$_id'
},
'count': 1,
_id: 0
}
},
{
$sort: {
'days': 1
}
}
]
]);
try {
const lastMonth = subtractMoment(1, 'month', 'subtract');
const DateMaps = (getDayAll(lastMonth, new Date()));
const data = await MODEL;
return {
...classify === 'pie' ? {
data
} : {
'data': {
datas: DateMaps.reduce((a, b) => {
let count;
data.forEach(e => {
if (e.days === b) count = e.count
});
a.push(count ? count : 0);
return a
}, []),
dates: DateMaps
}
}
}
} catch (error) {
console.log(error)
}
},
/**
* @param {String} type 产品类型()
*/
getCashCount: async function(ctx, auth, type, _comp) {
const {
endDate
} = ctx.helper.getPrevMoment('month');
const yestoday = ctx.helper.subtractMoment(1, 'day');
const typeoObj = type === 'all' ? {} : {
'serviceBillProduct': type,
'_comp': mongoose.Types.ObjectId(_comp ? _comp : auth.id)
};
return [(await this.aggregate([{
$match: {...typeoObj,
'created': {
"$gt": new Date(endDate)
},
'serviceBillType': '消费'
}
},
{
$group: {
'_id': {
'_comp': auth.id
},
'monthCashSum': {
$sum: "$serviceBillCash"
}
}
},
{
$project: {
'_id': 0
}
}
]))[0], (await this.aggregate([{
$match: {...typeoObj,
'created': {
"$gt": new Date(yestoday)
},
'serviceBillType': '消费'
}
},
{
$group: {
'_id': {
'_comp': auth.id
},
'dayCashSum': {
$sum: "$serviceBillCash"
}
}
},
{
$project: {
'_id': 0
}
},
]))[0]]
},
}; 查看全部
聚合(aggregate)主要用于计算数据,类似sql中的sum(),avg()。
db.集合名称.aggregate({管道:{表达式}})
?管道
?
管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的输入 在mongodb中,管道具有同样的作用,文档处理完毕后,通过管道进行下一次处理 常用管道
- $group:将集合中的文档分组,可用于统计结果
- $match:过滤数据,只输出符合条件的文档
- $project:修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
- $sort:将输入文档排序后输出
- $limit:限制聚合管道返回的文档数
- $skip:跳过指定数量的文档,并返回余下的文档
- $unwind:将数组类型的字段进行拆分
?
?
表达式
?
处理输入文档并输出
?
$sum:计算总和?
$avg:计算平均值?
$min:获取最小值?
$max:获取最大值?
$push:在结果文档中插入值到一个数组中?
$first:根据资源文档的排序获取第一个文档数据?
$last:根据资源文档的排序获取最后一个文档数据?
例子
表结构:
const compAccountDealrecordSchema = new Schema({静态方法
// billing 订单标识
numer: {
type: String
},
// 公司名称
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 消费金额
amount: {
type: Number,
default: 0.00
},
// 账户余额
balance: {
type: Number,
default: 0.00
},
// 渠道类型
channelType: {
type: String,
default: 'BALANCE'
},
// 日账单
dailyBill: {
type: Boolean,
default: false
},
// 按需扣费账单
debtDeduct: {
type: Boolean,
default: false
},
// 描述
detail: {
type: String,
default: ''
},
// 账单
detailRequest: {
type: Schema.Types.ObjectId,
ref: 'Company',
},
// 账单类型
detailType: {
type: String,
default: 'LINK'
},
// 账单链接
detailUrl: {
type: String,
default: ''
},
// 订单ID
orderId: {
type: Schema.Types.ObjectId,
ref: 'comp_account_billing_order'
},
// 账单产品详细
serviceItem: [],
// 产品类型
serviceType: {},
// 交易类型 [RECHARGE:收入,CONSUME:支出]
transactionType: {
type: String,
default: 'CONSUME'
},
// 备注
remark: {
}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
compAccountDealrecordSchema.statics = {
getAll: async function(queryMix, _comp) {
const {
querys,
select
} = queryMix;
const models = this.find({
...!!_comp && {
_comp
}
})
.sort({
'_id': -1,
});
try {
return {
data: {
result: await models,
totalCount: await models.count()
}
};
} catch (error) {
return error
}
},
getOne: async function(_id, _comp) {
const MODELS = this.findOne({..._comp && {
_comp
},
_id
}).sort({
'_id': -1
});
try {
return {
data: await MODELS
}
} catch (error) {
return error
}
},
createOne: async function(bodys) {
return await this.create(bodys)
},
backCashAdd: async function(bodys, auth) {
const {
_comp,
serviceBillCash,
serviceBillChannel,
serviceBillRemark
} = bodys
// 生成流水
await this.create({
'_comp': _comp,
'serviceBillProduct': '-',
'serviceBillType': '充值',
'serviceBillCash': `+${serviceBillCash}`,
'serviceBillChannel': `${serviceBillChannel}`,
'serviceBillRemark': `手工入款/n${serviceBillChannel}/n${serviceBillRemark}`,
'_admin': auth.id
});
return await ctx.model.Company.cashFN(serviceBillCash, auth, 'cash', 'add', _comp);
},
seachComp: async function(name) {
return await ctx.model.Company.find({
'compName': eval(`/${name}.*/i`)
}, {
'compEmail': 1,
'compName': 1,
'compPhone': 1
})
},
// 图表数据
/**
*
* @param {*} type 订单类型
* @param {*} scope 月份计算
* @param {*} _comp
*/
getPanelChart: async function(type = 'all', classify = "line", _comp) {
const MODEL = this.aggregate([
...classify === 'pie' ? [{
$unwind: `$serviceItem`,
}] : [],
...[{
$match: {
'_comp': mongoose.Types.ObjectId(_comp),
'created': {
"$gt": new Date(subtractMoment(1, 'month'))
},
"channelType": "CASH",
...type !== 'all' && {
'serviceItem.serviceType': type
}
}
},
{
$project: {
'_comp': 1,
'amount': 1,
'created': 1,
"serviceItem": 1,
'day': {
$substr: ["$created", 0, 10]
}
}
},
{
$group: {
...classify === "line" ? {
'_id': `$day`
} : {
'_id': '$serviceItem.serviceType'
},
'count': {
$sum: classify === 'pie' ? '$serviceItem.price' : '$amount'
}
}
},
{
$project: {
...classify === "line" ? {
'days': '$_id'
} : {
'serviceType': '$_id'
},
'count': 1,
_id: 0
}
},
{
$sort: {
'days': 1
}
}
]
]);
try {
const lastMonth = subtractMoment(1, 'month', 'subtract');
const DateMaps = (getDayAll(lastMonth, new Date()));
const data = await MODEL;
return {
...classify === 'pie' ? {
data
} : {
'data': {
datas: DateMaps.reduce((a, b) => {
let count;
data.forEach(e => {
if (e.days === b) count = e.count
});
a.push(count ? count : 0);
return a
}, []),
dates: DateMaps
}
}
}
} catch (error) {
console.log(error)
}
},
/**
* @param {String} type 产品类型()
*/
getCashCount: async function(ctx, auth, type, _comp) {
const {
endDate
} = ctx.helper.getPrevMoment('month');
const yestoday = ctx.helper.subtractMoment(1, 'day');
const typeoObj = type === 'all' ? {} : {
'serviceBillProduct': type,
'_comp': mongoose.Types.ObjectId(_comp ? _comp : auth.id)
};
return [(await this.aggregate([{
$match: {...typeoObj,
'created': {
"$gt": new Date(endDate)
},
'serviceBillType': '消费'
}
},
{
$group: {
'_id': {
'_comp': auth.id
},
'monthCashSum': {
$sum: "$serviceBillCash"
}
}
},
{
$project: {
'_id': 0
}
}
]))[0], (await this.aggregate([{
$match: {...typeoObj,
'created': {
"$gt": new Date(yestoday)
},
'serviceBillType': '消费'
}
},
{
$group: {
'_id': {
'_comp': auth.id
},
'dayCashSum': {
$sum: "$serviceBillCash"
}
}
},
{
$project: {
'_id': 0
}
},
]))[0]]
},
};
webhook
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1130 次浏览 • 2018-08-16 18:58
ssh-keygen -t rsa -C example@mail.com
ssh-keygen -t rsa -C example@mail.com
汉字转拼音 字典整理
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1384 次浏览 • 2018-08-02 22:08
代码: const zd = [...document.querySelectorAll('font b')].map(e => e.nextSibling.nextSibling).filter(e => e != null).map(e => {
const OBJ = {};
OBJ[e.previousSibling.previousSibling.innerText] = ([...e.data.trim().replace(/\([^\)]*\)/g, "")].reduce((a, b) => a += '\\u' + b.charCodeAt(0).toString(16), ''));
return OBJ
}); 查看全部
代码:
const zd = [...document.querySelectorAll('font b')].map(e => e.nextSibling.nextSibling).filter(e => e != null).map(e => {
const OBJ = {};
OBJ[e.previousSibling.previousSibling.innerText] = ([...e.data.trim().replace(/\([^\)]*\)/g, "")].reduce((a, b) => a += '\\u' + b.charCodeAt(0).toString(16), ''));
return OBJ
});
centos nodejs
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1289 次浏览 • 2018-07-24 20:36
curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -2.
sudo yum -y install nodejs3.
npm config set registry https://registry.npm.taobao.org
骚骚的reduce
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1470 次浏览 • 2018-07-05 02:04
accumulatorcurrentValuecurrentIndexarray
回调函数第一次执行时,accumulator?和currentValue的取值有两种情况:调用reduce时提供initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;没有提供?initialValue,accumulator取数组中的第一个值,currentValue取数组中的第二个值
?
计算某个字符出现的次数[...'abcdaabdcfggtre'].reduce((a,b)=>{a<strong>?a<strong>++:a<strong>=1;return a},{})[b][b]字母游戏[/b][/b]const anagrams = str => {
if (str.length <= 2) {
return str.length === 2 ? [str, str[1] + str[0]] : str;
}
return str.split("").reduce((acc, letter, i) => {
return acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val));
}, );
}
anagrams("abc");[b][b]累加器[/b][/b]const sum = arr => arr.reduce((acc, val) => acc + val, 0);
sum([1, 2, 3]);[b][b]计数器[/b][/b]const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);
countOccurrences([1, 2, 3, 2, 2, 5, 1], 1);[b][b]函数柯里化[/b][/b]const curry = (fn, arity = fn.length, ...args) =>
arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);
curry(Math.pow)(2)(10);
curry(Math.min, 3)(10)(50)(2);
[b][b]通过判断函数的参数取得当前函数的length(当然也可以自己指定),如果所传的参数比当前参数少,则继续递归下面,同时储存上一次传递的参数。[/b][/b]
[b][b]数组扁平化[/b][/b]const deepFlatten = arr =>arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), );
deepFlatten([1, [2, [3, 4, [5, 6]]]]);
[b][b]生成菲波列契数组[/b][/b]const fibonacci = n => Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), );
fibonacci(5);[b][b]pipe函数生成器[/b][/b]const pipe = (...funcs) => arg => funcs.reduce((acc, func) => func(acc), arg);
pipe(btoa, x => x.toUpperCase())("Test");
[b][b]通过对传递的参数进行函数加工,之后将加工之后的数据作为下一个函数的参数,这样层层传递下去。[/b][/b]
[b][b]compose函数生成器[/b][/b]const dispatch = action => {
console.log('action', action);
return action;
}
const middleware1 = dispatch => {
return action => {
console.log("middleware1");
const result = dispatch(action);
console.log("after middleware1");
return result;
}
}
const middleware2 = dispatch => {
return action => {
console.log("middleware2");
const result = dispatch(action);
console.log("after middleware2");
return result;
}
}
const middleware3 = dispatch => {
return action => {
console.log("middleware3");
const result = dispatch(action);
console.log("after middleware3");
return result;
}
}
const compose = middlewares => middlewares.reduce((a, b) => args => a(b(args)))
const middlewares = [middleware1, middleware2, middleware3];
const afterDispatch = compose(middlewares)(dispatch);
const testAction = arg => {
return { type: "TEST_ACTION", params: arg };
};
afterDispatch(testAction("1111"));[b][b]?
数据加工函数[/b][/b]const reducers = {
totalInEuros: (state, item) => {
return state.euros += item.price * 0.897424392;
},
totalInYen: (state, item) => {
return state.yens += item.price * 113.852;
}
};
const manageReducers = reducers => {
return (state, item) => {
return Object.keys(reducers).reduce((nextState, key) => {
reducers[key](state, item);
return state;
}, {})
}
}
const bigTotalPriceReducer = manageReducers(reducers);
const initialState = { euros: 0, yens: 0 };
const items = [{ price: 10 }, { price: 120 }, { price: 1000 }];
const totals = items.reduce(bigTotalPriceReducer, initialState);[b][b]对象空值判断[/b][/b]const get = (p, o) => p.reduce((xs, x) => (xs && xs[x] ? xs[x] : null), o);[b][b]分组[/b][/b]const groupBy = (arr, func) =>
arr.map(typeof func === 'function' ? func : val => val[func]).reduce((acc, val, i) => {
acc[val] = (acc[val] || ).concat(arr);
return acc;
}, {});
groupBy([6.1, 4.2, 6.3], Math.floor);
groupBy(['one', 'two', 'three'], 'length');[b][b]对象过滤[/b][/b]const pick = (obj, arr) =>
arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});
pick({ a: 1, b: '2', c: 3 }, ['a', 'c']);[b][b]promise顺序执行[/b][/b]const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
const delay = d => new Promise(r => setTimeout(r, d));
const print = args => new Promise(r => r(args));
runPromisesInSeries([() => delay(1000), () => delay(2000), () => print('hello')])[b][b]排序函数[/b][/b]const orderBy = (arr, props, orders) =>
[...arr].sort((a, b) =>
props.reduce((acc, prop, i) => {
if (acc === 0) {
const [p1, p2] = orders && orders === 'desc' ? [b[prop], a[prop]] : [a[prop], b[prop]];
acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
}
return acc;
}, 0)
);
const users = [{ name: 'fred', age: 48 }, { name: 'barney', age: 36 }, { name: 'fly', age: 26 }];
orderBy(users, ['name', 'age'], ['asc', 'desc']);
orderBy(users, ['name', 'age']);[b][b]快速选择器[/b][/b]const select = (from, selector) =>
selector.split('.').reduce((prev, cur) => prev && prev[cur], from);
const obj = { selector: { to: { val: 'val to select' } } };
select(obj, 'selector.to.val'); 查看全部
arr.reduce(callback[, initialValue])
reduce为数组中的每一个元素依次执行callback函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:- accumulator
- currentValue
- currentIndex
- array
回调函数第一次执行时,accumulator?和currentValue的取值有两种情况:调用reduce时提供initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;没有提供?initialValue,accumulator取数组中的第一个值,currentValue取数组中的第二个值
?
计算某个字符出现的次数
[...'abcdaabdcfggtre'].reduce((a,b)=>{a<strong>?a<strong>++:a<strong>=1;return a},{})
[b][b]字母游戏[/b][/b]const anagrams = str => {[b][b]累加器[/b][/b]
if (str.length <= 2) {
return str.length === 2 ? [str, str[1] + str[0]] : str;
}
return str.split("").reduce((acc, letter, i) => {
return acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val));
}, );
}
anagrams("abc");
const sum = arr => arr.reduce((acc, val) => acc + val, 0);[b][b]计数器[/b][/b]
sum([1, 2, 3]);
const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);[b][b]函数柯里化[/b][/b]
countOccurrences([1, 2, 3, 2, 2, 5, 1], 1);
const curry = (fn, arity = fn.length, ...args) =>
arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);
curry(Math.pow)(2)(10);
curry(Math.min, 3)(10)(50)(2);
[b][b]通过判断函数的参数取得当前函数的length(当然也可以自己指定),如果所传的参数比当前参数少,则继续递归下面,同时储存上一次传递的参数。[/b][/b]
[b][b]数组扁平化[/b][/b]
const deepFlatten = arr =>arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), );
deepFlatten([1, [2, [3, 4, [5, 6]]]]);
[b][b]生成菲波列契数组[/b][/b]
const fibonacci = n => Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), );[b][b]pipe函数生成器[/b][/b]
fibonacci(5);
const pipe = (...funcs) => arg => funcs.reduce((acc, func) => func(acc), arg);
pipe(btoa, x => x.toUpperCase())("Test");
[b][b]通过对传递的参数进行函数加工,之后将加工之后的数据作为下一个函数的参数,这样层层传递下去。[/b][/b]
[b][b]compose函数生成器[/b][/b]
const dispatch = action => {[b][b]?
console.log('action', action);
return action;
}
const middleware1 = dispatch => {
return action => {
console.log("middleware1");
const result = dispatch(action);
console.log("after middleware1");
return result;
}
}
const middleware2 = dispatch => {
return action => {
console.log("middleware2");
const result = dispatch(action);
console.log("after middleware2");
return result;
}
}
const middleware3 = dispatch => {
return action => {
console.log("middleware3");
const result = dispatch(action);
console.log("after middleware3");
return result;
}
}
const compose = middlewares => middlewares.reduce((a, b) => args => a(b(args)))
const middlewares = [middleware1, middleware2, middleware3];
const afterDispatch = compose(middlewares)(dispatch);
const testAction = arg => {
return { type: "TEST_ACTION", params: arg };
};
afterDispatch(testAction("1111"));
数据加工函数[/b][/b]
const reducers = {[b][b]对象空值判断[/b][/b]
totalInEuros: (state, item) => {
return state.euros += item.price * 0.897424392;
},
totalInYen: (state, item) => {
return state.yens += item.price * 113.852;
}
};
const manageReducers = reducers => {
return (state, item) => {
return Object.keys(reducers).reduce((nextState, key) => {
reducers[key](state, item);
return state;
}, {})
}
}
const bigTotalPriceReducer = manageReducers(reducers);
const initialState = { euros: 0, yens: 0 };
const items = [{ price: 10 }, { price: 120 }, { price: 1000 }];
const totals = items.reduce(bigTotalPriceReducer, initialState);
const get = (p, o) => p.reduce((xs, x) => (xs && xs[x] ? xs[x] : null), o);[b][b]分组[/b][/b]
const groupBy = (arr, func) =>[b][b]对象过滤[/b][/b]
arr.map(typeof func === 'function' ? func : val => val[func]).reduce((acc, val, i) => {
acc[val] = (acc[val] || ).concat(arr);
return acc;
}, {});
groupBy([6.1, 4.2, 6.3], Math.floor);
groupBy(['one', 'two', 'three'], 'length');
const pick = (obj, arr) =>[b][b]promise顺序执行[/b][/b]
arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});
pick({ a: 1, b: '2', c: 3 }, ['a', 'c']);
const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());[b][b]排序函数[/b][/b]
const delay = d => new Promise(r => setTimeout(r, d));
const print = args => new Promise(r => r(args));
runPromisesInSeries([() => delay(1000), () => delay(2000), () => print('hello')])
const orderBy = (arr, props, orders) =>[b][b]快速选择器[/b][/b]
[...arr].sort((a, b) =>
props.reduce((acc, prop, i) => {
if (acc === 0) {
const [p1, p2] = orders && orders === 'desc' ? [b[prop], a[prop]] : [a[prop], b[prop]];
acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
}
return acc;
}, 0)
);
const users = [{ name: 'fred', age: 48 }, { name: 'barney', age: 36 }, { name: 'fly', age: 26 }];
orderBy(users, ['name', 'age'], ['asc', 'desc']);
orderBy(users, ['name', 'age']);
const select = (from, selector) =>
selector.split('.').reduce((prev, cur) => prev && prev[cur], from);
const obj = { selector: { to: { val: 'val to select' } } };
select(obj, 'selector.to.val');
JS 线树互转(es6)
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1631 次浏览 • 2018-07-01 12:09
菜单或者分类等业务场景时,每个顶级节点的parentId约定为0,当存在多个顶级节点,显得不是一个完整的树。所以在这类特殊情况下,我们需要构造一个顶级节点。将菜单或者分类的原有顶级节点存储至该节点的children中。 所以最后约定顶级节点如下。
root = null || {
id: 0,
parentId: null,
children: [node1, node2, ...],
}
线性转树形:function listConvertTree(list) {
let root = null;
if (list && list.length) {
root = { id: 0, parentId: null, children:};
const group = {};
for (let index = 0; index < list.length; index += 1) {
if (list[index].parentId !== null && list[index].parentId !== undefined) {
if (!group[list[index].parentId]) {
group[list[index].parentId] = ;
}
group[list[index].parentId].push(list[index]);
}
}
const queue = ;
queue.push(root);
while (queue.length) {
const node = queue.shift();
node.children = group[node.id] && group[node.id].length ? group[node.id] : null;
if (node.children) {
queue.push(...node.children);
}
}
}
return root;
}树形转线性:function treeConvertList(root) {
const list = ;
if (root) {
const Root = JSON.parse(JSON.stringify(root));
const queue = ;
queue.push(Root);
while (queue.length) {
const node = queue.shift();
if (node.children && node.children.length) {
queue.push(...node.children);
}
delete node.children;
if (node.parentId !== null && node.parentId !== undefined) {
list.push(node);
}
}
}
return list;
} 查看全部
菜单或者分类等业务场景时,每个顶级节点的parentId约定为0,当存在多个顶级节点,显得不是一个完整的树。所以在这类特殊情况下,我们需要构造一个顶级节点。将菜单或者分类的原有顶级节点存储至该节点的children中。 所以最后约定顶级节点如下。
root = null || {线性转树形:
id: 0,
parentId: null,
children: [node1, node2, ...],
}
function listConvertTree(list) {树形转线性:
let root = null;
if (list && list.length) {
root = { id: 0, parentId: null, children:};
const group = {};
for (let index = 0; index < list.length; index += 1) {
if (list[index].parentId !== null && list[index].parentId !== undefined) {
if (!group[list[index].parentId]) {
group[list[index].parentId] = ;
}
group[list[index].parentId].push(list[index]);
}
}
const queue = ;
queue.push(root);
while (queue.length) {
const node = queue.shift();
node.children = group[node.id] && group[node.id].length ? group[node.id] : null;
if (node.children) {
queue.push(...node.children);
}
}
}
return root;
}
function treeConvertList(root) {
const list = ;
if (root) {
const Root = JSON.parse(JSON.stringify(root));
const queue = ;
queue.push(Root);
while (queue.length) {
const node = queue.shift();
if (node.children && node.children.length) {
queue.push(...node.children);
}
delete node.children;
if (node.parentId !== null && node.parentId !== undefined) {
list.push(node);
}
}
}
return list;
}
前端进阶之道
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1195 次浏览 • 2018-06-21 20:08
受益匪浅,整理分享出来,希望对大家有所启示
?背景
如果你是刚进入WEB前端研发领域,想试试这潭水有多深,看这篇文章吧;
如果你是做了两三年WEB产品前端研发,迷茫找不着提高之路,看这篇文章吧;
如果你是四五年的前端开发高手,没有难题能难得住你的寂寞高手,来看这篇文章吧。
?
前言
?
所谓的天才,只不过是比平常人更快的掌握技能、完成工作罢了;只要你找到了正确的方向,并辅以足够的时间,你一样能够踏上成功彼岸。
本文将WEB前端研发编程能力划分了八个等级,每个等级都列举出了对应的特征及破级提升之方法,希望每位在看本文的同学先准确定位自己的等级(不要以你目前能力的最高点,而是以你当前能力的中档与之等级作对比,以免多走弯路),参考突破之法破之。
所谓的级别,只是你面对需求时的一种态度:能够完成、能够完美地完成、能够超出预期地完成。以追求完美的态度加以扎实的编程功力,那就是你的编程水平。
切记心浮气燥,级别够了,那级别里的东西自然就懂了。悟了就是悟了,没悟也没关系,静下心来,投入时间而已。
?
(一)入门
能够解决一些问题的水平。有一定的基础(比如最常见的HTML标签及其属性、事件、方法;最常见的CSS属性;基础的JavaScript编程能力),能够完成一些简单的WEB前端研发需求。
举个例子:删除一字符串中指定的字符。
var str = "www.baidu.com/?page";
str = str.replace('?page', "");
console.log(str);
str = str.substring(0, str.indexOf("/"));
console.log(str);首先不要苛责代码的对错严谨,毕竟每个程序员都有这样的一个过程;
其次,这两段代码在这个实例里没有什么大过错,可能会有瑕疵,但能够解决问题(删除指定的字符),这就是这个级别的特征。
再举个例子:计算系统当前是星期几。
var str = "";
var week = new Date().getDay();
if (week === 0) {
str = "今天是星期日";
} else if (week === 1) {
str = "今天是星期一";
} else if (week === 2) {
str = "今天是星期二";
} else if (week === 3) {
str = "今天是星期三";
} else if (week === 4) {
str = "今天是星期四";
} else if (week === 5) {
str = "今天是星期五";
} else if (week === 6) {
str = "今天是星期六";
}
// 或者更好一些
var str1 = "今天是星期";
var week1 = new Date().getDay();
switch (week1) {
case 0 :
str1 += "日";
break;
case 1 :
str1 += "一";
break;
case 2 :
str1 += "二";
break;
case 3 :
str1 += "三";
break;
case 4 :
str1 += "四";
break;
case 5 :
str1 += "五";
break;
case 6 :
str1 += "六";
break;
}
console.log(str);
console.log(str1);进阶之路
将JavaScript、HTML、CSS之类的编码帮助手册里的每个方法/属性都通读几遍!只有将基础打好,以后的路才能走的顺畅。参考这些帮助文档,力争写出无瑕疵的代码。
这些编码文档建议不仅是在入门提高期看,在你以后每个阶段破阶的时候都应该看看,最基础的东西往往也是最给力的东西,有时能够给你带来意想不到的收获。
?
(二)登堂
能够正确地解决问题。不管你是通过搜索网络,或者通过改造某些成品代码(jQuery/Dojo/Ext/YUI)案例,只要能够无错地完成需求。
同样以“删除指定字符串”为例:
var str = "www.baidu.com/?page";
str = str.replace(/?page/, "");
console.log(str);仅仅解决问题对于“登堂”阶段来说已经不是问题,这个级别所给出方案不能是漏洞百出。
以上面这段代码为例:replace方法的第一个参数虽然可以支持字符串,但最佳的类型是正则表达式。
再看看“今天是星期几”示例:
var a = ["日", "一", "二", "三", "四", "五", "六"];
var week = new Date().getDay();
var str = "今天是星期" + a[week];
console.log(str)对比“入门级”的代码,不管是从代码量、代码效率、代码优美性、代码思路来说,“登堂”级的这个日期处理代码都要优秀很多。
进阶之路
这个阶段虽然能够给出正确的解题方案,但是不一定是最优秀的方案。如何才能得到最优秀的方案呢?首先就是积累各种能够解决需求的方案,然后再验证每个方案,在这些方案中选择最好的一种。因此该阶段的进阶之路就是“行万里路,看万卷书”,积累各个需求的各个解决方案。
你可以扎身在专业论坛(蓝色理想、无忧、CSDN)里,通读所有的FAQ及帖子;你可以打开搜索引擎,穷举所有的搜索结果。自己建立测试环境一一验证这些代码:去揣摩每段代码的意图,去比较每段代码之间的差异。这两条路可以让你快速完成原始积累,当你再面对大多数需求时能够说这些问题我以前做过,那你就水到渠成地晋阶了。
?
(三)入室
最强代码,知道所有能够解决需求的各种方案,能够选择使用最优秀的方案满足需求。这个级别基本上产品开发编程中的代码主力。给出的一招一式,招招都是绝招。
还以上面的那个例子为例,你能说出1、2、3之间的差别,以及适用于那种环境吗?
var str = "www.baidu.com/?page";
// 1、字符串剪裁
str.substring(0, str.indexOf("?page"));
// 2、正则表达式
str.replace(/?page/, "");
// 3、字符串分拆、合并
str.split("?page").join("");能够解决问题的方法会有很多,但是对于程序员来说应该选择最优秀的。上面这段代码从代码量来说“正则表达式”最优秀;从代码执行效率来说: “字符 串剪裁”法最高(Chrome中“正则表达式”效率最高),split法最次;从可扩展性上来说,“正则表达式”法最优。具体使用那种方案视具体的需求环境而定。
“入室”阶段,程序员应该能够肯定的回答:对于这个需求而言,我的代码就是最优秀的代码。
再以“今天是星期几”为例,“登堂”级的代码你敢说是最优秀的代码了吗?
var str = "今天是星期" + "日一二三四五六".charAt(new Date().getDay());对比“登堂”级的示例代码,上面这段代码给你什么感受?程序员追求的就是完美。“入室”级别追求的就是每一招每一式的完美无缺。
从WEB前端编程来说,通过2年左右的努力,很多人能够达到这个水平,但是,很大一部分人的编程能力也就止步于此。或限于产品的需求单一性,或限于需求开发的时间紧迫性,或限于人的惰性,能够完美地解决当前的需求就够了。
由于长期处于技术平台期,技术上得不到提高,通常这个级别的工程师会比较燥。技术上小有所成;或追求个人的突破;或追求产品差异性带来的新鲜感;或者只是想换个心情;因此很多此级别的工程师会经常换公司。
?
戒骄戒躁:
切勿以为自己能写一手漂亮的代码而自满;切莫以为别人“尊称”你一声“大侠”你就以 “大侠”自居;切莫以为自己积累了一些得意的代码就成了框架式开发。
细节决定成败,优秀的方案并不能保证最终的成功。
?
进阶之路
此阶段进阶之路就是:
切勿心浮气躁;你不再被需求牵着走,而是你牵着需求走。注重细节,注意那些当前需求里没有明文给出的细节:
代码性能的差异、运行平台(浏览器)的差异、需求的隐性扩展、代码的向后兼容等等。再通读几遍HTML/CSS/JavaScript帮助文档。
我建议这个级别的工程师做一做WebTreeView控件,要求总节点量一万左右操作流畅,你的晋升之路就在这个控件的编码过程中。
?
(四)入微
最强解决方案。你能够走在需求的前面,将当前需求里有的、没有直接提出来的、现在暂时没有但将来可能有的等等,及前端编程潜规则等各个方方面面都综合考虑,给出最优方案。以一招胜万招。
var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0我将这个字符串里所可能想到的各种情况都列举出来:
var a = [
"http://www.xxx.com/VMpn=?pn=0", // pn= 可能出现在 ? 前
"http://www.xxx.com/VMpn=?pn=", // URL里允许pn 值为空
"http://www.xxx.com/VMpn=?pn=0&a=1", // URL 里可有多个字段
"http://www.xxx.com/VMpn=?a=1&pn=0", // 可能排在最后
"http://www.xxx.com/VMpn%3D%3Fa ... ot%3B, // 可能有多个 pn 字段
"http://www.xxx.com/VMpn%3D%3Fa ... ot%3B, // 可能在中间
"http://www.xxx.com/VMpn%3D%3Fa ... ot%3B, // 可能在中间成组
"http://www.xxx.com/VMpn%3D%3Fa ... ot%3B // 可能零星分布
];
var reg = /((?)(pn=[^&]*&)+(?!pn=))|(((?|&)pn=[^&]*)+$)|(&pn=[^&]*)/g;
for (var i = 0; i < a.length; i++) {
console.log(a[i] + "n" + a[i].replace(reg, "$2"));
}这个阶段已经不再追求一招一式,对你来说不是使用什么创新绝招解决需求,而是给出成熟稳重的方案,从根上解决问题。针对某个当前需求你的代码可能不是最优,但是针对此类的需求你的代码却是最优秀的代码。
进阶之路
很多WEB前端研发工程师在做了3-4年之后就会进入一个瓶颈期:产品开发需求是小菜一碟,没有新鲜的可以挑战的东西;代码开发中的稀奇的解题方法 都已经尝试过。没有了可挑战的难题,失去了探索的激情,也就没有了再上升的动力,好不容易走过“入室”级别的人又会有八九成止步于此。或转做技术领导人, 或转到其它的领域,或换公司。
?
这些人的上升之路在哪里呢?
这个阶段单单依靠技巧和数量的累积已经没有什么效果了,突破之路在第5层《化蝶》里会详细说明,我建议你在这个阶段末尾着重关注编程理论:面向对象/过程、代码组织形式、编译、代码规范、其它的框架设计等等。
我建议这个级别的工程师做一做WebEditor控件,不要求完整功能,但是该控件里的模块划分、代码组织、编程思想做到位,给出一个系统的解决方案。
?
(五)化蝶?
?
破茧重生,这个层次关注的是编程语言本身,而不再关心产品需求。什么是茧?产品需求就是茧。当你一招胜万招,打遍天下需求之时,你如果还拘泥于需求开发,那就是你限于茧中而不自知。要么就在这个茧里默默地老去,要么就破开茧获得新生。
还是以那个“字符串剪裁”的老例子:
function escapeReg(str) {
return str.replace(new RegExp("([.*+?^=!:x24{}()|[\]/\\])", "g"), "\x241");
}
function delUrlQuery(url, key) {
key = escapeReg(key);
var reg = new RegExp("((\?)(" + key + "=[^&]*&)+(?!" + key + "=))|(((\?|&)" + key + "=[^&]*)+$)|(&" + key + "=[^&]*)", "g");
return url.replace(reg, "x241")
}
// 应用实例
var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0
delUrlQuery(str, "pn");这段代码相对于层次4《入微》有什么区别吗?从代码实现上来说没有太大的区别,但是从思路上来说却有着本质的区别:
不再是就事论事,头疼医头,而是把一类问题抽象理论化,一招破万招;有封装的概念,不再是每次从零开始,而是站在半山腰开始爬。
在WEB前端研发队伍里也有很大一部分人《入室》层次时就自我感觉良好,直接跨跃到《化蝶》,积累自己的代码库,抽象化问题。但没有基础,缺少强大 的后劲,即使能够破茧也经受不了风吹雨打。一份不成熟的架构设计对团队开发带来的危害远大于它带来的好处,这种例子在业界屡见不鲜。不要拔苗助长,不要不 会走就想着跑,夯实基础,水到渠成地成长,厚积薄发,强力地破茧而出。
?
进阶之路
你已经从原始积累,到厚积薄发,到破茧而出之后,你所关注的应该不再是一招一式、一个项目、一个模块,而应该是一种思路,一种理论。你可以做以下几 个步骤以突破到更高层次:
再仔细看几遍HTML/CSS/JavaScript接口帮助文档;
选择一门强语言(C++/C#/Java等)观察理解这些语 言的组织结构,语言设计;看看原型链,链式语法编程,泛型,接口编程,DOM遥控器等等;仔细阅读成熟的WEB前端开发框架的设计文档,看他们为什么要这样设计。
?
(六)大侠
这里所说的大侠,不是大家互相吹捧的“大侠”,而是实至名归的高手。这个级别的人完全有能力写出不差于 Bindows/jQuery/Ext/YUI/Dojo 的同等级别规模的前端开发框架。应用成熟的开发框架指导、解决问题。// 库文件 /mz/string/escapeReg.js
mz.string.escapeReg = function (str) {
return str.replace(new RegExp("([.*+?^=!:x24{}()|[\]/\\])", "g"), "\x241");
};
// 库文件 /mz/url/delQuery.js
/// include mz.string.escapeReg;
mz.url.delQuery = function (url, key) {
key = mz.string.escapeReg(key);
var reg = new RegExp("((\?)(" + key + "=[^&]*&)+(?!" + key + "=))|(((\?|&)" + key + "=[^&]*)+$)|(&" + key + "=[^&]*)", "g");
return url.replace(reg, "x241");
};
// 应用实例
/// include mz.url.delQuery;
var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0
mz.url.delQuery(str, "pn");自成体系,有基础,也有理论高度。知道为什么这样设计,也知道什么样的设计最好。比如这个例子可以有这样的封装:
// 库文件 /mz/url/delQuery.js
/// include mz.string.escapeReg;
String.prototype.delQuery = function (key) {
key = mz.string.escapeReg(key);
var reg = new RegExp("((\?)(" + key + "=[^&]*&)+(?!" + key + "=))|(((\?|&)" + key + "=[^&]*)+$)|(&" + key + "=[^&]*)", "g");
return this.replace(reg, "x241");
};
// 应用实例
/// include mz.url.delQuery;
var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0
str.delQuery("pn");而为什么不采用下面的那种封装呢?经过了《知微》和《化蝶》你就懂了。
进阶出路
道法自然,从根上去寻找突破的契机。你可以研读HTML解析引擎设计与实现,JS解析引擎设计与实现,其它语言的代码解析与编译实现等等。
或者出些书。低级别的人写的书要么是一大抄,空无一物;要么是害人。
?(七)宗师
这个级别的人已然到了无招胜有招的境界。项目开发中的难题?没有难题!运行平台的差异?从根本上搞定!代码规范、开发模式,早已经被抛在身后。这个级别的人已经不再关注于某个前端开发框架,而是应对具体的环境给出最佳的理论指导。
这个级别的人所注意的应该是以最合理的系统架构引领着整个团队的进步,在什么样的场景下该用什么样的架构设计。3个、10个、50个、100个人的团队最应该用哪种模式?等你到了宗师级别,你再来回答吧。
进阶出路
每一个宗师就是一个高山,就是一个领域里的神,但是你仅满足于在一群比你弱的群体展现你的强大吗?如果还你是止步原地,那总会有人乘着飞机、宇宙飞船从你的头领掠过,高处不胜寒!
要突破这片领域,那就必须跳出这片领域。要想突破WEB前端研发的宗师级,那就跳出WEB前端吧,上面还有WEB开发。即使你是WEB前端的宗师, 但没有快速的数据响应,没有高速的网络架构,没有优美的系统支持,你又能如何?所以突破之路就是把目光投到整条WEB开发的链条中去。
(八)飞升
其实严格来说,飞升已经不是原领域的范围了。在WEB研发领域,对于这个层次的有一个很好听的称谓:架构师。当然那些“伪架构师”另当别论。
总结
一法通,万法通。在其它的技术领域,也可以按照《入门》《登堂》《入室》《入微》《化蝶》《大侠》《宗师》来划分等级;一样也可以按照我这里所写的每个级别的【进阶之路】来快速提升。 查看全部
原文作者无从考究了,侵删!
受益匪浅,整理分享出来,希望对大家有所启示
?背景
如果你是刚进入WEB前端研发领域,想试试这潭水有多深,看这篇文章吧;
如果你是做了两三年WEB产品前端研发,迷茫找不着提高之路,看这篇文章吧;
如果你是四五年的前端开发高手,没有难题能难得住你的寂寞高手,来看这篇文章吧。
?
前言
?
所谓的天才,只不过是比平常人更快的掌握技能、完成工作罢了;只要你找到了正确的方向,并辅以足够的时间,你一样能够踏上成功彼岸。
本文将WEB前端研发编程能力划分了八个等级,每个等级都列举出了对应的特征及破级提升之方法,希望每位在看本文的同学先准确定位自己的等级(不要以你目前能力的最高点,而是以你当前能力的中档与之等级作对比,以免多走弯路),参考突破之法破之。
所谓的级别,只是你面对需求时的一种态度:能够完成、能够完美地完成、能够超出预期地完成。以追求完美的态度加以扎实的编程功力,那就是你的编程水平。
切记心浮气燥,级别够了,那级别里的东西自然就懂了。悟了就是悟了,没悟也没关系,静下心来,投入时间而已。
?
(一)入门
能够解决一些问题的水平。有一定的基础(比如最常见的HTML标签及其属性、事件、方法;最常见的CSS属性;基础的JavaScript编程能力),能够完成一些简单的WEB前端研发需求。
举个例子:删除一字符串中指定的字符。
var str = "www.baidu.com/?page";首先不要苛责代码的对错严谨,毕竟每个程序员都有这样的一个过程;
str = str.replace('?page', "");
console.log(str);
str = str.substring(0, str.indexOf("/"));
console.log(str);
其次,这两段代码在这个实例里没有什么大过错,可能会有瑕疵,但能够解决问题(删除指定的字符),这就是这个级别的特征。
再举个例子:计算系统当前是星期几。
var str = "";进阶之路
var week = new Date().getDay();
if (week === 0) {
str = "今天是星期日";
} else if (week === 1) {
str = "今天是星期一";
} else if (week === 2) {
str = "今天是星期二";
} else if (week === 3) {
str = "今天是星期三";
} else if (week === 4) {
str = "今天是星期四";
} else if (week === 5) {
str = "今天是星期五";
} else if (week === 6) {
str = "今天是星期六";
}
// 或者更好一些
var str1 = "今天是星期";
var week1 = new Date().getDay();
switch (week1) {
case 0 :
str1 += "日";
break;
case 1 :
str1 += "一";
break;
case 2 :
str1 += "二";
break;
case 3 :
str1 += "三";
break;
case 4 :
str1 += "四";
break;
case 5 :
str1 += "五";
break;
case 6 :
str1 += "六";
break;
}
console.log(str);
console.log(str1);
将JavaScript、HTML、CSS之类的编码帮助手册里的每个方法/属性都通读几遍!只有将基础打好,以后的路才能走的顺畅。参考这些帮助文档,力争写出无瑕疵的代码。
这些编码文档建议不仅是在入门提高期看,在你以后每个阶段破阶的时候都应该看看,最基础的东西往往也是最给力的东西,有时能够给你带来意想不到的收获。
?
(二)登堂
能够正确地解决问题。不管你是通过搜索网络,或者通过改造某些成品代码(jQuery/Dojo/Ext/YUI)案例,只要能够无错地完成需求。
同样以“删除指定字符串”为例:
var str = "www.baidu.com/?page";仅仅解决问题对于“登堂”阶段来说已经不是问题,这个级别所给出方案不能是漏洞百出。
str = str.replace(/?page/, "");
console.log(str);
以上面这段代码为例:replace方法的第一个参数虽然可以支持字符串,但最佳的类型是正则表达式。
再看看“今天是星期几”示例:
var a = ["日", "一", "二", "三", "四", "五", "六"];对比“入门级”的代码,不管是从代码量、代码效率、代码优美性、代码思路来说,“登堂”级的这个日期处理代码都要优秀很多。
var week = new Date().getDay();
var str = "今天是星期" + a[week];
console.log(str)
进阶之路
这个阶段虽然能够给出正确的解题方案,但是不一定是最优秀的方案。如何才能得到最优秀的方案呢?首先就是积累各种能够解决需求的方案,然后再验证每个方案,在这些方案中选择最好的一种。因此该阶段的进阶之路就是“行万里路,看万卷书”,积累各个需求的各个解决方案。
你可以扎身在专业论坛(蓝色理想、无忧、CSDN)里,通读所有的FAQ及帖子;你可以打开搜索引擎,穷举所有的搜索结果。自己建立测试环境一一验证这些代码:去揣摩每段代码的意图,去比较每段代码之间的差异。这两条路可以让你快速完成原始积累,当你再面对大多数需求时能够说这些问题我以前做过,那你就水到渠成地晋阶了。
?
(三)入室
最强代码,知道所有能够解决需求的各种方案,能够选择使用最优秀的方案满足需求。这个级别基本上产品开发编程中的代码主力。给出的一招一式,招招都是绝招。
还以上面的那个例子为例,你能说出1、2、3之间的差别,以及适用于那种环境吗?
var str = "www.baidu.com/?page";能够解决问题的方法会有很多,但是对于程序员来说应该选择最优秀的。上面这段代码从代码量来说“正则表达式”最优秀;从代码执行效率来说: “字符 串剪裁”法最高(Chrome中“正则表达式”效率最高),split法最次;从可扩展性上来说,“正则表达式”法最优。具体使用那种方案视具体的需求环境而定。
// 1、字符串剪裁
str.substring(0, str.indexOf("?page"));
// 2、正则表达式
str.replace(/?page/, "");
// 3、字符串分拆、合并
str.split("?page").join("");
“入室”阶段,程序员应该能够肯定的回答:对于这个需求而言,我的代码就是最优秀的代码。
再以“今天是星期几”为例,“登堂”级的代码你敢说是最优秀的代码了吗?
var str = "今天是星期" + "日一二三四五六".charAt(new Date().getDay());对比“登堂”级的示例代码,上面这段代码给你什么感受?程序员追求的就是完美。“入室”级别追求的就是每一招每一式的完美无缺。
从WEB前端编程来说,通过2年左右的努力,很多人能够达到这个水平,但是,很大一部分人的编程能力也就止步于此。或限于产品的需求单一性,或限于需求开发的时间紧迫性,或限于人的惰性,能够完美地解决当前的需求就够了。
由于长期处于技术平台期,技术上得不到提高,通常这个级别的工程师会比较燥。技术上小有所成;或追求个人的突破;或追求产品差异性带来的新鲜感;或者只是想换个心情;因此很多此级别的工程师会经常换公司。
?
戒骄戒躁:
- 切勿以为自己能写一手漂亮的代码而自满;
- 切莫以为别人“尊称”你一声“大侠”你就以 “大侠”自居;
- 切莫以为自己积累了一些得意的代码就成了框架式开发。
细节决定成败,优秀的方案并不能保证最终的成功。
?
进阶之路
此阶段进阶之路就是:
切勿心浮气躁;你不再被需求牵着走,而是你牵着需求走。注重细节,注意那些当前需求里没有明文给出的细节:
- 代码性能的差异、运行平台(浏览器)的差异、需求的隐性扩展、代码的向后兼容等等。
- 再通读几遍HTML/CSS/JavaScript帮助文档。
我建议这个级别的工程师做一做WebTreeView控件,要求总节点量一万左右操作流畅,你的晋升之路就在这个控件的编码过程中。
?
(四)入微
最强解决方案。你能够走在需求的前面,将当前需求里有的、没有直接提出来的、现在暂时没有但将来可能有的等等,及前端编程潜规则等各个方方面面都综合考虑,给出最优方案。以一招胜万招。
var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0我将这个字符串里所可能想到的各种情况都列举出来:
var a = [这个阶段已经不再追求一招一式,对你来说不是使用什么创新绝招解决需求,而是给出成熟稳重的方案,从根上解决问题。针对某个当前需求你的代码可能不是最优,但是针对此类的需求你的代码却是最优秀的代码。
"http://www.xxx.com/VMpn=?pn=0", // pn= 可能出现在 ? 前
"http://www.xxx.com/VMpn=?pn=", // URL里允许pn 值为空
"http://www.xxx.com/VMpn=?pn=0&a=1", // URL 里可有多个字段
"http://www.xxx.com/VMpn=?a=1&pn=0", // 可能排在最后
"http://www.xxx.com/VMpn%3D%3Fa ... ot%3B, // 可能有多个 pn 字段
"http://www.xxx.com/VMpn%3D%3Fa ... ot%3B, // 可能在中间
"http://www.xxx.com/VMpn%3D%3Fa ... ot%3B, // 可能在中间成组
"http://www.xxx.com/VMpn%3D%3Fa ... ot%3B // 可能零星分布
];
var reg = /((?)(pn=[^&]*&)+(?!pn=))|(((?|&)pn=[^&]*)+$)|(&pn=[^&]*)/g;
for (var i = 0; i < a.length; i++) {
console.log(a[i] + "n" + a[i].replace(reg, "$2"));
}
进阶之路
很多WEB前端研发工程师在做了3-4年之后就会进入一个瓶颈期:产品开发需求是小菜一碟,没有新鲜的可以挑战的东西;代码开发中的稀奇的解题方法 都已经尝试过。没有了可挑战的难题,失去了探索的激情,也就没有了再上升的动力,好不容易走过“入室”级别的人又会有八九成止步于此。或转做技术领导人, 或转到其它的领域,或换公司。
?
这些人的上升之路在哪里呢?
这个阶段单单依靠技巧和数量的累积已经没有什么效果了,突破之路在第5层《化蝶》里会详细说明,我建议你在这个阶段末尾着重关注编程理论:面向对象/过程、代码组织形式、编译、代码规范、其它的框架设计等等。
我建议这个级别的工程师做一做WebEditor控件,不要求完整功能,但是该控件里的模块划分、代码组织、编程思想做到位,给出一个系统的解决方案。
?
(五)化蝶?
?
破茧重生,这个层次关注的是编程语言本身,而不再关心产品需求。什么是茧?产品需求就是茧。当你一招胜万招,打遍天下需求之时,你如果还拘泥于需求开发,那就是你限于茧中而不自知。要么就在这个茧里默默地老去,要么就破开茧获得新生。
还是以那个“字符串剪裁”的老例子:
function escapeReg(str) {这段代码相对于层次4《入微》有什么区别吗?从代码实现上来说没有太大的区别,但是从思路上来说却有着本质的区别:
return str.replace(new RegExp("([.*+?^=!:x24{}()|[\]/\\])", "g"), "\x241");
}
function delUrlQuery(url, key) {
key = escapeReg(key);
var reg = new RegExp("((\?)(" + key + "=[^&]*&)+(?!" + key + "=))|(((\?|&)" + key + "=[^&]*)+$)|(&" + key + "=[^&]*)", "g");
return url.replace(reg, "x241")
}
// 应用实例
var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0
delUrlQuery(str, "pn");
- 不再是就事论事,头疼医头,而是把一类问题抽象理论化,一招破万招;
- 有封装的概念,不再是每次从零开始,而是站在半山腰开始爬。
在WEB前端研发队伍里也有很大一部分人《入室》层次时就自我感觉良好,直接跨跃到《化蝶》,积累自己的代码库,抽象化问题。但没有基础,缺少强大 的后劲,即使能够破茧也经受不了风吹雨打。一份不成熟的架构设计对团队开发带来的危害远大于它带来的好处,这种例子在业界屡见不鲜。不要拔苗助长,不要不 会走就想着跑,夯实基础,水到渠成地成长,厚积薄发,强力地破茧而出。
?
进阶之路
你已经从原始积累,到厚积薄发,到破茧而出之后,你所关注的应该不再是一招一式、一个项目、一个模块,而应该是一种思路,一种理论。你可以做以下几 个步骤以突破到更高层次:
再仔细看几遍HTML/CSS/JavaScript接口帮助文档;
- 选择一门强语言(C++/C#/Java等)观察理解这些语 言的组织结构,语言设计;
- 看看原型链,链式语法编程,泛型,接口编程,DOM遥控器等等;
- 仔细阅读成熟的WEB前端开发框架的设计文档,看他们为什么要这样设计。
?
(六)大侠
这里所说的大侠,不是大家互相吹捧的“大侠”,而是实至名归的高手。这个级别的人完全有能力写出不差于 Bindows/jQuery/Ext/YUI/Dojo 的同等级别规模的前端开发框架。应用成熟的开发框架指导、解决问题。
// 库文件 /mz/string/escapeReg.js自成体系,有基础,也有理论高度。知道为什么这样设计,也知道什么样的设计最好。比如这个例子可以有这样的封装:
mz.string.escapeReg = function (str) {
return str.replace(new RegExp("([.*+?^=!:x24{}()|[\]/\\])", "g"), "\x241");
};
// 库文件 /mz/url/delQuery.js
/// include mz.string.escapeReg;
mz.url.delQuery = function (url, key) {
key = mz.string.escapeReg(key);
var reg = new RegExp("((\?)(" + key + "=[^&]*&)+(?!" + key + "=))|(((\?|&)" + key + "=[^&]*)+$)|(&" + key + "=[^&]*)", "g");
return url.replace(reg, "x241");
};
// 应用实例
/// include mz.url.delQuery;
var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0
mz.url.delQuery(str, "pn");
// 库文件 /mz/url/delQuery.js而为什么不采用下面的那种封装呢?经过了《知微》和《化蝶》你就懂了。
/// include mz.string.escapeReg;
String.prototype.delQuery = function (key) {
key = mz.string.escapeReg(key);
var reg = new RegExp("((\?)(" + key + "=[^&]*&)+(?!" + key + "=))|(((\?|&)" + key + "=[^&]*)+$)|(&" + key + "=[^&]*)", "g");
return this.replace(reg, "x241");
};
// 应用实例
/// include mz.url.delQuery;
var str = "http://www.xxx.com/?pn=0"; // 删除指定字符 pn=0
str.delQuery("pn");
进阶出路
道法自然,从根上去寻找突破的契机。你可以研读HTML解析引擎设计与实现,JS解析引擎设计与实现,其它语言的代码解析与编译实现等等。
或者出些书。低级别的人写的书要么是一大抄,空无一物;要么是害人。
?(七)宗师
这个级别的人已然到了无招胜有招的境界。项目开发中的难题?没有难题!运行平台的差异?从根本上搞定!代码规范、开发模式,早已经被抛在身后。这个级别的人已经不再关注于某个前端开发框架,而是应对具体的环境给出最佳的理论指导。
这个级别的人所注意的应该是以最合理的系统架构引领着整个团队的进步,在什么样的场景下该用什么样的架构设计。3个、10个、50个、100个人的团队最应该用哪种模式?等你到了宗师级别,你再来回答吧。
进阶出路
每一个宗师就是一个高山,就是一个领域里的神,但是你仅满足于在一群比你弱的群体展现你的强大吗?如果还你是止步原地,那总会有人乘着飞机、宇宙飞船从你的头领掠过,高处不胜寒!
要突破这片领域,那就必须跳出这片领域。要想突破WEB前端研发的宗师级,那就跳出WEB前端吧,上面还有WEB开发。即使你是WEB前端的宗师, 但没有快速的数据响应,没有高速的网络架构,没有优美的系统支持,你又能如何?所以突破之路就是把目光投到整条WEB开发的链条中去。
(八)飞升
其实严格来说,飞升已经不是原领域的范围了。在WEB研发领域,对于这个层次的有一个很好听的称谓:架构师。当然那些“伪架构师”另当别论。
总结
一法通,万法通。在其它的技术领域,也可以按照《入门》《登堂》《入室》《入微》《化蝶》《大侠》《宗师》来划分等级;一样也可以按照我这里所写的每个级别的【进阶之路】来快速提升。
Mongo VS Mysql 查询语句对比
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1286 次浏览 • 2018-06-18 01:24
db.users.find()
select * from users
db.users.find({"age" : 27})
select * from users where age = 27
db.users.find({"username" : "joe", "age" : 27})
select * from users where "username" = "joe" and age = 27
db.users.find({}, {"username" : 1, "email" : 1})
select username, email from users
db.users.find({}, {"username" : 1, "_id" : 0}) // no case
// 即时加上了列筛选,_id也会返回;必须显式的阻止_id返回
db.users.find({"age" : {"$gte" : 18, "$lte" : 30}})
select * from users where age >=18 and age <= 30
// $lt(<) $lte(<=) $gt(>) $gte(>=)
db.users.find({"username" : {"$ne" : "joe"}})
select * from users where username <> "joe"
db.users.find({"ticket_no" : {"$in" : [725, 542, 390]}})
select * from users where ticket_no in (725, 542, 390)
db.users.find({"ticket_no" : {"$nin" : [725, 542, 390]}})
select * from users where ticket_no not in (725, 542, 390)
db.users.find({"$or" : [{"ticket_no" : 725}, {"winner" : true}]})
select * form users where ticket_no = 725 or winner = true
db.users.find({"id_num" : {"$mod" : [5, 1]}})
select * from users where (id_num mod 5) = 1
db.users.find({"$not": {"age" : 27}})
select * from users where not (age = 27)
db.users.find({"username" : {"$in" : [null], "$exists" : true}})
select * from users where username is null
// 如果直接通过find({"username" : null})进行查询,那么连带"没有username"的纪录一并筛选出来
db.users.find({"name" : /joey?/i})
// 正则查询,value是符合PCRE的表达式
db.food.find({fruit : {$all : ["apple", "banana"]}})
// 对数组的查询, 字段fruit中,既包含"apple",又包含"banana"的纪录
db.food.find({"fruit.2" : "peach"})
// 对数组的查询, 字段fruit中,第3个(从0开始)元素是peach的纪录
db.food.find({"fruit" : {"$size" : 3}})
// 对数组的查询, 查询数组元素个数是3的记录,$size前面无法和其他的操作符复合使用
db.users.findOne(criteria, {"comments" : {"$slice" : 10}})
// 对数组的查询,只返回数组comments中的前十条,还可以{"$slice" : -10}, {"$slice" : [23, 10]}; 分别返回最后10条,和中间10条
db.people.find({"name.first" : "Joe", "name.last" : "Schmoe"})
// 嵌套查询
db.blog.find({"comments" : {"$elemMatch" : {"author" : "joe", "score" : {"$gte" : 5}}}})
// 嵌套查询,仅当嵌套的元素是数组时使用,
db.foo.find({"$where" : "this.x + this.y == 10"})
// 复杂的查询,$where当然是非常方便的,但效率低下。对于复杂查询,考虑的顺序应当是 正则 -> MapReduce -> $where
db.foo.find({"$where" : "function() { return this.x + this.y == 10; }"})
// $where可以支持javascript函数作为查询条件
db.foo.find().sort({"x" : 1}).limit(1).skip(10);
// 返回第(10, 11]条,按"x"进行排序; 三个limit的顺序是任意的,应该尽量避免skip中使用large-number 查看全部
db.users.find()
select * from users
db.users.find({"age" : 27})
select * from users where age = 27
db.users.find({"username" : "joe", "age" : 27})
select * from users where "username" = "joe" and age = 27
db.users.find({}, {"username" : 1, "email" : 1})
select username, email from users
db.users.find({}, {"username" : 1, "_id" : 0}) // no case
// 即时加上了列筛选,_id也会返回;必须显式的阻止_id返回
db.users.find({"age" : {"$gte" : 18, "$lte" : 30}})
select * from users where age >=18 and age <= 30
// $lt(<) $lte(<=) $gt(>) $gte(>=)
db.users.find({"username" : {"$ne" : "joe"}})
select * from users where username <> "joe"
db.users.find({"ticket_no" : {"$in" : [725, 542, 390]}})
select * from users where ticket_no in (725, 542, 390)
db.users.find({"ticket_no" : {"$nin" : [725, 542, 390]}})
select * from users where ticket_no not in (725, 542, 390)
db.users.find({"$or" : [{"ticket_no" : 725}, {"winner" : true}]})
select * form users where ticket_no = 725 or winner = true
db.users.find({"id_num" : {"$mod" : [5, 1]}})
select * from users where (id_num mod 5) = 1
db.users.find({"$not": {"age" : 27}})
select * from users where not (age = 27)
db.users.find({"username" : {"$in" : [null], "$exists" : true}})
select * from users where username is null
// 如果直接通过find({"username" : null})进行查询,那么连带"没有username"的纪录一并筛选出来
db.users.find({"name" : /joey?/i})
// 正则查询,value是符合PCRE的表达式
db.food.find({fruit : {$all : ["apple", "banana"]}})
// 对数组的查询, 字段fruit中,既包含"apple",又包含"banana"的纪录
db.food.find({"fruit.2" : "peach"})
// 对数组的查询, 字段fruit中,第3个(从0开始)元素是peach的纪录
db.food.find({"fruit" : {"$size" : 3}})
// 对数组的查询, 查询数组元素个数是3的记录,$size前面无法和其他的操作符复合使用
db.users.findOne(criteria, {"comments" : {"$slice" : 10}})
// 对数组的查询,只返回数组comments中的前十条,还可以{"$slice" : -10}, {"$slice" : [23, 10]}; 分别返回最后10条,和中间10条
db.people.find({"name.first" : "Joe", "name.last" : "Schmoe"})
// 嵌套查询
db.blog.find({"comments" : {"$elemMatch" : {"author" : "joe", "score" : {"$gte" : 5}}}})
// 嵌套查询,仅当嵌套的元素是数组时使用,
db.foo.find({"$where" : "this.x + this.y == 10"})
// 复杂的查询,$where当然是非常方便的,但效率低下。对于复杂查询,考虑的顺序应当是 正则 -> MapReduce -> $where
db.foo.find({"$where" : "function() { return this.x + this.y == 10; }"})
// $where可以支持javascript函数作为查询条件
db.foo.find().sort({"x" : 1}).limit(1).skip(10);
// 返回第(10, 11]条,按"x"进行排序; 三个limit的顺序是任意的,应该尽量避免skip中使用large-number
关于柯里化 curry
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1599 次浏览 • 2018-06-16 02:37
多亏了ES6,减少了不少阔怕的return,先上李子再扯蛋const add=>(x,y)=>x+y; //一般套路
add(1,2);const addcurry = a=>b=>a+b;//curry套路套路套路
addcurry(1)(2);
// 写下面的代码方便大家理解
var addcurry = function(a){
return function(b){
return b+a
}
}
截图很明显,一下下就明白了,这尼玛就是函数式编程里面的吉娃娃嘛,对 返回的就是一个函数,第一次输入值是返回一个函数,第二次的值作为了他的参数,就是这样 喵!嗷呜。。。。。对了,这里涉及了面试逼问的 闭包!!
是不是觉得 我靠 贼吉尔简单哇!
下面给大家copy 一个最近项目用到的一个李子,这个李子有点酸 是node下面用的?(虽然PHP是世界上最好的语言,总觉得自己可以玩node,结果还是愉快的使用了express-generator,对没错,我的理想还是做一名FBI纪录片摄影记者,侧重点是关注亚麻种植户的疾苦)。但是意思都差球不多 无太多的区别。const client = new AipNlp(...aipconf);
//
const AIPFC = (res) => async(aipfn) => {
const DATA = Object.assign({}, BACKJSON);
let result;
try {
result = await aipfn;
} catch (error) {
result = error
}
res.json(Object.assign(DATA, { data: result })).end();
};
exports.lexer = (req, res) => {
let aipfc = AIPFC(res);
aipfc(client.lexer(req.body.texts));
}前面的废话讲完了 先来说些柯里化 或涉及的一些基础术语
一、一元函数
接收一个参数的函数就叫一元函数(对对对 就是那种绿色的)const fn = (x) => x二、二元函数
接收连个参数的函数就叫二元函数(两张绿色的)const fn = (x,y) = > x+y
三、便餐函数
参数不固定的函数,es6之前用arguments来捕获,es6 可以用结构来获取const fn =(a,...b)=>b.reduce((x,y)=>x+y,a)
这些东西懂的可以忽略或者拍砖 标榜语言不准确的地方,谢谢
很多的函数库里面有会有curry这个函数 比如loash _.curry
下面我简单的来实现一个将多参数 变成一元的函数const add = (a,b)=>a+b;
const curry = (fn)=>(fir)=>(sec)=>fn(fir,sec);
写了那么多东西,你肯定要说,这有屁用啊,屁用都没有,我什么要拆开他,老子的用户都是16核,16G的高端电脑,好好,说个吉娃娃,下面继续李子发起走,我们慢慢的玩上去,来波澳门赌场,澳门皇家赌场开业了。。。。扑哧
假如,我是说假如我们要创建一个列表 有点那么小规律的列表 list2 list3 list4等等
我们先暂时用这来试试const list2 = y=>2*y;
const list3 = y=>3*y;
const list4 = y=>4*y再看看 尼玛 有规律的 来来来 改一版const clist = (a,b)=>a*b;
//
clist(2,2)
clist(2,3)
clist(2,4)
//拿东北的话来说 有一个姓铁的老大爷掉毛了(老铁没毛病);
继续骚起走,李子继续挥泪大甩卖,老板娘除外;const list2 =curry(clist)(2);
const list3 =curry(clist)(3);
const list4 =curry(clist)(4);
console.log(list2(2));
console.log(list2(3));
console.log(list2(4));
console.log(list3(2));
console.log(list3(3));
console.log(list3(4));
console.log(list4(2));
console.log(list4(3));
console.log(list4(4));
查看全部
函数 一等公民 ,越简单的函数越快,把一个复杂的函数打散成多个简单的小函数来执行,能增加执行的速度喝效率,和复用性?。在很多的js大牛的博客上肯定能找到许多的关于这个几把万一二的一些文章喝装13套路的文章,大多数都是互相的复制粘贴之流。简单的说柯里化就是把一个多参函数转换成嵌套的一元函数(毕竟一元最牛逼,因为脸都是绿的)。扯到柯里化 肯定就要继续再扯其他的反柯里化 偏函数 之类的 ,那个以后又空了再装逼。
多亏了ES6,减少了不少阔怕的return,先上李子再扯蛋
const add=>(x,y)=>x+y; //一般套路
add(1,2);
const addcurry = a=>b=>a+b;//curry套路套路套路
addcurry(1)(2);
// 写下面的代码方便大家理解
var addcurry = function(a){
return function(b){
return b+a
}
}
截图很明显,一下下就明白了,这尼玛就是函数式编程里面的吉娃娃嘛,对 返回的就是一个函数,第一次输入值是返回一个函数,第二次的值作为了他的参数,就是这样 喵!嗷呜。。。。。对了,这里涉及了面试逼问的 闭包!!
是不是觉得 我靠 贼吉尔简单哇!
下面给大家copy 一个最近项目用到的一个李子,这个李子有点酸 是node下面用的?(虽然PHP是世界上最好的语言,总觉得自己可以玩node,结果还是愉快的使用了express-generator,对没错,我的理想还是做一名FBI纪录片摄影记者,侧重点是关注亚麻种植户的疾苦)。但是意思都差球不多 无太多的区别。
const client = new AipNlp(...aipconf);前面的废话讲完了 先来说些柯里化 或涉及的一些基础术语
//
const AIPFC = (res) => async(aipfn) => {
const DATA = Object.assign({}, BACKJSON);
let result;
try {
result = await aipfn;
} catch (error) {
result = error
}
res.json(Object.assign(DATA, { data: result })).end();
};
exports.lexer = (req, res) => {
let aipfc = AIPFC(res);
aipfc(client.lexer(req.body.texts));
}
一、一元函数
接收一个参数的函数就叫一元函数(对对对 就是那种绿色的)
const fn = (x) => x二、二元函数
接收连个参数的函数就叫二元函数(两张绿色的)
const fn = (x,y) = > x+y
三、便餐函数
参数不固定的函数,es6之前用arguments来捕获,es6 可以用结构来获取
const fn =(a,...b)=>b.reduce((x,y)=>x+y,a)
这些东西懂的可以忽略或者拍砖 标榜语言不准确的地方,谢谢
很多的函数库里面有会有curry这个函数 比如loash _.curry
下面我简单的来实现一个将多参数 变成一元的函数
const add = (a,b)=>a+b;
const curry = (fn)=>(fir)=>(sec)=>fn(fir,sec);
写了那么多东西,你肯定要说,这有屁用啊,屁用都没有,我什么要拆开他,老子的用户都是16核,16G的高端电脑,好好,说个吉娃娃,下面继续李子发起走,我们慢慢的玩上去,来波澳门赌场,澳门皇家赌场开业了。。。。扑哧
假如,我是说假如我们要创建一个列表 有点那么小规律的列表 list2 list3 list4等等
我们先暂时用这来试试
const list2 = y=>2*y;再看看 尼玛 有规律的 来来来 改一版
const list3 = y=>3*y;
const list4 = y=>4*y
const clist = (a,b)=>a*b;拿东北的话来说 有一个姓铁的老大爷掉毛了(老铁没毛病);
//
clist(2,2)
clist(2,3)
clist(2,4)
//
继续骚起走,李子继续挥泪大甩卖,老板娘除外;
const list2 =curry(clist)(2);
const list3 =curry(clist)(3);
const list4 =curry(clist)(4);
console.log(list2(2));
console.log(list2(3));
console.log(list2(4));
console.log(list3(2));
console.log(list3(3));
console.log(list3(4));
console.log(list4(2));
console.log(list4(3));
console.log(list4(4));
RESTful API(2018-5-8整理)
每天进步一点点 • lopo1983 发表了文章 • 0 个评论 • 1272 次浏览 • 2018-05-08 11:35
GET:MDN 解释
请求指定的资源。使用?GET?的请求应该只用于获取数据。请求是否有主体 否
成功的响应是否有主体 是
安全 是
幂等 是
可缓存 是
HTML 表单是否支持 是
示例:
GET /api/example/
GET /api/example/:id(\\d+)/
------------------------------------------------------------------------------------------------------------------
POSTMDN解释
给指定资源新增内容。使用POST的请求建议仅用户新增数据。请求是否有主体 是
成功的响应是否有主体 是
安全 否
幂等 否
可缓存 仅缓存最近的数据
HTML 表单是否支持 是
请求主体:
1.application/x-www-form-urlencoded: 数据被编码成以 '&' 分隔的键-值对, 同时以 '=' 分隔键和值. 非字母或数字的字符会被?percent encoded: 这也就是为什么这种类型不支持二进制数据的原因 (应使用?multipart/form-data?代替).
2.multipart/form-data
3.text/plain
示例:
POST /api/example/
----------------------------------------------------------------------------------------------------------------
DELETEMDN解释
用于删除指定的资源。请求是否有主体 否
成功的响应是否有主体 否
安全 否
幂等 是
可缓存 否
HTML 表单是否支持 否
响应:
1.状态码 ?202?(Accepted) 表示请求的操作可能会成功执行,但是尚未开始执行。
2.状态码?204?(No Content) 表示操作已执行,但是无进一步的相关信息。
3.状态码 ?200?(OK) 表示操作已执行,并且响应中提供了相关状态的描述信息。
示例:
DELETE /api/example/1
DELETE /api/example?id=1,2,3
?
----------------------------------------------------------------------------------------------------------------
?
PUTMDN解释
用于新增资源或者使用请求中的有效负载替换目标资源的表现形式。请求是否有主体 是
成功的响应是否有主体 否
安全 否
幂等 是
可缓存 否
HTML 表单是否支持 否?
PUT与POST方法的区别在于,PUT方法是幂等的:调用一次与连续调用多次是等价的(即没有副作用),而连续调用多次POST方法可能会有副作用,比如将一个订单重复提交多次。PUT 用于整体覆盖
响应:
1.如果目标资源不存在,并且PUT方法成功创建了一份,那么源头服务器必须返回201?(Created) 来通知客户端资源已创建。
2.如果目标资源已经存在,并且依照请求中封装的表现形式成功进行了更新,那么,源头服务器必须返回200?(OK) 或者204?(No Content) 来表示请求的成功完成。
示例
PUT /api/example/1
?
----------------------------------------------------------------------------------------------------------------
?
PATCHMDN解释
在HTTP协议中,请求方法?PATCH??用于对资源进行部分修改。请求是否有主体 也许有
成功的响应是否有主体 否
安全 否
幂等 否
可缓存 否
HTML 表单是否支持 否?
在HTTP协议中,?PUT?方法已经被用来表示对资源进行整体覆盖, 而?POST?方法则没有对标准的补丁格式的提供支持。不同于??PUT?方法,而与?POST?方法类似,PATCH??方法是非幂等的,这就意味着连续多个的相同请求会产生不同的效果。
请求主体
1.使用默认的?application/x-www-form-urlencoded?做为 content type 的简单表单:
2.使用?multipart/form-data?作为 content type 的表单:
?
示例
PATCH /api/example/1
?
?----------------------------------------------------------------------------------------------------------------
?
关于返回
当GET,PUT和PATCH请求成功时,要返回对应的数据,及状态码200,即SUCCESS当PUT,POST创建数据成功时,要返回创建的数据,及状态码201,即CREATED当DELETE删除数据成功时,不返回数据,状态码要返回204,即NO CONTENT当GET不到数据时,状态码要返回404,即NOT FOUND任何时候,如果请求有问题,如校验请求数据时发现错误,要返回状态码400,即BAD REQUEST当API 请求需要用户认证时,如果request中的认证信息不正确,要返回状态码401,即NOT AUTHORIZED当API 请求需要验证用户权限时,如果当前用户无相应权限,要返回状态码403,即FORBIDDEN
?
?
?
----------------------------------------------------------------------------------------------------------------
?
序列化和反序列化
?
序列化和反序列化是RESTful API开发中的一项硬需求,所以几乎每一种常用的开发语言都会有一个或多个优秀的开源库,来实现序列化和反序列化,因此,我们在开发RESTful API时,没必要制造重复的轮子,选一个好用的库即可,$.AJAX AXIOS FLY都可以!其他的语言的可以去git下(如 python中的marshmallow)。
?
----------------------------------------------------------------------------------------------------------------
?
?
数据校验
?
当客户端向服务器发出post,?put或patch请求时,通常会同时给服务器发送json格式的相关数据,服务器在做数据处理之前,先做数据校验,是最合理和安全的前后端交互。如果客户端发送的数据不正确或不合理,服务器端经过校验后直接向客户端返回400错误及相应的数据错误信息即可。常见的数据校验包括:
数据类型校验,如字段类型如果是int,那么给字段赋字符串的值则报错数据格式校验,如邮箱或密码,其赋值必须满足相应的正则表达式,才是正确的输入数据数据逻辑校验,如数据包含出生日期和年龄两个字段,如果这两个字段的数据不一致,则数据校验失败(目前而言最复杂)
?
?
----------------------------------------------------------------------------------------------------------------
?
Authentication 和 Permission
?
常用的认证机制是Basic Auth和OAuth,RESTful API 开发中,除非API非常简单,且没有潜在的安全性问题,否则,认证机制是必须实现的,并应用到API中去。Basic Auth非常简单,很多框架都集成了Basic Auth的实现,自己写一个也能很快搞定,OAuth目前已经成为企业级服务的标配,其相关的开源实现方案非常丰富。如:jsonwebtoken.js 等等
?
----------------------------------------------------------------------------------------------------------------
?
CORS
?
懒 难求的打字?自己跳过去看
?
node express CORS配置app.all('*', (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With,token,authorization");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,PATCH,OPTIONS");
res.header("X-Powered-By", ' 3.2.1');
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
?
----------------------------------------------------------------------------------------------------------------
?
?
URL 规范
?
其命名和结构需要有意义。因此,在设计和编写URL时,要符合一些规范。
?
1. API 版本控制
?
规范的API应该包含版本信息,在RESTful API中,最简单的包含版本的方法是将版本信息放到url中,如:GET /api/v1/posts/
GET /api/v2/posts/另一种优雅的做法是,使用HTTP header中的accept来传递版本信息。
?
2.URL应指向资源
?
URL应指向资源,而不是规定动作,动作由METHOD来决定GET /api/Article/1/
3.GET 和HEAD必须安全
贼吉尔恐怖的一个案例GET /api/deleteArticle?id=1?
4.使用?nested routing来获取一个资源子集GET /api/lopo/Article/1/
如果语义上将资源子集看作一个独立的资源集合,则使用?nested routing?感觉更恰当,如果资源子集的获取是出于过滤的目的,则使用filter更恰当。
?
5.Filter
对于资源集合,可以通过URLSearchParams对资源进行过滤GET /api/comp/?user=lopo&&column=id,name,labelPagination 案例{
"page": 1, # 当前是第几页
"pages": 3, # 总共多少页
"per_page": 10, # 每页多少数据
"has_next": true, # 是否有下一页数据
"has_prev": false, # 是否有前一页数据
"total": 27 # 总共多少数据
}6.?Url 设计建议
Url 严格区分大小写的 不同大小写会被定为到不同的资源建议以/对Url进行结尾建议Url 片段组合时用-,params用_
?
?
?
? 查看全部
GET:MDN 解释
请求指定的资源。使用?GET?的请求应该只用于获取数据。
请求是否有主体 否
成功的响应是否有主体 是
安全 是
幂等 是
可缓存 是
HTML 表单是否支持 是
示例:
GET /api/example/
GET /api/example/:id(\\d+)/
------------------------------------------------------------------------------------------------------------------
POSTMDN解释
给指定资源新增内容。使用POST的请求建议仅用户新增数据。
请求是否有主体 是
成功的响应是否有主体 是
安全 否
幂等 否
可缓存 仅缓存最近的数据
HTML 表单是否支持 是
请求主体:
1.application/x-www-form-urlencoded: 数据被编码成以 '&' 分隔的键-值对, 同时以 '=' 分隔键和值. 非字母或数字的字符会被?percent encoded: 这也就是为什么这种类型不支持二进制数据的原因 (应使用?multipart/form-data?代替).
2.multipart/form-data
3.text/plain
示例:
POST /api/example/
----------------------------------------------------------------------------------------------------------------
DELETEMDN解释
用于删除指定的资源。
请求是否有主体 否
成功的响应是否有主体 否
安全 否
幂等 是
可缓存 否
HTML 表单是否支持 否
响应:
1.状态码 ?202?(Accepted) 表示请求的操作可能会成功执行,但是尚未开始执行。
2.状态码?204?(No Content) 表示操作已执行,但是无进一步的相关信息。
3.状态码 ?200?(OK) 表示操作已执行,并且响应中提供了相关状态的描述信息。
示例:
DELETE /api/example/1
DELETE /api/example?id=1,2,3
?
----------------------------------------------------------------------------------------------------------------
?
PUTMDN解释
用于新增资源或者使用请求中的有效负载替换目标资源的表现形式。
请求是否有主体 是?
成功的响应是否有主体 否
安全 否
幂等 是
可缓存 否
HTML 表单是否支持 否
- PUT与POST方法的区别在于,PUT方法是幂等的:调用一次与连续调用多次是等价的(即没有副作用),而连续调用多次POST方法可能会有副作用,比如将一个订单重复提交多次。
- PUT 用于整体覆盖
响应:
1.如果目标资源不存在,并且PUT方法成功创建了一份,那么源头服务器必须返回201?(Created) 来通知客户端资源已创建。
2.如果目标资源已经存在,并且依照请求中封装的表现形式成功进行了更新,那么,源头服务器必须返回200?(OK) 或者204?(No Content) 来表示请求的成功完成。
示例
PUT /api/example/1
?
----------------------------------------------------------------------------------------------------------------
?
PATCHMDN解释
在HTTP协议中,请求方法?PATCH??用于对资源进行部分修改。
请求是否有主体 也许有?
成功的响应是否有主体 否
安全 否
幂等 否
可缓存 否
HTML 表单是否支持 否
在HTTP协议中,?PUT?方法已经被用来表示对资源进行整体覆盖, 而?POST?方法则没有对标准的补丁格式的提供支持。不同于??PUT?方法,而与?POST?方法类似,PATCH??方法是非幂等的,这就意味着连续多个的相同请求会产生不同的效果。
请求主体
1.使用默认的?application/x-www-form-urlencoded?做为 content type 的简单表单:
2.使用?multipart/form-data?作为 content type 的表单:
?
示例
PATCH /api/example/1
?
?----------------------------------------------------------------------------------------------------------------
?
关于返回
- 当GET,PUT和PATCH请求成功时,要返回对应的数据,及状态码200,即SUCCESS
- 当PUT,POST创建数据成功时,要返回创建的数据,及状态码201,即CREATED
- 当DELETE删除数据成功时,不返回数据,状态码要返回204,即NO CONTENT
- 当GET不到数据时,状态码要返回404,即NOT FOUND
- 任何时候,如果请求有问题,如校验请求数据时发现错误,要返回状态码400,即BAD REQUEST
- 当API 请求需要用户认证时,如果request中的认证信息不正确,要返回状态码401,即NOT AUTHORIZED
- 当API 请求需要验证用户权限时,如果当前用户无相应权限,要返回状态码403,即FORBIDDEN
?
?
?
----------------------------------------------------------------------------------------------------------------
?
序列化和反序列化
?
序列化和反序列化是RESTful API开发中的一项硬需求,所以几乎每一种常用的开发语言都会有一个或多个优秀的开源库,来实现序列化和反序列化,因此,我们在开发RESTful API时,没必要制造重复的轮子,选一个好用的库即可,$.AJAX AXIOS FLY都可以!其他的语言的可以去git下(如 python中的marshmallow)。
?
----------------------------------------------------------------------------------------------------------------
?
?
数据校验
?
当客户端向服务器发出post,?put或patch请求时,通常会同时给服务器发送json格式的相关数据,服务器在做数据处理之前,先做数据校验,是最合理和安全的前后端交互。如果客户端发送的数据不正确或不合理,服务器端经过校验后直接向客户端返回400错误及相应的数据错误信息即可。常见的数据校验包括:
- 数据类型校验,如字段类型如果是int,那么给字段赋字符串的值则报错
- 数据格式校验,如邮箱或密码,其赋值必须满足相应的正则表达式,才是正确的输入数据
- 数据逻辑校验,如数据包含出生日期和年龄两个字段,如果这两个字段的数据不一致,则数据校验失败(目前而言最复杂)
?
?
----------------------------------------------------------------------------------------------------------------
?
Authentication 和 Permission
?
常用的认证机制是Basic Auth和OAuth,RESTful API 开发中,除非API非常简单,且没有潜在的安全性问题,否则,认证机制是必须实现的,并应用到API中去。Basic Auth非常简单,很多框架都集成了Basic Auth的实现,自己写一个也能很快搞定,OAuth目前已经成为企业级服务的标配,其相关的开源实现方案非常丰富。如:jsonwebtoken.js 等等
?
----------------------------------------------------------------------------------------------------------------
?
CORS
?
懒 难求的打字?自己跳过去看
?
node express CORS配置
app.all('*', (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With,token,authorization");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,PATCH,OPTIONS");
res.header("X-Powered-By", ' 3.2.1');
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
?
----------------------------------------------------------------------------------------------------------------
?
?
URL 规范
?
其命名和结构需要有意义。因此,在设计和编写URL时,要符合一些规范。
?
1. API 版本控制
?
规范的API应该包含版本信息,在RESTful API中,最简单的包含版本的方法是将版本信息放到url中,如:
GET /api/v1/posts/另一种优雅的做法是,使用HTTP header中的accept来传递版本信息。
GET /api/v2/posts/
?
2.URL应指向资源
?
URL应指向资源,而不是规定动作,动作由METHOD来决定
GET /api/Article/1/
3.GET 和HEAD必须安全
贼吉尔恐怖的一个案例
GET /api/deleteArticle?id=1?
4.使用?nested routing来获取一个资源子集
GET /api/lopo/Article/1/
如果语义上将资源子集看作一个独立的资源集合,则使用?nested routing?感觉更恰当,如果资源子集的获取是出于过滤的目的,则使用filter更恰当。
?
5.Filter
对于资源集合,可以通过URLSearchParams对资源进行过滤
GET /api/comp/?user=lopo&&column=id,name,labelPagination 案例
{6.?Url 设计建议
"page": 1, # 当前是第几页
"pages": 3, # 总共多少页
"per_page": 10, # 每页多少数据
"has_next": true, # 是否有下一页数据
"has_prev": false, # 是否有前一页数据
"total": 27 # 总共多少数据
}
- Url 严格区分大小写的 不同大小写会被定为到不同的资源
- 建议以/对Url进行结尾
- 建议Url 片段组合时用-,params用_
?
?
?
?