JavaScript的对象复制存在一些容易犯错的位置,从下面代码可以看出来:
直接赋值:

1
2
3
4
5
6
7
let obj = {
cat: 1,
a: { b: {c: 1} }
}
const sameObj = obj;
obj.a.b.c =2;
console.log(obj === sameObj); // ture

明明更改了初始的对象obj,但是这两个对象还是完全相等。所以这赋值操作与一般的变量赋值是有区别的。js中这种直接赋值来复制对象是将原对象在内存中的引用(c语言中的指针)复制了过去,并没有给新的对象加入新的内存空间。当这两个对象中的一个对象改变时,于是这块内存也改变了,但是这两个对象都是指向这块内存空间,于是就发生了这种情况:当一个对象改变时,这两个对象都会改变。

浅拷贝:所以要使用新的方法来复制对象:例如 Object.keys(),Object.assign(),for(let … in Obj)等方法来复制对象const a = {};``a = { ...ArrObj }Object.assign({}, Obj)

1
2
3
4
5
const obj2 = Object.assign({}, obj);    
obj.cat = 2;
obj.a.b.c =2;
console.log(obj2.cat, obj.cat === obj2.cat); // 1, false
console.log(obj2.a.b.c, obj2.a.b.c === obj.a.b.c) // 2, true

第一条打印的结果是我们想要的,新对象与原始对象不再有联系。但是第二条打印结果却又出现来跟直接赋值来复制对象相识的情况:改变一个对象的属性值,另一个对象也相应的改变了。这是因为上述的复制方法也都是只把第一层属性值重新用赋值的方式复制了一下,如果第一层的属性值仍然是指向对象的引用,这些方法复制的依然是指针,所以改变其中一个对象第二层以上的属性值,还是会导致两个对象同时改变。

理解之后可以思考一下下面的打印结果

1
2
obj.a = 1;
console.log(obj2.a.b.c); // 2

深拷贝:使用const newObj = JSON.parse(JSON.stringify(obj))可以实现复制对象是不再复制指针,进而两个对象不再有关联。可以这样理解:对象先转换成字符串,对象的指针信息自然就不再保存下来了,从这个字符串生成的对象也就没有了指针的关联,两个对象不再有任何关联。这种方法只有在原对象是json格式时有效,因为这种复制方法也会将原对象的方法(属性为函数)丢失。

ps:这样设计的目的是为了节省内存,所以为了这几kb的内存我就还是忍了吧!

递归函数深层复制

React组件更新的四个途径

  1. 首次渲染Initial Render
  2. 调用this.setState (并不是一次setState会触发一次render,React可能会合并操作,再一次性进行render)
  3. 父组件发生更新(一般就是props发生改变,但是就算props没有改变或者父子组件之间没有数据交换也会触发render)
  4. 调用this.forceUpdate
    如图所示
    React

原作者:linjinhe

链接:http://www.jianshu.com/p/4784216b8194

  • 图片按钮<input name="submit" type="image" value="ee" src="12.jpg" />
  • img下面有4px留白
  • 使用title属性显示提示信息
  • 空格:&nbsp;
  • <hr />color属性设置颜色
  • ie兼容性<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  • 只要是有src或href的HTML标签都有跨域的能力。
  • 网页中的空格转义字符&nbsp;在命令行中不能识别为正常的空格,会导致命令执行出错
  • 将dom元素关联自定义变量:加入属性data-*
1
2
3
4
5
6
<img
data-value="31940"
onclick={(e) => {
console.log(e.target.dataset.value);
}}
/>
  • dom元素绑定事件,传入当前元素
1
2
3
4
5
6
<p onclick="handleClick(this)">如果你点我,我就会消失。</p>
<script>
function handleClick(domObj) {
console.log(domObj);
}
</script>

  • mongosh:打开命令行

  • 进入数据库:use DBname。展示:show dbsshow collections

  • mongoimport mongoexport备份与恢复集合(collection)

    共同的相关配置:-d DBname -c COLname -h IPaddress:PORTname

    从filename导入:mongoimport [config] filename

    filename:mongoexport [config] -o filename

    导入数据要避免与原来的数据重复,先删除集合db.COLname.drop() 再mongoimport

  • MongoDB 数据库备份(mongodump)与恢复(mongorestore)

  • 命令行中打印变量print VARIABLE

  • 使用javascipt的数组迭代方法forEach map
    这样可以方便的操作字段名(field),如字段重命名、字段合并

