KevinSwift

  • 首页
  • 关于
  • 标签
  • 分类
  • 相册

相册功能添加说明

发表于 2019-11-02 | 分类于 博客相关

相册添加功能说明
将需要上传的图片添加到photos文件夹下,文件的命名方式为2019-11-01_hznu01.jpg 这种形式
随后执行根目录下的tool.py文件,会将图片进行压缩,并且写入min_photos文件夹。随后要通过source_tree上传,
因为网页里面读取的是github上的图片地址信息。随后修改source/photo下的data.json文件,显示的图片和github上图片对应。

1
2
3
4
5
6
7
8
9
10
11
{"arr": {"link": ["2019-09-07_csyt01.jpg","2019-09-07_csyt02.jpg","2019-09-07_csyt03.jpg","2019-09-07_csyt04.jpg",
"2019-09-07_csyt05.jpg","2019-09-07_csyt06.jpg","2019-09-07_csyt07.jpg","2019-09-07_csyt08.jpg",
"2019-09-07_csyt09.jpg","2019-09-07_csyt10.jpg","2019-09-07_csyt11.jpg","2019-09-07_csyt12.jpg",
"2019-09-07_csyt13.jpg","2019-09-07_csyt14.jpg","2019-09-07_csyt15.jpg","2019-09-07_csyt16.jpg",
"2019-09-07_csyt17.jpg"
], "text": ["\u5915\u9633\u65e0\u9650\u597d\uff0c\u53ea\u662f\u8fd1\u9ec4\u660f"], "month": 9, "type": ["image","image","image","image",
"image","image","image","image",
"image","image","image","image",
"image","image","image","image",
"image"
], "year": 2019}, "date": "2019-09"},

添加arrs数组,link为图片名称。修改month为想显示的月份,type里的image程度必须和link长度相同。

vscode的debug技巧

发表于 2019-10-22 | 分类于 调试相关

vscode每打开一个项目,会有一个./vscode文件夹产生,里面的是整个项目在vscode里面的运行配置。
我们可以配置每个项目的debug目录。

1
2
3
4
5
6
7
8
9
10
11
12
"configurations": [


{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:8080",
"webRoot": "${workspaceFolder}",
"file": "${workspaceFolder}/index.html"
}
]

表示加载一个chrome进程。来调试该网页。
可能会以文件形式file:///这种形式 或者localhost:8080
如果以文件形式打开的话,不利于debug.
因此可以利用另外一种配置形式 attach模式

1
2
3
4
5
6
7
8
9
{
"name": "Attach",
"type": "chrome",
"request": "attach",
"port": 9222,

"sourceMaps": false,
"webRoot": "${workspaceFolder}",
}

表示附加在当前chrome进程上,chrome的debug进程的端口号为9222
在mac端 chrome进程以debug模式打开。

1
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

npm全局安装包live-server
随后在本地目录夹跑起来

1
live-server

就可以在附加的chrome的debug进程上调试

offset,scroll,client了解

发表于 2019-09-06 | 分类于 js相关

1.offset
offset指偏移,包括这个元素在文档中占用的所有显示宽度,包括滚动条,padding,border,不包括overflow隐藏的部分。
obj.offsetWidth,指obj控件自身的绝对宽度。不包括overflow未显示的部分
obj.offsetHeight,只obj控件自身的绝对高度。不包括overflow未显示的部分
obj.offsetTop,指obj相对于版面或由offsetParent属性指定的父坐标的计算上的侧位置
obj.offsetLeft 指 obj 相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置,整型,单位:像素。
2.scroll
scroll指滚动,包括整个元素没显示出来的实际宽度,包括padding,不包括滚动条,border
scrollHeight 获取对象的滚动高度,对象的实际高度。
scrollLeft 设置或者获取位于对象左边界和窗口中目前可见内容的最左端之间的距离
scrollTop 设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离
scrollWidth 获取对象的滚动宽度
3.client
client指元素本身的可视内容,不包括overflow被折叠起来的部分,不包括滚动条,border
clientHeight 对象可见的高度
clientTop、clientLeft 这两个返回的是元素周围边框的厚度,一般它的值就是0。因为滚动条不会出现在顶部或者左侧

js中的this

发表于 2019-08-13 | 分类于 js相关

参考博客
js中的this,表示函数的当前执行上下文,js中函数调用主要有以下几种方式,
函数调用 alert(‘hello world’);
方法调用 console.log(‘hello world’);
构造函数 new RegExp(‘\d’)
隐式调用 alert.call(undefined,’hello world’)
理解this的关键是要清楚知道函数的调用以及如何影响上下文
函数表达式 表示定义函数并且在其中使用

1
2
3
4
function hello(name){
return 'hello' + name;
}
hello("ckq");

1.在函数调用中的this
this在函数调用中是一个全局对象
对象是由执行环境来决定的,在浏览器中,this是window对象
严格模式下的this
this在严格模式的函数调用中为undefined
严格模式是在ecs5中引入的,提供了更好的安全性和更强的类型检查。
2.方法调用
当一个表达式以属性访问的形式来执行时,执行的是方法调用,
例如myObject.helloFunction(),[1,2].join(‘,’);
在方法调用中,this是拥有这个方法的对象
object.create也是创建一个对象,调用的上下文仍然是对象本身
在es6的语法中,方法调用的上下文也是实例本身

