0%

用fs文件系统模块

在使用fs模块操作文件时,如果提供的操作路径是以./或../开头的相对路径时,很容易出现路径动态拼接错误的问题
原因:代码在运行的时候,会以执行 node 命令时所处的目录,动态拼接出被操作文件的完整路径
解决方案:
1.在使用fs模块操作文件时,直接提供完整的路径,不要提供./或../开头的相对路径,从而防止路径动态拼接的问题。缺点:移植性非常差,不利于维护
2.使用 __dirname + ‘相对路径’,__dirname表示当前文件所处的目录

1
2
3
4
5
6
7
const fs = require('fs');
fs.readFile(__dirname+'/files/1.txt','utf-8',function(err,dataStr) {
if(err) {
return console.log('读取文件失败!'+err.message);
}
console.log('读取文件成功'+dataStr);
})

用path路径模块

使用path.join()方法,可以把多个路径片段拼接为完整的路径字符串

1
2
3
4
5
6
7
8
9
10
const fs = require('fs');
const path = require('path');
const pathStr = path.join(__dirname,'/files/1.txt');
fs.readFile(pathStr,'utf-8',function(err,dataStr) {
if(err) {
return console.log('读取文件失败!'+err.message);
}
console.log('读取文件成功'+dataStr);
})

1.1 同源策略

1. 什么是同源

如果两个页面的协议,域名和端口都相同,则两个页面具有相同的源
列如,下表给出了相对于 http://www.test.com/index.html 页面的同源检测:

URL 是否同源 原因
http://www.test.com/other.html 同源(协议、域名、端口相同)
https://www.test.com/about.html 协议不同(http 与 https)
http://blog.test.com/movie.html 域名不同(www.test.com 与 blog.test.com)
http://www.test.com:7001/home.html 端口不同(默认的 80 端口与 7001 端口)
http://www.test.com:80/main.html 同源(协议、域名、端口相同)

2. 什么是同源策略

同源策略(英文全称Same origin policy) 是浏览器提供的一个安全功能
MDN官方给定的概念:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行互换。这是一个用于隔离潜在恶意文件的重要安全机制
通俗的理解:浏览器规定,A网站的JavaScript,不允许和非同源的网站C之间,进行资源的交互,例如:
1.无法读取非同源网页的Cookie,LocalStorage和IndexedDB
2.无法接触非同源网页的DOM
3.无法向非同源地址发送Ajax请求

1.2 跨域

1. 什么是跨域

同源指的是两个URL的协议、域名、端口一致,反之,则是跨域
出现跨域的根本原因:浏览器的同源策略不允许非同源的URL之间进行资源的互换

2. 浏览器对跨域请求的拦截

跨域

3.如何实现跨域数据请求
现如今,实现跨域数据请求,最主要的两种解决方案,分别是JSONP和CORS
JSONP:出现的早,兼容性好(兼容低版本IE)。是前端程序员为了解决跨域问题,被迫想出来的一种临时解决方案。缺点是只支持GET请求,不支持POST请求。
CORS:出现的较晚,它是W3C标准,属于跨域Ajax请求的根本解决方案。支持GET和POST请求。缺点是不兼容某些低版本的浏览器

2.1 什么是JSONP

JSONP(JSON with Padding)是JSON的一种”使用模式”,可用于解决主流浏览器的跨域数据访问的问题

2.2 JSONP的实现原理

由于浏览器同源策略的限制,网页中无法通过Ajax请求非同源的接口数据。但是<script>标签不受浏览器同源策略的影响,可以通过src属性,请求非同源的js脚本
因此,JSONP的实现原理,就是通过<script>标签的src属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据

2.3 自己实现一个简单的JSONP

定义一个success回调函数:

1
2
3
4
function success(data) {
console.log('获取到了data数据');
console.log(data);
}

通过<script>标签,请求接口数据:

1
<script src="http://ajax.frontend.itheima.net:3006/api/jsonp?callback=success&name=zs&age=20"></script>

2.4 JSONP的缺点

由于JSONP是通过<script>标签的src属性,来实现跨域数据获取的,所以,JSONP只支持GET数据请求,不支持POST请求
注意:JSONP和Ajax之间没有任何关系,不能把JSONP请求数据的方式叫做Ajax,因为JSONP没有用到XMLHttpRequest这个对象

什么是axios

Axios是专注于网络数据请求的库
相比于原生的XMLHttpRequest对象,axios简单易用
相比于jQuery,axios更加轻量化,只专注与网络数据请求

axios发起GET请求

语法

1
axios.get('url',{params:{/*参数*/}}).then(callback)

示例