1
2
3
4
5
6
7
8
9
10
11
12
db.getCollection('spaces').find({}).forEach(function(x) {
if(x.in_company) {
x.in_company = x.in_company.map(function(ele, i) {
return {
name: ele,
website: x.in_company_url ? x.in_company_url[i] : null,
}
});
delete x.in_company_url;
db.spaces.save(x);
}
})
  • 加入表的关联:mongoose字段加入ref属性,查询时加入populate方法。

数据库操作

  • db.COLname.save()包含_id则为修改文档,不包含则与db.COLname.insert()功能相同

  • 查询时注意String(‘1’)与Number(1)的区别,在mongoose的保存save与更新update操作中,定义好Schema的字段类型后,则数字与字符串没有区别。

  • 查询不存在字段名或者字段的值为nulldb.COLname.find({"FIELD": null})

    相应的查询字段存在并且值为nulldb.COLname.find({"FIELD": {"$in":[null],"$exists":true}})

    查询存在该字段的文档{$exists: true}

  • 文档中的字段存储数组/对象时的查询:在文档{"fruits" : [ "apple", "pear", "orange" ] }

    字段的值为数组:

    找到数组中包含值:find({"fruits":"apple"})

    数组中包含多个值:find({"fruits":{"$all":["apple","banana"]}})

    数组中某个索引为某个值:find({"fruits.1":"orange"})

    精确查询,要求值相同并且顺序一致:
    find({"fruits":["apple","orange","pear"]})

    字段的值为对象数组时:使用精确匹配时,只会找出字段的值相等并且顺序一致的文档。

    要求包含某些键值对,注意与字段的值为对象时查询的区别:

    find({ "name.first":"joe", "name.middle":"bush" })

安全策略

设置用户权限

  • mongod --auth开启权限认证,再加入用户分配权限
  • TLS/SSL传输加密
  • 数据库定时备份
  • 不使用mongodb默认端口,防止端口扫描
  • 不需要公网连接时,bind_ip只允许本地访问

mongoose

mongoose的model名称为首字母大写时,存到mongodb时会自动首字母小写

mongodb版本6安装

安装MongoDB Community Server

安装MongoDB Shell

  • React.Children.map(this.props.children, () => { …… })
  • 改变state不使用push方法而是setState
  • ES6语法const props = { ...this.props };
  • 组件自定义背景,在父组件位置设置style属性,然后在子组件里面继承style属性;
  • setState是异步的,不会马上更新state。setState有callback函数
  • react-router的onEnter设置路由权限
  • 使用div模拟textarea:属性contentEditable="plaintext-only"onInput={this.emitChange}this.inputRef.innerHTML获取输入内容。
1
2
3
4
5
6
7
<div
className="input_content"
onInput={this.inputChange}
contentEditable="plaintext-only"
ref={(inp) => { this.inputRef = inp }}
onKeyDown={this.onKeyDown}
/>

新版ref写法ref={this.inputRef}

  • 键盘事件
1
2
3
onKeyDown = (e) => {
console.log(e.key);
}

状态提升

你一定听说过变量提升,函数提升,那么状态提升是什么呢?

首先你得了解双向绑定和单向数据流,双向绑定中,数据可以在不同的组件之间实现共享,这样做的确有很大的好处,但是在react中,不推荐使用双向绑定,而是使用状态提升的方式。

状态提升:state推崇单向数据流,数据从父组件通过props流向子组件,如果你在子组件中,需要修改state来和其他子组件共享数据更新,你需要使用回调函数给使数据更新给父组件,然后从父组件流向其他的子组件,这样做是保证数据有单一的来源。

如果子组件和子组件之间任意共享数据,那么,后期维护会比较痛苦,特别是找bug的时候。

看一个状态提升的例子吧。

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
class Child extends React.Component {
constructor(props) {
super(props);
}
handleChange = (e) => {
this.props.upDateValue(e.target.value);
}
render() {
const {name, value} = this.props;
return (
<div>
<p>{name}:</p>
<input
value={value}
onChange={this.handleChange}
/>
</div>
);
}
}

class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {value: '', name: ''};
}
upDateValue = (value) => {
this.setState({value: value})
}
render() {
const {value} = this.state;
return (
<div>
<Child name="组件1" value={value} upDateValue={this.upDateValue} />
<Child name="组件2" value={value} upDateValue={this.upDateValue} />
</div>
);
}
}
ReactDOM.render(
<Demo />,
document.getElementById('root')
);
- 传递属性给子组件

const childrenWithProps = React.Children.map(this.props.children,
child => React.cloneElement(child, {
equityRate: this.publishTasks,
}),
);

this.context

  • this.context用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//在父组件中包含方法