陷阱:将方法与其对象分离
方法可以从对象中提取到一个单独的变量中,
const alone = myObj.method;
当方法调用的时候,与原始对象alone分离,如果方法在没有对象的情况下调用,那么函数调用就会发生,此时this指向全局对象window,严格模式下是undefined.
3.构造函数调用,

1
2
3
4
5
6
7
8
function country(name,travel){
this.name = name;
this.travel = travel;
}
country.travel = function() {
this.travel = true;
}
const frace = new country('france';false);

new City(‘paris’,false);是构造函数的调用,这个对象的初始化由这个类中一个特殊的方法constructor来处理,this指向新创建的对象
构造函数创建了一个新的空的对象,它从构造函数的原型继承了属性。构造函数的作用就是去初始化这个对象。 可能你已经知道了,在这种类型的调用中,上下文指向新创建的实例。
myobject.fun()时会执行构造函数的调用而不是方法调用
在构造函数调用中this指向新创建的对象
当忘记new的时候,在函数调用中,this是window对象,因此 Vehicle(’Car’,4)在window对象上设置属性。 显然这是错误,它并没有创建新对象
5 隐式调用
使用myFun.call()或者myFun.apply()方法调用函数的时候,执行的是隐式调用
对象的类型是function,.call()和.apply调用具有可配置的上下文的函数

方法 .call(thisArg[, arg1[, arg2[, …]]])将接受的第一个参数thisArg作为调用时的上下文,arg1, arg2, …这些则作为参数传入被调用的函数。
方法.apply(thisArg, [args])将接受的第一个参数thisArg作为调用时的上下文,并且接受另一个类似数组的对象[arg1, arg2, …]作为被调用函数的参数传入。

1
2
3
4
5
function increment(number){
return ++number;
}
increment.call(undefined,10); //11
increment.apply(undefined,[10]); //11

隐式调用非常有用。例如为了解决方法调用时,this总是window或严格模式下的undefined的上下文问题。隐式调用可以模拟在一个对象上调用另外某个方法

1
2
3
4
5
    function Runner(str) {
return str + this.name;
}
var rabbit = {name:'white '};
console.log(Runner.call(rabbit,"ccc"));

.bind()创建一个永久的上下文连接,并且始终保持它,一个函数不能通过.call或者.apply来改变它的上下文,甚至是再次绑定也没什么作用
6.箭头函数
箭头函数用以更短的形式声明函数,并在词法上绑定上下文,

1
2
3
4
const hello = (name) => {
return 'hello' + name;
}
hello('world');

箭头函数是匿名的,这意味着name属性是一个空字符串’’;跟常规函数也相反,也不提供arguments对象。但是,在es6中通过剩余参数修复了。

1
2
3
4
5
const sumArguments = (...args) => {
return args.reduce((result,item) => {
result + item;
})
}

**注意,箭头函数不会创建自己的上下文,而是从定义他的外部来获取上下文,换句话说,箭头函数在词汇上获取上下文。

1
2
3
4
5
this;
var myFunc = () => {
this;
}
myFunc();

angular学习(1)

发表于 2019-08-09 | 分类于 js相关

简介

angular js是一个MVVM框架,通过ng-app指令定义一个angular js的应用程序,
AngularJS 模块(Module) 定义了 AngularJS 应用。
AngularJS 控制器(Controller) 用于控制 AngularJS 应用。
ng-app指令指明了应用, ng-controller 指明了控制器。
ng-model来进行双向绑定
表达式可以写在双大括号内