1
2
3
4
5
6
7
8
9
10
// 请求的URL地址
var url = 'http://www.liulongbin.top:3006/api/get';
// 请求的参数对象
var paramsObj = {name:'zs',age:20};
// 调用 axios.get() 发起 GET请求
axios.get(url,{params:paramsObj}).then( (res) => {
// res.data 是服务器返回的数据
var result = res.data;
console.log(result);
})

axios发起POST请求

语法

1
axios.get('url',{/*参数*/}).then(callback)

示例

1
2
3
4
5
6
7
8
9
10
// 请求的URL地址
var url = 'http://www.liulongbin.top:3006/api/post';
// 要提交到服务器的数据
var dataObj = {location:'北京',address:'顺义'};
// 调用 axios.post() 发起 POST请求
axios.post(url,dataObj).then( (res) => {
// res.data 是服务器返回的数据
var result = res.data;
console.log(result);
})

直接使用axios发起请求

axios也提供了类似与jQuery中$.ajax()的函数
语法

1
2
3
4
5
6
axios({
method: '请求类型',
url: '请求的URL地址',
data: {/*POST数据*/},
params: {/*GET参数*/}
}).then(callback)

直接使用axios发起GET请求

1
2
3
4
5
6
7
8
9
10
11
axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/get',
params: {
// GET 参数要通过 params 属性提供
name: 'zs',
age: 20
}
}).then((res) => {
console.log(res.data);
})

直接使用axios发起POST请求

1
2
3
4
5
6
7
8
9
10
11
axios({
method: 'POST',
url :'http://www.liulongbin.top:3006/api/post',
data: {
// POST 数据要通过 data 属性提供
bookname :'程序员的自我修养',
price: 666
}
}).then( (res) => {
console.log(res.data);
})

XMLHttpRequest发起GET请求

步骤:
1.创建xhr对象
2.调用 xhr.open()函数
3.调用 xhr.send()函数
4.监听 xhr.onreadystatechange 事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1.创建xhr对象
var xhr = new XMLHttpRequest();
// 2.调用 xhr.open()函数,指定 请求方式 与 URL地址
xhr.open('GET','http://www.liulongbin.top:3006/api/getbooks');
// 2.1 使用xhr发起带参数的GET请求 这种在 URL 地址后面拼接的参数,叫做查询字符串
// xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks?id=1');
// 3.调用 send函数 发起ajax 请求
xhr.send();
// 4.监听 onreadystatechange 事件
xhr.onreadystatechange = () => {
// 4.1 监听 xhr 对象的请求状态 readyState;与服务器响应的状态 status
if(xhr.readyState === 4 && xhr.status === 200) {
// 4.2 打印服务器响应回来的数据
console.log(xhr.responseText);
}
}

了解xhr对象的readyState属性

XMLHttpRequest 对象的 readyState 属性,用来表示当前 Ajax 请求所处的状态。每个 Ajax 请求必然处于以下状态中的一个:

状态 描述
0 UNSENT XMLHttpRequest 对象已被创建,但尚未调用 open方法
1 OPENED open() 方法已经被调用
2 HEADERS_RECEIVED send() 方法已经被调用,响应头也已经被接收
3 LOADING 数据接收中,此时 response 属性中已经包含部分数据
4 DONE Ajax 请求完成,这意味着数据传输已经彻底完成或失败

XMLHttpRequest发起POST请求