getChildContext() {
return {
USER: this.state.USER,
};
}
Parent.childContextTypes = {
USER: PropTypes.object,
};
//在子组件中
Child.contextTypes = {
USER: PropTypes.object,
};
constructor(props, context)

Atom快捷键

  • Ctrl+shift+m预览markdown

  • string操作不改变原string,只会返回新string。Buffer只改变原Buffer。
  • bin => dup
1
2
var dup = new Buffer(bin.length);
bin.copy(dup);
  • 附件上传
1
2
3
4
5
6
7
8
9
10
11
12
const uploadFile = (req, res, next) => {
//生成multiparty对象,并配置上传目标路径
var form = new multiparty.Form({uploadDir: './public/files/'});
//上传完成后处理
form.parse(req, function(err, fields, files) {
var inputFile = files.file[0].path;
console.log('inputFile', inputFile);
res.writeHead(200, {'content-type': 'text/plain;charset=utf-8'});
res.end(inputFile);
//res.end(util.inspect({files: inputFile}));
});
};
  • 实现文件下载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
router.get('/file/:fileName', function(req, res, next) {
var fileName = req.params.fileName;
var filePath = path.join(__dirname, fileName);
var stats = fs.statSync(filePath);
if(stats.isFile()){
res.set({
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment; filename='+fileName,
'Content-Length': stats.size
});
fs.createReadStream(filePath).pipe(res);
} else {
res.end(404);
}
});

前端<a>标签加上download属性后就可以下载了

服务器操作

  • pm2 list
  • pm2 restart 1
  • pm2 start <path/to/app.js>