1
{{}}

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
<body ng-app="LoginModel" ng-controller="LoginCtrl">
<script>
var app = angular.module('LoginModel', []); //可以通过依赖注入来注入服务
app.controller('LoginCtrl', function($scope,httpService) {
$scope.firstName= "John";
$scope.lastName= "Doe";
});
</script>
//定义http公共服务
//定义公有的http服务
LoginModel.factory("httpService",
function ($http, $q) {
return {
post: function (suburl, params) {
var defer = $q.defer();
$http({
method: 'POST',
params: params,
url: "http://dodo.hznu.edu.cn/" + suburl,
}).success(function (data) {
if (data.retcode == 0) {
defer.resolve(data.items);
}
else
defer.reject(data.message);
}).error(function (data) {
defer.reject(data);
});
return defer.promise;
},

$scope是当前的控制域,在当前控制域当中可以获得双向绑定的一些值和定义函数ng-click,ng-model等

1
2
3
4
$scope.user = {};
$scope.submit = function() {

}

angular js脏检查机制

监控对象的属性
$watch和$digest是相辅相成的,两者构成了angular作用域的核心,数据变化的响应

原理:

angular在scope模型上设置了一个一个监听队列,用来监听数据变化并且更新view,每次绑定一个东西
到view上的时候,angular 就会往$watch队列中插入一个$watch,用来检测他监控的model里面是否有变化的东西,
当浏览器接收到可以被angular context处理的事件时,$digest循环就会触发,遍历所有的$watch,最后更新dom.

$watch绑定要检查的值
当一个作用域创建的时候,angular会去解析模板中当前作用域下的模板结构,并且自动将插值,

1
2
{{text}}
ng-click = "update"

找出来,利用$watch建立绑定,并且push到当前作用域上的$$watcher队列
当使用$wacth绑定了要检查的属性之后,当这个属性值发生变化了,触发$digest循环,angular会去遍历这个数组,并且用$dirty去记录$$watcher里面记录的哪些$scope的属性值发生变化,当有变化的时候,dirty设置为true,一轮脏检查执行完,会再检查dirty,如果dirty为true的话,会再次调用自己,知道dirty为true.但是为了防止死循环,一般设置当递归发生了10次或者以上,直接抛出一个错误,并且跳出循环。
当递归流程执行完,$digest还将变化后的$scope重新渲染到界面,$apply函数

脏检查的实现:

bind(),apply(),$scope构造函数以及构造函数的原型方法$watch(),$digest()
实现的四步骤
1.实现数据的单向绑定
2.实现$watch()监听数据变化
3.实现$digest()循环遍历每一个watch对象
4.实例化$scope

触发脏检查时间

只有当UI事件,ajax请求或者timeout延迟事件,才会触发脏检查
angular 每一个绑定到UI的数据,都有一个$watch对象,

双向数据绑定

界面到数据的修改,是由ajax,UI事件,或者timeout等回调事件来做。
数据到界面的呈现是由脏检查机制来做的。

参考博客
掘金链接
angularjs常用函数:$apply,$watch和$digest
$digest是一个内部函数,正常的代码都是无法应用到的,想要主动触发他,就要调用scope.$apply函数,是触发angularjs”脏检查机制”的常用公开接口,$digest循环包括2个while循环,分别是,处理$evalAsync的异步运算队列,处理$watch的watchers队列,当该循环触发后,会遍历当前$scope及其所有的子$scope上已经注册的所有watcher函数,遍历一遍所有的watcher被称为一轮脏检查,执行完一轮脏检查后,如果任何一个watcher所监听的值改变了,就会重新再进行一轮脏检查,直到所有的函数都报告其所监听的值都不再改变了。当循环结束后,才把模型的变化结果更新到DOM上去,这样做是为了防止频繁的更新。
表达式,angularjs不仅会渲染该数据,还会为这个值创建创建一个观察序列,之后,只要程序发生了任何事情,angularjs就会检查该观察过程中的值是否发生了修改,如果有,重新呈现表达式,运行这些观察者的过程称为脏检查。

angular2 双向绑定机制

angularjs是由$scope.apply()和$scope.digest触发,angular2接入了Zonejs,监听所有的异步事件,zonejs重写了所有的异步API,
监听异步api,当异步api发生变化,通知angular以组件树的形式进行检查。
zonejs会通知angularjs可能有数据发生变化,需要检测更新。
脏检查就是存储变量所有的值,每当可能的值发生变化的时候,将所有变量的旧值和新值进行比较,不相等就更新视图。

angular js采用脏检查。而angular的核心是组件化,组件的嵌套会形成一棵树。angular的变化检测可以按组件进行,每个组件有相应的变化检测器changeDetector,可想而知,这些变化检测器也会构成一棵树。
angular数据流是自顶向下的,从父组件到子组件单向流动。单向数据流向保证了高效、可预测的变化检测,尽管检查了负组件之后,自组件可能会改变父组件的数据使得父组件需要再次被检查,这是不被推荐的数据处理方式。在开发模式下,Angular会进行二次检查,如果出现上述情况,二次检查就会报错:ExpressionChangedAfterItHasBeenCheckedError(关于这个问题的答案,可以在参考资料中找到)。而在生产环境中,脏检查只会执行一次。

vue js与angular js区别

vue js更加灵活,比起angular更少专制,能够按照自己想要的方式构建应用,而非凡事都得按照vue的方法。它只不过是一层界面而已,因此你可以拿它作为页面中一个轻量的功能来使用,而不是一个完整的 SPA。
vue 仅仅是一个view层,只是一个如jquery搬的工具库,而angular是一个mvvm框架。
vue 使用数据劫持,object.defineProperty来实现,angular使用自己实现的一套模板编译规则。”脏检查机制”,来实现。相对来说vue性能更高。
vue 需要提供一个el对象并且进行实例化,后续所有作用范围都在el对象之下,而angular是整个html页面,一个页面,可以有多个vue实例,而angular不是这样的。

angular常见面试题

链接1
链接2

js原理题的实现

发表于 2019-08-08 | 分类于 js相关

参考微信
1.实现一个call函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//将改变this指向的方法挂到目标this上执行并且返回
Function.prototype.mycall = function (context) {

if(typeof this !== 'function')
throw new TypeError("not function");
context = context || window;
context.fn = this;
//获取参数
//从后面获取
let args = [...arguments].slice(1);
let result = context.fn(...args);
delete context.fn;
return result;
}

2.实现一个apply函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype.myapply = function (context) {
if(typeof this !== 'function')
throw new TypeError("not function");
context = context || window;
context.fn = this;
let result;
if(arguments[1]){

result = context.fn(arguments[1]);
}else
result = context.fn();
delete context.fn;
return result;
}

3.实现bind函数,思路与call相似,但是返回的是函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function.prototype.mybind = function (context) {
if(typeof this !== 'function')
throw new TypeError("not function");
let _this = this;
let arg = [...arguments].slice(1);
return function F() {
//处理函数使用new的情况
if (this instanceof F) {
return new _this(...arg, ...arguments)
} else {
return _this.apply(context, arg.concat(...arguments))
}
}

};

实现instancceof方法

1
2
3
4
5
6
7
8
9
10
11
12
13
//instanceof 右边变量的原型位于左边变量的原型上 左边变量一直递归查找
function instanceOf(left,right) {
let leftValue = left.__proto__;
let rightValue = right.prototype;
while (true){
if(left == null)
return false;
if(leftValue == rightValue)
return true;
leftValue = leftValue.__proto__;
}

}

实现object.create方法

1
2
3
4
5
6
7
8
9
//object.create的原理
//思路 将传入的对象作为原型
function create(obj) {
function F() {

}
F.prototype = obj;
return new F();
}

new的本质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//new 的本质 创建一个新对象并且将其隐式原型指向构造函数
function myNew(fun) {
return function () {
let obj = {
__proto__:fun.prototype
}
fun.call(obj,...arguments);
return obj;
}
}
function person(name,age) {
this.name = name;
this.age = age;
}
let obj = myNew(person)('chen',18);

实现一个promise

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
 //实现一个promise
class Promise{
constructor(fn) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
let resolve = value => {
if (this.state == 'pending') {
this.state = 'fullfilled';
this.value = value;
}
}
let reject = value => {
if (this.state == 'pending') {
this.state = 'rejected';
this.reason = value;
}
}
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled,onRejected){
switch (this.state) {
case 'fullfilled':
onFulfilled();
break;
case 'rejected':
onRejected();
break;
}
}



}

实现浅拷贝

1
2
3
4
5
6
// 1. ...实现
let copy1 = {...{x:1}}

// 2. Object.assign实现

let copy2 = Object.assign({}, {x:1})

实现基本的深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. JOSN.stringify()/JSON.parse()
let obj = {a: 1, b: {x: 3}}
JSON.parse(JSON.stringify(obj))

// 2. 递归拷贝
function deepClone(obj) {
let copy = obj instanceof Array ? [] : {}
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
}
}
return copy
}

