0%

  1. 可在方法中用this.constructor.prototype挂在额外方法
  2. this.constructor指向 class Point {…} 所以在this.constructor.prototype上可以挂载方法。
  3. 追加的方法内部同样可以调用constructor构造函数声明的属性
  4. 也可以使用Object.assign 动态追加方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    {
    class Point {
    constructor(x, y) {
    this.x = x;
    this.y = y;
    }
    add() {
    this.constructor.prototype.add1 = () => {
    console.log('这是追加的add1方法' + this.x)
    }

    Object.assign(Point.prototype,{
    add2() {
    console.log('这是追加的add2方法')
    }
    })
    }
    }
    var a = new Point(2, 3)
    a.add();
    a.add1();
    a.add2();
    }

1.首先,应该在登录页面点击登录按钮时候 存token

1
cacheUtils.cookie.setObject(consts.ACCESSTOKEN,response.data.accessToken);
Read more »

MVVM具体含义

M:model(服务端逻辑) v:view vm(viewModel 特指vue.js,react.js处理服务和视图层的逻辑)
和MVC的区别:页面与逻辑完全分离,可双向绑定

双向绑定原理

data - > watcher ->更新 view

Object.defineProperty(obj,prop,descripor)
obj:对象
prop:要修改的属性
descripor:定义或者修改的属性的描述
会直接在一个对象上定义一个新属性或对已有的属性修改,然后返回这个对象
比如vue 的data就是一个对象,所以可以对里边定义的属性进行双向绑定

双向绑定设计模式

data => observer(Object.defineProperty) => watcher => view

实现一个简单的双向绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<input id="objInput" type="" name="">
<div id="area"></div>

var obj = {}
Object.defineProperty(obj,'a',{
get:function(){
return val
},
set:function(val){
document.getElementById('objInput').value = val
document.getElementById('area').innerHTML = val
}
})

document.addEventListener('keyup',function(e){
obj.a = e.target.value
})

源码解析

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
new Vue({
data:{
a:1
},
methods:{

}
})

function Vue(options) {
this.data = options.data;
this.methods = options.methods;
var self = this;
//循环data中的数据,每个key做代理
Object.keys(this.data).forEach(function(key){
self.proxyKeys(key)
})

observe(this.data)
}

Vue.prototype = {
proxyKeys:function(key){
var self = this;
//注意:这里this代表vm实例,绑定data中的key直接到vm上,就实现了 vm.key = vm.data.key的效果
//所以你代码里写this.a = this.data.a
Object.defineProperty(this,key,{
enumerable:false,
configurable:true,
get:function() {
return self.data[key]
},
set:function(newVal){
self[data].key = newVal;
}
})
}
}

//Observer 作用就是遍历所有的data 为其中所有的属性(可能有递归)添加观察者(getter) ,数据有变化应该通知观察者(setter) dep就是一个观察者列表,有添加(需要观察的属性),有通知功能(通知wather)调用自身的update方法
function Observe(data) {
this.data = data;
this.walk(data)
}

Observe.prototype = {
walk:function(data) {
var self = this;
Object.keys(data).forEach(function(key) {
self.defineReactive(data,key,data[key])
})
},
defineReactive:function(data,key,val){
var dep = new Dep()
//如果data中的值还是对象,递归
var childObj = observe(val);
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get:function getter() {
if(Dep.target) { //Dep.target就是watcher
dep.addSub(Dep.target)
}
return val
},
set:function setter(newVal) {
val === newVal ? return : val = newVal;
dep.notify()
}
})
}
}

function Dep() {
this.subs = []
}

Dep.prototype = {
addSub:function(sub) {
this.subs.push(sub)
},
notify:function() {
this.subs.forEach(function(sub) {
sub.update();
})
}
}

Dep.target = null;


function observe(val) {
if(!val || typeof val !== Object) {
return
}
return new Observe(value)
}

标准模型 和 IE模型区别

1.标准模型不包含border padding , IE模型相反
2.用box-sizeing:border-box/content-box设置 默认是IE模型 content-box

js如何设置

1
2
3
4
5
dom.style.width/height 取得内联样式
window.getComputedStyle(dom).width/height //兼容GOOGLE,MOZ的写法
dom.currentStyle.width/height //只有IE支持
dom.getBoundingClientRect().width/height //根据屏幕左上角取得的 返回4个值
// top left width height

BFC

https://www.w3cplus.com/css/understanding-block-formatting-contexts-in-css.html

概念:块级格式化上下文
用途:解决边距重叠问题(分别创建不同的BFC)
原理:bfc是一个独立的容器,外边的元素不会影响里边的,里边的也不会影响外边的
创建:overflow:hidden 或者 position不为static或者relative
清除浮动也可以用 设置父元素 overflow:auto/hidden 或者 float:left 让子元素高度也可以参与计算

每个解决方案的优缺点