1.创建 xhr对象
2.调用 xhr.open()函数
3.设置 Content-Type属性(固定写法)
4.调用 xhr.send()函数,同时指定要发送的数据
5.监听 xhr.onreadystatechange 事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1.创建 xhr对象
var xhr = new XMLHttpRequest();
// 2.调用 open()
xhr.open('POST','http://www.liulongbin.top:3006/api/addbook');
// 3.设置 Content-Type属性(固定写法)
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
// 4.调用 xhr.send()函数,同时将数据以查询字符串的形式,提交给服务器
xhr.send('bookname=水浒传&author=施耐庵&publisher=天津图书出版社')
// 5.监听 xhr.onreadystatechange 事件
xhr.onreadystatechange = () => {
if(xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
}

封装自己的Ajax函数

要实现的效果

1
2
3
4
5
6
7
8
9
10
11
ajax({
method: '请求类型',
url: '请求地址',
data: {
请求参数对象
},
success: (res) => { // 成功的回调函数
console.log(res); //打印数据
}
})

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
// 需要把data对象,转化成查询字符串的形式,从而提交给服务器,因此提前定义 resolveData 函数如下:
/**
 * 处理 data 参数
 * @param {data} 需要发送到服务器的数据
 * @returns {string} 返回拼接好的查询字符串 name=zs&age=10
 */

function resolveData(data) {
let arr = [];
// 遍历对象,k=value
for(let k in data) {
arr.push(k + '='+ data[k]);
}
// 将数组连成字符串,用&分隔符
return arr.join('&');
}

// 定义ajax函数
// 在ajax()函数中,需要创建xhr对象,并监听 onreadystatechange 事件
function ajax(options) {
let xhr = new XMLHttpRequest();
// 拼接查询字符串
let qs = resolveData(options.data);

// 监听请求状态改变的事件
xhr.onreadystatechange = () => {
if(xhr.readyState === 4 && xhr.status === 200) {
// JSON.parse(),把字符串转换为数据对象
let result = JSON.parse(xhr.responseText);
options.success(result);
}
}
if(options.method.toUpperCase() === 'GET') {
// 发起GET请求
xhr.open(options.method,options.url+ '?' + qs);
xhr.send()
} else if(options.method.toUpperCase() === 'POST') {
// 发起POST请求
xhr.open(options.method,options.url);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send(qs);
}
}

window.sessionStorage

1.生命周期为关闭浏览器窗口
2.在同一窗口(页面)下数据可以共享
3.以键值对的形式存储使用

语法

1
2
3
4
5
6
7
8
9
10
11
12
// 保存数据到 sessionStorage
sessionStorage.setItem('key', 'value');

// 从 sessionStorage 获取数据
let data = sessionStorage.getItem('key');

// 从 sessionStorage 删除保存的数据
sessionStorage.removeItem('key');

// 从 sessionStorage 删除所有保存的数据
sessionStorage.clear();

window.localStorage

1.生命周期永久有效,除非手动删除,否则关闭页面也会存在
2.可以多窗口(页面)共享(同一浏览器可以共享)
3.以键值对的形式存储使用

语法

1
2
3
4
5
6
7
8
9
10
11
12
// 保存数据到 sessionStorage
localStorage.setItem('key', 'value');

// 从 sessionStorage 获取数据
let data = localStorage.getItem('key');

// 从 sessionStorage 删除保存的数据
localStorage.removeItem('key');

// 从 sessionStorage 删除所有保存的数据
localStorage.clear();

for…in

语法:

1
2
for (variable in object)
statement

为什么用 for…in
for…in是为遍历对象属性而构建的,不建议与数组一起使用,数组可以用Array.prototype.forEach()和for…of(),那么for…in到底有什么用呢?

它最常用的地方应该是用于调试,可以更方便的去检查对象属性(通过输出到控制台或其他方式)。尽管对于处理存储数据,数组更实用些,但是你在处理有key-value数据(比如属性用作“键”),需要检查其中的任何键是否为某值的情况时,还是推荐用for … in。

forEach()

语法:
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
参数
callback
为数组中每个元素执行的函数,该函数接收一至三个参数:
currentValue
数组中正在处理的当前元素。
index(可选)
数组中正在处理的当前元素的索引。
array(可选)
forEach() 方法正在操作的数组。
thisArg(可选)
可选参数。当执行回调函数 callback 时,用作 this 的值。

描述:
forEach() 方法按升序为数组中含有效值的每一项执行一次 callback 函数。
可依次向 callback 函数传入三个参数:
1.数组当前项的值
2.数组当前项的索引
3.数组对象本身

navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值。
下面前端代码可以判断用户那个终端打开页面,实现跳转

1
2
3
4
5
6
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
window.location.href = ""; //手机
} else {
window.location.href = ""; //电脑
}

DOM事件流

事件流描述的是从页面中接收事件的顺序
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
DOM事件流分为3个阶段:
1.捕获阶段
2.当前目标阶段
3.冒泡阶段

事件捕获

事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到具体的元素接受的过程

事件冒泡

事件冒泡:IE最早提出,事件开始由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程

注意:
1.JS代码中只能执行捕获或者冒泡其中的一个阶段
2.onclick和attachEvent只能得到冒泡阶段
3.addEventListener(type,listener,[useCapture])第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(不写默认是false),表示在事件冒泡阶段调用事件处理程序
4.实际开发中我们很少使用事件捕获,我们更关注事件冒泡
5.有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave

事件对象

1.event 就是一个事件对象 写到我们侦听函数的
2.事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
3.事件对象是我们事件一系列相关数据的集合 跟事件相关的 比如鼠标点击里面就包含了鼠标的相关信息,鼠标坐标,如果是键盘事件里面就包含了键盘事件的信息 比如 判断用户按下了哪个键

事件对象的常见属性和方法