使用setTimeout模拟setInterval

1
2
3
4
setTimeout (function () {
// do something
setTimeout (arguments.callee, 500)
}, 500)

js实现继承方法

1
2
3
4
5
6
7
8
9
10
// 借用构造函数继承实例属性
function Child () {
Parent.call(this)
}
// 寄生继承原型属性
(function () {
let Super = function () {}
Super.prototype = Parent.prototype
Child.prototype = new Super()
})()

实现一个eventBus

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
// 组件通信,一个触发与监听的过程
class EventEmitter {
constructor () {
// 存储事件
this.events = this.events || new Map()
}
// 监听事件
addListener (type, fn) {
if (!this.events.get(type)) {
this.events.set(type, fn)
}
}
// 触发事件
emit (type) {
let handle = this.events.get(type)
handle.apply(this, [...arguments].slice(1))
}
}

// 测试
let emitter = new EventEmitter()
// 监听事件
emitter.addListener('ages', age => {
console.log(age)
})
// 触发事件
emitter.emit('ages', 18) // 18

实现双向数据绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get() {
console.log('获取数据了')
},
set(newVal) {
console.log('数据更新了')
input.value = newVal
span.innerHTML = newVal
}
})
// 输入监听
input.addEventListener('keyup', function(e) {
obj.text = 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
// hash路由
class Route{
constructor(){
// 路由存储对象
this.routes = {}
// 当前hash
this.currentHash = ''
// 绑定this,避免监听时this指向改变
this.freshRoute = this.freshRoute.bind(this)
// 监听
window.addEventListener('load', this.freshRoute, false)
window.addEventListener('hashchange', this.freshRoute, false)
}
// 存储
storeRoute (path, cb) {
this.routes[path] = cb || function () {}
}
// 更新
freshRoute () {
this.currentHash = location.hash.slice(1) || '/'
this.routes[this.currentHash]()
}
}

实现懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let imgs =  document.querySelectorAll('img')
// 可视区高度
let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
function lazyLoad () {
// 滚动卷去的高度
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
for (let i = 0; i < imgs.length; i ++) {
// 图片在可视区冒出的高度
let x = clientHeight + scrollTop - imgs[i].offsetTop
// 图片在可视区内
if (x > 0 && x < clientHeight+imgs[i].height) {
imgs[i].src = imgs[i].getAttribute('data')
}
}
}
// addEventListener('scroll', lazyLoad) or setInterval(lazyLoad, 1000)

rem实现原理

1
2
3
4
5
6
7
8
9
// 原始配置
function setRem () {
let doc = document.documentElement
let width = doc.getBoundingClientRect().width
let rem = width / 75
doc.style.fontSize = rem + 'px'
}
// 监听窗口变化
addEventListener("resize", setRem)

手写ajax

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
// 1. 简单流程

// 实例化
let xhr = new XMLHttpRequest()
// 初始化
xhr.open(method, url, async)
// 发送请求
xhr.send(data)
// 设置状态变化回调处理请求结果
xhr.onreadystatechange = () => {
if (xhr.readyStatus === 4 && xhr.status === 200) {
console.log(xhr.responseText)
}
}

// 2. 基于promise实现

function ajax (options) {
// 请求地址
const url = options.url
// 请求方法
const method = options.method.toLocaleLowerCase() || 'get'
// 默认为异步true
const async = options.async
// 请求参数
const data = options.data
// 实例化
const xhr = new XMLHttpRequest()
// 请求超时
if (options.timeout && options.timeout > 0) {
xhr.timeout = options.timeout
}
// 返回一个Promise实例
return new Promise ((resolve, reject) => {
xhr.ontimeout = () => reject && reject('请求超时')
// 监听状态变化回调
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
// 200-300 之间表示请求成功,304资源未变,取缓存
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
resolve && resolve(xhr.responseText)
} else {
reject && reject()
}
}
}
// 错误回调
xhr.onerror = err => reject && reject(err)
let paramArr = []
let encodeData
// 处理请求参数
if (data instanceof Object) {
for (let key in data) {
// 参数拼接需要通过 encodeURIComponent 进行编码
paramArr.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
}
encodeData = paramArr.join('&')
}
// get请求拼接参数
if (method === 'get') {
// 检测url中是否已存在 ? 及其位置
const index = url.indexOf('?')
if (index === -1) url += '?'
else if (index !== url.length -1) url += '&'
// 拼接url
url += encodeData
}
// 初始化
xhr.open(method, url, async)
// 发送请求
if (method === 'get') xhr.send(null)
else {
// post 方式需要设置请求头
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8')
xhr.send(encodeData)
}
})
}