1.float布局 兼容性好 但是需要清除浮动

2.绝对定位 布局快捷,但是子元素也要脱离文档流

3.flex 布局比较好 优先选择 如果不固定高度也可以用

4.table 布局 兼容性好 如果不固定高度也可以用

5.grid布局 新技术 兼容性不太好

5种CSS

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
<style type="text/css">
*{padding: 0; margin: 0}
.box{height: 300px; margin-top: 50px;}
.left,.right{width: 300px; height: 100%;}
.left{background-color: red}
.right{background-color: blue}
.center{background-color: yellow; height: 100%;}
/*第一种 float 布局 */
.box1 .left{float: left;}
.box1 .right{ float: right; }
.box1 .center{background-color: yellow; height: 100%;}

/*第二种 绝对定位布局*/
.box2{position: relative;width: 100%;}
.box2 .left{position: absolute; left: 0; top: 0; }
.box2 .right{position: absolute; right: 0; top: 0; }
.box2 .center{position: absolute; left: 300px; right: 300px; top: 0}

/*第三种 flex*/
.box3{display: flex;}
.box3 .left {flex:0 0 300px;}
.box3 .right {flex: 0 0 300px;}
.box3 .center {flex: 1}

/*第四种 表格布局*/
.box4 {display: table; width: 100%;}
.box4 .left {display: table-cell;}
.box4 .right {display: table-cell;}
.box4 .center {display: table-cell;}

/*第5种 grid 分为3列*/
.box5 {
display: grid;
width: 100%;
grid-template-columns: 300px auto 300px;
}

</style>

HTML

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
<section>
<div class="box1 box">
<div class="left"></div>
<div class="right"></div>
<div class="center">
<p>注意要先写left,right的div ,center布局在最后</p>
</div>
</div>
</section>

<section>
<div class="box2 box">
<div class="left"></div>
<div class="center">绝对定位写法</div>
<div class="right"></div>
</div>
</section>

<section>
<div class="box3 box">
<div class="left"></div>
<div class="center">flex写法</div>
<div class="right"></div>
</div>
</section>

<section>
<div class="box4 box">
<div class="left"></div>
<div class="center">table</div>
<div class="right"></div>
</div>
</section>

<section>
<div class="box5 box">
<div class="left"></div>
<div class="center">grid布局 </div>
<div class="right"></div>
</div>
</section>

dom事件级别

DOM0:element.oncick= fn
DOM2: element.addEventListener(‘click’,fn,false) / attachEvent
DOM3: element.addEventListener(‘keyup’,fn,false)

事件模型

冒泡或者捕获
事件流:事件通过捕获到达目标元素(捕获阶段),从目标元素上传到window对象(冒泡阶段)
捕获:从window->document->html

事件对象

event.preventDefault() 阻止默认行为(链接跳转)
event.stopProgatation() 阻止冒泡

1
2
3
4
5
6
7
8
9
10
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配目标元素
if (target.nodeName.toLocaleLowerCase === 'li') {
console.log('the content is: ', target.innerHTML);
}
});

new 一个对象的步骤

1.创建一个空对象instance
2.设置空对象的原型链继承自创建这个空对象的构造函数的原型 instance.proto=F.prototype;
3.构造函数执行 this 指向新事例
4.判断构造函数的返回结果是对象类型就返回这个对象,前边就废了,如果不是就返回原来的instance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
function M() {
this.a = 1;
}

let o = new M()
//函数的prototype有个constructor属性 指向本身的构造函数 如果重新定义了prototype则为false;
M.prototype.constructor === M //true
// js里所有的对象都有proto属性,指向构造该对象的构造函数的原型(用于继承)
//原型prototype是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法
o.__proto__ === M.prototype //true
M.__proto__ === Function.prototype //true 一层一层向上找

//instanceof 的原理就是判断实例对象的__proto__和 构造函数的原型是不是引用同一个地址
M.prototype.__proto__ === Object.prototype //true

console.log(o instanceof M) //true
console.log(o instanceof Object) //true

// 那么如何判断 o 到底是谁创建的呢
console.log(o.__proto__.constructor === M) //true

}

继承方式

1.借助构造函数 实现继承(call),但是父类的原型链的方法 子类无法继承,只能继承父类构造函数里边的属性和方法。

1
2
3
4
5
6
7
8
9
10
11
function Parent(){
this.name = 'parent'
}
Parent.prototype.say = function () {...}

function Child(){
Parent.call(this)
this.type = 'child'
}

console.log(new Child()) //{name:parent,type:child} 注意,没有父类原型的方法

2.原型链实现继承 由于子类的的对象原型链都是父类是一样的,所以改变一个子类的继承父类的属性,另一个子类也变了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Parent(){
this.name = 'parent';
this.arr = [1,2,3]
}

Parent.prototype.say = function () {console.log("say parent")}

function Child(){
this.type = 'child'
}