阻止事件捕获和事件冒泡

有时候我们并不希望事件捕获或事件冒泡,可以用事件对象中的方法:

1
2
e.stopPropagation();
e.stopImmediatePropagation();

阻止事件捕获和冒泡

它们的区别是:如果在其中一个事件监听器中执行 stopImmediatePropagation() ,那么剩下的事件监听器都不会被调用:

例子:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 300px;
background-color: #bfa;
margin: 100px auto;
}

.son {
width: 150px;
height: 150px;
background-color: pink;
}
</style>
</head>

<body>
<div class="father">
<div class="son"></div>
</div>
<script>
var father = document.querySelector('.father');
var son = document.querySelector('.son');
son.addEventListener('click', function (e) {
console.log('我是 son 元素上被绑定的第一个监听函数');
}, true);
son.addEventListener('click', function (e) {
console.log('我是 son 元素上被绑定的第二个监听函数');
}, true);
father.addEventListener('click', function (e) {
console.log('我是 father 元素上被绑定的第一个监听函数');
// 阻止事件捕获和冒泡
e.stopPropagation();
}, true);
father.addEventListener('click', function (e) {
console.log('我是 father 元素上被绑定的第二个监听函数');
}, true);
</script>
</body>
</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
42
43
44
45
46
47
48
49
50
51
52
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 300px;
background-color: #bfa;
margin: 100px auto;
}

.son {
width: 150px;
height: 150px;
background-color: pink;
}
</style>
</head>

<body>
<div class="father">
<div class="son"></div>
</div>
<script>
var father = document.querySelector('.father');
var son = document.querySelector('.son');
son.addEventListener('click', function (e) {
console.log('我是 son 元素上被绑定的第一个监听函数');
}, true);
son.addEventListener('click', function (e) {
console.log('我是 son 元素上被绑定的第二个监听函数');
}, true);
father.addEventListener('click', function (e) {
console.log('我是 father 元素上被绑定的第一个监听函数');
// 阻止事件捕获和冒泡
// e.stopPropagation();
e.stopImmediatePropagation();
}, true);
father.addEventListener('click', function (e) {
console.log('我是 father 元素上被绑定的第二个监听函数');
}, true);
</script>
</body>
</html>

父子节点

获取父节点

node.parentNode

获取子节点

childNodes 得到所有的子节点 包含 元素节点 文本节点等等
1.node.childNodes (不常用)
2.node.children

获取第一个子节点和最后一个子节点

获取第一个子节点:
firstChid 得到第一个子节点 包含 元素节点 文本节点等等
1.node.firstChild (不常用)
2.node.firstElementChild (IE9以上支持)
3.node.children[0]

获取最后一个子节点:
lastChid 得到最后一个子节点 包含 元素节点 文本节点等等
1.node.lastChild (不常用)
2.node.lastElementChild (IE9以上支持)
3.node.children[node.children.length-1]

兄弟节点

获取下一个兄弟节点

nextSibling 得到下一个兄弟节点 包含 元素节点 文本节点等等
1.node.nextSibling (不常用)
2.node.nextElementSibling (IE9以上支持)

获取上一个兄弟节点

previousSibling 得到上一个兄弟节点 包含 元素节点 文本节点等等
1.node.previousSibling (不常用)
2.node.previousElementSibling (IE9以上支持)

创建节点

document.createElement(‘tagname’)

添加节点

appendChild 将一个节点添加指定父元素的子节点列表末尾,类似数组中的push
insertBefore 将一个节点添加指定父元素的子节点列表指定位置
1.node.appendChild(child)
2.node.insertBefore(child,指定元素)

删除节点

node.removeChild(child)

复制节点

node.cloneNode()
注意:如果括号参数为空或者为false,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点

indexOf()数组去重

核心算法:遍历旧数组,然后拿着旧数组元素去查询新数组,如果该元素在新数组没有出现过,就添加,否则不添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function unique(arr) {
if(arr instanceof Array) {
let newArr = [];
for(let i =0;i<arr.length;i++) {
if(newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i])
}
}
return console.log(newArr);
} else {
console.log('error');
}
};
unique([1,5,7,11,5,7,55,88,6,7,5,11]);

Set()数组去重

简述:Set对象是值的集合,你可以按照插入的顺序迭代它的元素。Set中的元素只会出现一次,即Set中的元素是唯一的

1
2
3
4
5
6
7
8
9
function unique(arr) {
if(arr instanceof Array) {
let newArr = [...new Set(arr)];
return console.log(newArr);
} else {
console.log('error');
}
};
unique([1,5,7,11,5,7,55,88,6,7,5,11]);