实现拖拽

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
window.onload = function () {
// drag处于绝对定位状态
let drag = document.getElementById('box')
drag.onmousedown = function(e) {
var e = e || window.event
// 鼠标与拖拽元素边界的距离 = 鼠标与可视区边界的距离 - 拖拽元素与边界的距离
let diffX = e.clientX - drag.offsetLeft
let diffY = e.clientY - drag.offsetTop
drag.onmousemove = function (e) {
// 拖拽元素移动的距离 = 鼠标与可视区边界的距离 - 鼠标与拖拽元素边界的距离
let left = e.clientX - diffX
let top = e.clientY - diffY
// 避免拖拽出可视区
if (left < 0) {
left = 0
} else if (left > window.innerWidth - drag.offsetWidth) {
left = window.innerWidth - drag.offsetWidth
}
if (top < 0) {
top = 0
} else if (top > window.innerHeight - drag.offsetHeight) {
top = window.innerHeight - drag.offsetHeight
}
drag.style.left = left + 'px'
drag.style.top = top + 'px'
}
drag.onmouseup = function (e) {
this.onmousemove = null
this.onmouseup = null
}
}
}

实现防抖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 思路:在规定时间内未触发第二次,则执行
function debounce (fn, delay) {
// 利用闭包保存定时器
let timer = null
return function () {
let context = this
let arg = arguments
// 在规定时间内再次触发会先清除定时器后再重设定时器
clearTimeout(timer)
timer = setTimeout(function () {
fn.apply(context, arg)
}, delay)
}
}

function fn () {
console.log('防抖')
}
addEventListener('scroll', debounce(fn, 1000))

实现节流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 思路:在规定时间内只触发一次
function throttle (fn, delay) {
// 利用闭包保存时间
let prev = Date.now()
return function () {
let context = this
let arg = arguments
let now = Date.now()
if (now - prev >= delay) {
fn.apply(context, arg)
prev = Date.now()
}
}
}

function fn () {
console.log('节流')
}
addEventListener('scroll', throttle(fn, 1000))

只能说tql

node.js事件循环机制探索

发表于 2019-08-08 | 分类于 js相关 , node相关

每当我们运行一个程序时,都会创建一个实例,并且我们会调用一些内部线程,它们是与该实例相关联的,线程可以看做是CPU必须执行的操作单元,许多不同的线程可以与程序的单个进程相关联。
每当我们能运行node程序时,都会自动创建一个线程,这个线程就是我们整个代码库被执行的唯一地方,其中,还生成了事件循环的东西,这个循环的作用是安排我们唯一的线程在某个给定的时间点应该执行哪些操作。
事件循环实际上就是一个循环,事件循环的每次迭代都成为tick.
事件循环执行tick的条件是什么:
挂起的定时器操作(setTimeout(),setInterval());
挂起的操作系统任务(OS)
挂起的长时间运行操作的执行
只要其中一个操作处于挂起的状态,事件循环就会执行一个新的tick
执行tick
阶段1
node查看挂起的计时器内部集合。并检查传递给setTimeout()和setInterval()
阶段2
node查看挂起的os任务的内部集合,并检查哪些回调函数已经准备好被调用
从计算机检索文件便是一个例子
阶段3
node暂停执行,等待新事件的出现。新事件包括:新的计时器完成、新的 OS 任务完成和新的挂起操作完成。
阶段 4:Node 检查是否准备好调用与挂起定时器(挂起定时器与setImmediate()函数相关)相关的任何函数。
阶段 5:管理关闭事件,用于清理应用程序的状态。
node js完全是单线程吗
错误,node js只是js的运行时单线程的,但是node js标准库中包含的函数并非如此,其异步IO通过线程池的调用是单线程的
例如fs模块函数,是为了保持程序的速度和性能。
这些线程在哪里外包?
使用node js时,使用一个名为libuv的特殊库模块来执行异步操作,此库还与node的后向逻辑一起被用来管理称为libuv的特殊线程池。

面经笔记

发表于 2019-08-05 | 分类于 面经

阿里一面凉经
nod中版本号前的^是什么意思
~x.y.z匹配大于x.y.z的z的最新版
^x.y.z匹配大于x.y.z的y.z的最新版
body-parser:~1.15.2 匹配1.15的最新版
body-parser:^1.15.2 匹配3.x.x的最新版
参考网站
http报文的格式:
http请求报文有:请求行,请求头,和请求体组成
http响应报文有:响应行,响应头,和响应体组成。
参考网站
npm的含义 包管理器
promise async 和 await的区别
参考网站
tcp底层原理
参考网站