Child.prototype = new Parent()

console.log(new Child().name ) //parent
console.log(new Child().say() ) //say parent


var c1 = new Child()
var c2 = new Child()
c1.arr.push(4)
console.log(c1.arr ,c2.arr) //[1,2,3,4] [1,2,3,4]



3.组合继承方式 防止上边两种的缺点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Parent() {
this.name = 'Parent'
this.arr = [1,2,3]
}

function Child(){
Parent.call(this)
this.type = 'child'
}
Child.prototype = new Parent()

var c1 = new Child()
var c2 = new Child()

c1.arr.push(4)

console.log(c1.arr,c2.arr)

利用SASS转换REM

这里以IPONE6标准375宽度 / 10 = 37.5作为基准值 10这个数字是随便写的, 当然也可以选择不除,只要在后面动态js计算时保证一样的值就可以

1
2
3
4
5
6
7
8
9
10
11
12
@function px2rem($px) {
$rem: 37.5px;
@return ($px / $rem) + rem;
}



//使用
.header{
height:px2rem(40px);
}

配合用JS转换

1
2
3
4
5
6
7
8
9
10
11
<script>

window.onresize = function() {
change()
}
change()
function change() {
let html = document.documentElement || document.body;
html.style.fontSize = html.clientWidth / 10 + "px";
}
</script>

炎黄JS方案

1
2
3
4
5
6
7
8
9
10
11
new function (){
var _self = this ;
_self.width = 640; // 设置默认最大宽度
_self.fontSize = 100; // 默认字体大小
_self.widthProportion = function (){ var p = (document.body&&document.body.clientWidth||document.getElementsByTagName("html")[0].offsetWidth)/_self.width;return p>1?1:p<0.5?0.5:p;};
_self.changePage = function (){
document.getElementsByTagName("html")[0].setAttribute("style","font-size:"+_self.widthProportion()*_self.fontSize+"px !important");
}
_self.changePage();
window.addEventListener('resize', function (){_self.changePage();}, false );
};

  1. server.js
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
const express = require('express')
const fs = require('fs')
const path = require('path')
const port = 8000

const app = express()

// 设置模板引擎 路径是views目录下
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')

//form路由打开页面 是个表单 可提交文件 访问路由:http://localhost:8000/form/weibin
app.get('/form/:name', (req, res) => {
const person = req.params.name

const data = { age: 18, hobbie: ['eating', 'sleeping', 'working'] }

//渲染数据并返回html.index.ejs文件
res.render('index', { person: person, data: data })
})

app.listen(port, () => {
console.log('server is running at port: ' + port)
})



  1. server.js 下同目录文件夹 views 下 index.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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<%- include('header') -%>
<h2><%= person %></h2>
<h2><%= data.age %></h2>
<h2>hobbie:</h2>
<ul>
<% data.hobbie.forEach((item)=>{ %>
<li><%= item %></li>
<% }) %>
</ul>
<!-- formdata处理数据 -->
<form action="/upload" method="post" enctype="multipart/form-data">
<h2>ejs模板引擎</h2>
<input type="file" name="logo" />
<input type="submit" value="提交" />
</form>
</body>
</html>


  1. server.js
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
const express = require('express')
const fs = require('fs')
var multer = require('multer')
//Multer 是一个 node.js 中间件,用于处理 multipart/form-data 类型的表单数据
//指定上传文件的目录,再当前文件夹下的uploads 但是存储的是一段哈希值的文件,所以用下面的storage方法
// var upload = multer({ dest: 'uploads/' })

//判断目录不存在就创建目录
var createFolder = (folder) => {
try {
fs.accessSync(folder)
} catch (e) {
fs.mkdirSync(folder)
}
}
var uploadFolder = './upload/'
createFolder(uploadFolder)

//控制存储 将上传的文件存在指定文件夹下
var storage = multer.diskStorage({
//放置目标位置
destination: function (req, file, cb) {
cb(null, uploadFolder)
},

//上传文件的名称和扩展名一起originalname
filename: function (req, file, cb) {
cb(null, file.originalname)
},
})

var upload = multer({ storage: storage })

const port = 8000

const app = express()

//form路由打开页面 是个表单 可提交文件
app.get('/form', (req, res) => {
var form = fs.readFileSync('./index.html', { encoding: 'utf-8' })
res.send(form)


//或者直接使用express提供的sendFile
// res.sendFile(__dirname + '/index.html')
})

//提交的name 是 logo
app.post('/upload', upload.single('logo'), (req, res) => {
res.send({ code: 0 })
})

app.listen(port, () => {
console.log('server is running at port: ' + port)
})



  1. 表单页面 同目录下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- formdata处理数据 -->
<form action="/upload" method="post" enctype="multipart/form-data">
<h2>单图上传</h2>
<input type="file" name="logo" />
<input type="submit" value="提交" />
</form>
</body>
</html>