Yarn

  • yarn add [package]
  • yarn add [package]@[version]
  • yarn add [package]@[tag]
  • yarn upgrade [package]
  • yarn remove [package]
  • yarn install

  • Boolean && 变量
  • Boolean || 变量可以用来设置默认值,如果 user.name 是 undefined、null、0、’’(空字符串)或 false 等 falsy 值,name 将被设置为’Guest’。空值合并运算符(??)是一个逻辑运算符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。
  • a.indexOf(b)a为字符串或者数组
  • a.toFixed(2)a类型为number,保留小数
  • toString()可以判断对象的类型。Object.prototype.toString.call(<value>)
  • -a可以将a转化为number类型
  • 正则表达式//gi,g表示全局搜索,i表示忽略大小写
  • js不支持负向零宽断言/(?<=Jack)Sprat/(ES2018后支持)
  • Math.floor()向下取整,Math.ceil()向上取整,Math.round()四舍五入。
  • arr.sort(func):改变原数组。func返回值为负数则a,b顺序不变。(从小到大排序,则a<b并且返回负值,则返回a-b;从大到小排序,则a>b并且返回负值,则返回b-a
  • reverse()改变原数组
  • substr() 方法返回一个字符串中从指定位置开始到指定字符数的字符。
    不改变原数组,字符串
    str.substr(start[, length])
  • Array.prototype.slice()
    String.prototype.slice()
    slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
  • Array.prototype.splice(start[, deleteCount[, item1[, item2[, ...]]]])
    splice() 方法通过删除现有元素或者原地添加新的元素来修改数组,并以数组形式返回删除的元素。此方法会改变原数组。
  • Array.forEach(arguments, func())对非数组序列进行forEach处理
  • Array(<len>).fill(<value>) 填满数组
  • 数组操作: shift(),unshift(),pop(),push()。改变原数组。
  • Array.prototype.join(<string>)合并为字符串。String.prototype.split(''):返回分割成的数组。
  • this总是指向函数的直接调用者(而非间接调用者),在DOM事件中,this指向定义这个事件的DOM对象;
  • t=setTimeout(fun(), N毫秒);clearTimeout(t)t=setInterval();clearInterval(t)
  • async与await
1
2
3
4
5
6
var start = async function () {
for (var i = 1; i <= 10; i++) {
console.log(`当前是第${i}次等待..`);
await sleep(1000);
}
};
  • EventTarget.addEventListener()
  • e.preventDefault();
  • e.stopPropagation()阻止捕获或冒泡阶段中当前事件的进一步传播

对象处理

  • Object.keys(obj)得到对象的可枚举属性
  • delete Obj.prop删除对象属性
  • 字符串与对象的转换:JSON.stringify(json)JSON.parse(string)。Array也是Object类型
  • 字符串变量d用作对象属性名:{ [d]: !this.state[d] }

作用域

  • if块级作用域在ES5与ES6中不同。用 let 或 const 声明是块级作用域:用一对花括号(一个代码块)创建出来的作用域
  • const a = func; 与function a(){}的变量提升方式不同;
  • for循环中let 与var i = 0,在函数中不同

精度:使用 64 位双精度浮点型来表示

0.1 和 0.2 在二进制中是无限循环小数

原型继承

  • 对象的constructor属性指向它的构造函数。对象的__proto__属性指向上一级的原型对象。构造函数的prototype等于其生成对象的getPrototypeOf(__proto__)。
  • 必须显式地设置原型才能确保动态的继承,将B设置为A的原型链: A.prototype = new B;
  • 将构造器B属性传给A作为A的自身属性
1
2
3
4
function A (a, b) {
B.call(this, c, d);
this.machine = mach || "";
}
  • A为函数:A.prototype={ constructor: A, __proto__: 基本prop }

Web APIs

  • 在chrome中加入断点:在源码中加入debugger
  • 前端全局对象window,node.js全局对象global。全局对象可以用来设置全局变量window.abc
  • window.location获取当前页面网址信息,可进行页面跳转
  • fetch跨域加上{ mode: ‘no-cors’ },response类型为opaque,status为0。或者使用fetch-jsonp
  • 赋值localStorage.abc = string,数字存入localStorage后变为string
  • URLSearchParams
  • es6 array新方法

样式

  • border: 5px solid red;
  • margin: top left-right bottom;
  • display:inline-block;
  • cursor: point;
  • 设置边框阴影box-shadow: inset 0 0 2em blue;
1
2
3
4
5
6
7
8
9
10
11
12
13
/* 图文并排 */
.dom {
background:url('/images.jpg') no-repeat;
padding-left:
width:
}

/* 输入框取消默认样式 */
.input {
border: none;
resize: none;
outline: none; /* 输入框边框 */
}
  • inline元素设置高度: {display: inline-block;}再设置 width height
  • a {color: #000;} * 未被访问的链接 *
  • a:visited {color: #3ac1bd;} * 已被访问的链接 *
  • a:hover {color: #3ac1bd;} * 鼠标指针移动到链接上 *
  • a:active {color: #3ac1bd;}
  • a{ text-decoration: none; } 去掉链接的下划线

排版

  • position: relative - absolute;
  • 填满整个元素:父元素设置高度,子元素height: 100%
  • css度量运算calc(100% - 36px)
  • hover显示新元素
1
2
3
4
<parent>
<child1/>
<child2/>
</parent>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.parent {
position: relative;
.child1, .child2 {
height: 100px;
width: 100px;
}
.child2 {
display: none;
background: rgba(230, 230, 230, 0.9);
}
}
.parent:hover .child2 {
display: block;
position: absolute;
top: 0;
cursor: pointer;
}

对齐

  • margin: auto;

  • 水平对齐

    父元素中设置text-align: left | right | center | justify | inherit;

  • 垂直对齐:

    子元素为inline,table-cell或者inline-block时vertical-align: top | text-top | middle | bottom | text-bottom;table-cell与inline-block中middle显示不同

    block元素中的文字line-height设置为该元素height

Flex

  • 子元素的float、clear和vertical-align属性将失效。
  • flex-direction: row | row-reverse | column | column-reverse;
  • flex-flow: flex-direction | flex-wrap;
  • justify-content: flex-start | flex-end | center 水平对齐方式
  • align-items: flex-start | flex-end | center 垂直对齐方式
  • 多根轴线align-content: flex-start | flex-end | center | space-between | space-around | stretch;
  • 保留空白符white-space: normal|pre|nowrap|pre-wrap|pre-line|inherit;

Css单位

  • vh:屏幕比例。px:像素。em根据当前位置的字体大小。rem根元素的字体大小。

Css选择器

  • .class #id element
  • 直接子元素:>
  • 选择器之间,表示and

进入项目文件根目录

git init将该文件夹加入git的管理之中, 并新建了一个.git/文件夹

新建.gitignore文件让git忽略不需要同步的文件夹/文件。

git add ..目录——就是当前目录加入git暂存区域(staging area)。

git commit -m "first commit"将声明区的文件加入到本地仓库

git remote add origin git@github.com:ABCD.git添加远程仓库的路径,并命名为origin,git@ABCD.git为使用ssh协议同步,https://ABCD.git使用https协议同步。ssh同步需用额外的配置,但是之后不需要输入密码,而且安全性更高。https协议需要每次输入用户名和密码,想要省略输入密码,就需要明文保存在本地电脑上,安全性低。

git push -u origin master将本地仓库推送到名为origin的远程仓库的master分支(branch)

0%