页面性能优化与常见的攻击

发表于 2019-07-21 | 分类于 js相关

互联网有一项著名的8秒规则,用户在访问web页面的时候,如果时间超过了8秒就会感觉到不耐烦,如果加载需要太长的时间,他们就会放弃访问。向网页开发者表明了加载时间的重要性
1.资源合并和压缩:主要包括有html压缩,css压缩,js的压缩和混乱文件的合并
从文件中去除多余的字符,例如回车,空格等。
css代码的压缩:是无效代码删除和css语义合并
js的压缩和混乱:1.无效字符的删除 2.删除注释 3代码语义的缩减和优化 4.代码的保护
css和js的压缩是非常有必要的,压缩合并css和js可以减少网站http请求的次数,但合并文件可能会带来问题:首屏渲染和缓存失效问题
2.非核心代码进行异步加载。
异步加载的3种方式-async和defer。动态脚本的创建

1
<script src = "text/javascript" src = "xxx.js" async = "async">

defer是在html解析完之后才会执行,如果是多个,则会按照加载的顺序依次执行,
async是在加载完之后立即执行,如果是多个,执行顺序和加载顺序无关
3.利用浏览器缓存
3.1利用前缓存 不会向服务器发送请求 直接从缓存中读取资源
3.2利用协商缓存 向服务器发送请求,服务器会根据request header中的一些参数来判断是否命中协商缓存
强缓存优先于协商缓存,若强制缓存生效则直接使用缓存。若不生效则使用协商缓存。
用户行为对浏览器缓存的影响
1.地址栏访问,链接跳转是正常用户行为,将会触发浏览器的缓存机制
2.F5刷新,浏览器会设置max-age = 0,跳过强缓存判断,会进行协商缓存判断。
3.ctrl + F5,跳过强缓存和协商缓存,直接从服务器获取数据
4.使用CDN
浏览器的缓存只是为了提高二次访问的速度需要从网络层面进行优化,最常见的是CDN(内容分发网络),通过将静态资源(js,css,html图片)等缓存到离用户很近的相同网络运营商的CDN节点上,不但提高用户的访问速度,还能节省服务器的带宽消耗,降低负载。
CDN服务商在全国各个省份部署计算节点,当到达计算节点时,首先判断缓存是否有效,若有效,则立即响应缓存给用户,否则,就去内容服务器上请求数据,反馈给用户,并将内容缓存下来以便响应给后续访问的用户。
一个地区内只要有一个用户预先加载资源,在CDN中建立了缓存,该地区中的其余用户便会因此而受益
5.
预解析DNS 使用该技术预先告知浏览器某些资源可能会在将来被使用,
通过DNS预解析告诉浏览器我们未来可能从某个特定的URL获取资源,当浏览器真正使用的时候,就可以尽快完成DNS的解析。

1
<link rel = "dns-prefetch" href = "//example.com">

懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种很好用的性能优化方式,,
用户滚动到它们之前,可视区域外的图像不会加载,
使用延迟加载使得网页加载更快
为什么使用懒加载:1.能提升用户体验。2.减少无效资源的加载 3.防止并发加载的资源过多会阻塞js的加载。
预加载:另一个性能优化技术,告知浏览器某些资源可能会在未来使用到,
预加载把所有资源提前加载到本地,这样后面要使用的话就直接从缓存中读取。

懒加载和预加载的对比,
两者主要区别是一个是提前加载,一个是迟缓甚至不加载,懒加载对服务器端有一定的缓解压力作用,预加载则会增加服务器端压力。
函数防抖和节流
函数节流(throttle):一个函数执行一次后,只有大于设定的执行周期后才会执行第二次。
有个需要频繁触发的函数,出于性能优化,在规定时间内,只让函数触发一次。
判断当前时间戳和上次执行的时间戳是否已经达到规定时间段。
函数节流:
1.DOM元素的拖拽功能,计算鼠标移动,射击游戏的mousedown/keydown.
函数防抖(debounce):
防抖函数:一个需要频繁触发的函数,在规定时间内,只让最后一次生效即可,前面的不生效

常见的攻击和防御方法

xss(跨站脚本攻击),通过存在安全漏洞的web网站注册用户的浏览器内运行非法的html标签或者js进行攻击
1.利用虚假输入表单获取信息
2.利用脚本窃取用户的cookie值。被害者在不知情的情况下,帮助攻击者发送恶意请求。
3.显示伪造的文章或者图片
防御XSS攻击:
CSP建立白名单,开发者明确告诉浏览器哪些外部资源可以加载
通过设置http header

1
content-security-policy:defalut-src 'self'

通过使用转移字符
3.httpOnly cookie
预防xss攻击的最有效手段 设置httpOnly,避免网页的cookie被客户端恶意js获取,保护用户的cookie信息
2.csrf攻击:
在登录a的情况下已经设置好cookie,所以在登录恶意网站时,将a网站上的cookie信息获取。
防范:get请求不对数据进行修改,不让第三方网站访问到用户的cookie.阻止第三方网站请求接口,请求时必须携带验证信息,例如cookie
3.点击劫持:视觉欺骗的攻击手段,攻击者将需要攻击的网站通过iframe嵌套到自己的网页中,并将iframe设置为透明,在页面中透出一个按钮诱导用户点击,特点:隐蔽性较高,UI-覆盖点击,利用iframe或者其他标签的属性。
4.URL跳转漏洞:借助未验证的URL进行跳转,将应用程序引导到不安全的第三方区域,从而导致安全问题。
5.SQL注入,一种典型的web安全漏洞,攻击者利用这个漏洞,可以访问或者修改数据,或者利用潜在的数据库漏洞进行攻击。
6.OS命令注入攻击:和SQL注入差不多,SQL注入是针对数据库的,而OS注入是针对操作系统的。OS命令注入攻击指的是通过Web应用,执行非法的操作系统命令而达到攻击的目的,只要在能调用shell函数的地方就能存在被攻击的风险。防御:进行规则限制,
在调用系统命令前将所有传入参数进行命令行参数转义过滤。不要直接拼接命令语句,借助一些工具作拼接。,例如 Node.js 的 shell-escape npm包

http相关知识,tcp知识,web实时推送

发表于 2019-07-20

http状态码:
1xx 指示信息-表示请求已经接受,继续处理 表示客户端需要提交下一步请求
2xx 成功 表示请求已被成功接受
304未修改,客户端的缓存是最新的
3xx 重定向 要完成请求需要进一步操作 302(重定向), 307 和 304(拿缓存)
4xx 客户端错误 请求有语法错误等
5xx 服务器端错误 服务器未能实现合法的请求

403 //对被请求的页面访问禁止 404 请求资源不存在 比如输入了错误的URL
post 向URL指定的资源提交数据
put 提交数据 put指定了在服务器上的位置 而post没有
head 只请求页面的首部
delete 方法:删除服务器上的某资源
options 方法:它用于获取当前 URL支持的方法,如果请求成功,会有一个 Allow 的头包含类似“GET,POST”这样的信息

持久连接:只要任意一端没有提出明确断开连接,则保持TCP的连接状态

https的工作原理:!!!
https在HTTP上建立了SSL加密层,对传输数据进行了加密,是http的安全版
主要作用:
1.对数据进行加密,并建立一个信息安全通道,来保证传输数据的过程中的数据安全
2.对网站服务器进行真实身份认证
http使用的是明文进行发送。
完成了数据隐私性传输,完整性的校验,身份认证
http先和SSL通信,再由SSL和tcp通信。
对称加密:加密和解密用同一个秘钥,将秘钥也需要发送给对方。
非对称加密:私有秘钥不能让任何人知道,公开秘钥可以随意发布
使用公开秘钥加密的方式:发送密文的一方使用对方的公开秘钥进行加密,
对方收到后,利用私钥进行解密。
非对称加密特点是信息一传输多,服务端只需要维护一个私钥就能和多个客户端进行通信

http使用的方式:在交换秘钥环节使用非对称加密方式,之后的建立通信交换报文阶段采用对称加密方式

发送密文的一方使用对方的公开秘钥加密处理对称的秘钥,然后对方利用自己的私钥进行解密拿到对称的秘钥,这样可以确保秘钥在安全的情况下,使用对称秘钥进行加密

解决报文是否可能遭到篡改 – 数字签名
确定消息是由发送方签名并且发过来的,因为别人假冒不了
数字签名保证消息的完整性,证明数据没有被篡改
文本用hash生成消息摘要 再由私钥生成数字签名,发送给接受者
接受者只有用发送者的公钥才能进行解密,然后用hash函数产生摘要信息。

解决通信双方身份可能被伪装的问题:数字证书
数字认证机构处于客户端和服务器双方都可信赖的第三方机构的立场上

https通信过程
1.client发送https请求 server的连接端口为443
2.server把事先配置好的公钥证书返回给客户端
3.client验证公钥证书
4.clent使用伪随机数产生对称秘钥,然后用证书的公钥加密,发送给server
5.server利用自己私钥进行解密,client和server拥有了相同的私钥
6.server使用对称秘钥加密明文内容,发送给client
7.client使用对称秘钥解密密文,得到明文内容
8.client再次发送http,使用对称秘钥加密请求B,然后server使用对称秘钥解密

tcp3次握手过程
client -> server发送
server 接收后 回复ack
client收到ack后回复ack

问:为什么建立连接需要3次
答:防止失效的连接请求报文段被服务端接收,从而产生报错 A是client,B是server
发送的失效报文到达时,A和B已经断开了,但是B又建立了连接,向A发送数据,所以造成资源浪费
tcp 4次挥手
客户端打算关闭连接
tcp停止释放报文,fin = 1
服务器接收到后确认,确认号是ack + 1
客户端到服务器这个连接已经释放,但是服务器到客户端还未释放,client不能发送,server不能接受,但是server可以发送,client可以接受
因为tcp是全双工的,两端可以同时发送和接收数据

服务器到客户端发送fin
客户端接收到后,必须发出请求,ack = 1
运输层中有传输控制协议tcp和用户数据协议udp
dns查看 浏览器缓存 -> 系统缓存 -> 路由器缓存

http/2相比于http/1,可以说大幅度提高了网页的性能,只需要升级到该协议就能减少很多之前需要做的性能优化工作,
当然兼容问题以及如何优雅降级应该是国内还不普遍使用的原因之一。
http/3相对于http/2又是一个提升
http协议:
处于计算机网络中的应用层,http是建立在tcp协议之上,所以http协议的瓶颈及其优化技巧都是基于tcp本身的特性,
例如tcp建立的3次握手和断开连接的4次挥手以及每次建立连接带来的RTT延迟时间

http/1.x缺陷
连接无法复用 每次都会导致3次握手和慢启动,慢启动对大量小文件请求影响较大。
head-of-line blocking(holb) 导致宽度无法被充分利用,以及后续请求被阻塞
http/1.x在传输数据时,所有传输内容都是明文,客户端和服务端都无法验证对方身份,这在一定程度上无法保证数据的安全性

http/2方法/状态码/语义都与http/1.x一样,http/2基于SPDY3协议,专注于性能,最大的目标是在用户和网站之间只用一个连接。

http/2采用二进制格式传输数据,而非http1.x的文本格式。
http/2采用了多路复用技术。
解决了同一个域名下的请求数量的问题。
同时也更容易实现全速传输。
http/2
同域名下所有通行都在单个连接上完成。
单个连接可以承载任意数量的双向数据流。
数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。
同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应,消除了因多个 TCP 连接而带来的延时和内存消耗。
并行交错地发送多个请求,请求之间互不影响。
并行交错地发送多个响应,响应之间互不干扰。
在HTTP/2中,每个请求都可以带一个31bit的优先值,0表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。
http/2只采用一个tcp协议,
http/2压缩了header
server-push也叫cache push
某些资源客户端一定会请求,可以采用服务端push方式,提前给客户端必要的资源。
http3协议是基于QUIC协议,QUIC协议是基于UDP协议。
QUIC新功能:
0-RTT 通过使用类似TCP快速打开的技术,缓存当前会话的上下文,在下次恢复会话的时候,只需要将之前的缓存传递给服务端验证通过就可以进行传输了。0-RTT建立连接可以说是QUIC相比HTTP2最大的性能优势。
https建立一次完全握手需要3个RTT,而QUIC建立在UDP基础上,同时又实现了0RTT的安全握手,所以在大部分情况下,只需要0个RTT就能实现数据传送。
同一条QUIC连接上可以创建多个stream,来发送多个http请求。
QUIC有向前纠错机制,每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。
http/1.x有连接无法复用,队头阻塞,协议开销大和安全因素等多个缺陷。
http/2通过多路复用,二进制流,Header等压缩技术,极大的提高了性能。但是还是存在性能问题。
quic基于udp协议来实现,是http/3中的底层支撑协议,该协议基于UDP,又吸取了TCP中的精华,实现了既块又可靠的协议传输。

web实时推送机制 将后台发生的变化主动的,实时的传送到浏览器端。而不需要用户端主动刷新页面
轮询是客户端和服务器之间会一直进行连接,每隔一段时间就询问一次,缺点:连接数会非常多,一个接受,一个发送,
而且每次发送请求都会有http的header,会很耗流量,也会消耗cpu的利用率。 长轮询客户端发送http给服务器后,看有没有新消息,如果没有新消息,就一直等待,当有新消息的时候,才会返回给客户端。在某种程度上减小了网络带宽和CPU利用率等问题。由于http数据包的头部数据量往往很大(通常有400多个字节),但是真正被服务器用到的数据却很少,
这样的数据在网络上周期性的传输,
难免对网络带宽是一种浪费 iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间建立一条长连接,服务器想iframe传输数据,来实时更新页面。
优点:消息能够实时到达;浏览器兼容好
缺点:服务器维护一个长连接会增加开销;IE、chrome、Firefox会显示加载没有完成,图标会不停旋转。
web socket机制,web服务器和客户端之间建立连接后,之后所有的通信都依靠这个专用协议进行。任意一方可直接向对方发送报文。
http是半双工协议,web socket是全双工协议。
tcp和udp的比较
tcp/ip是互联网相关的各类协议族的总称,例如tcp,udp,ip,ftp,http,icmp,smtp等
是互联网的基础,一系列网络协议的总称,
这些协议可以划分为四层,分别为链路层、网络层、传输层和应用层。
tcp/ip的4层模型
链路层 -> 网络层 -> 传输层 -> 应用层
udp协议全称是用户数据报协议,在网络中它与 TCP 协议一样用于处理数据包,是一种
无连接的协议。
面向无连接,不需要和tcp一样在发送数据前建立3次握手。
有单播,多播,广播的功能。
支持一对一,一对多,多对多,多对一的方式。
udp是面向报文的,是不可靠的。
头部开销小,传播数据报文是很高效的
两台计算机通信时,需要畅通并且可靠。
当你想查看网页或查看电子邮件时,希望完整且按顺序查看网页,而不丢失任何内容。当你下载文件时,希望获得的是完整的文件,而不仅仅是文件的一部分,因为如果数据丢失或乱序,都不是你希望得到的结果,于是就用到了 TCP。
tcp协议全称是传输控制协议,是一种面向连接的,可靠的,基于字节流的传输层通信协议.
3次握手和4次挥手
tcp仅支持单播传输
ajax是一种
异步请求数据的web开发技术**,在不需要重新刷新页面的情况下,ajax通过异步请求加载后台数据,并在前端显示。
ajax目的是提高用户体验,减少网络数据传输量
基于浏览器提供的xmlhttprequest对象,使得浏览器可以发出http请求和接收到http响应。
浏览器接着干自己的事情

123…6
KevinSwift

KevinSwift

58 日志
28 分类
64 标签
© 2020 KevinSwift
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4