// 1.利用对象字面量创建对象 {}
// var obj = {}; // 创建了一个空的对象
var obj = {
uname: "张三疯",
age: 18,
sex: "男",
sayHi: function () {
console.log("hi~");
},
};
// 方法冒号后面跟的是一个匿名函数
// 2. 使用对象
// (1). 调用对象的属性 我们采取 对象名.属性名 . 我们理解为 的
console.log(obj.uname);
// (2). 调用属性还有一种方法 对象名['属性名']
console.log(obj["age"]);
// (3) 调用对象的方法 sayHi 对象名.方法名() 千万别忘记添加小括号
obj.sayHi();
// 修改:对象名.属性名=新的值
// 添加: 对象名.新属性名=值/方法
// 2 利用 new Object 创建对象 了解
var obj = new Object(); // 创建了一个空的对象
obj.uname = '张三疯';
obj.age = 18;
obj.sex = '男';
obj.sayHi = function() {
console.log('hi~');
}
// (1) 我们是利用 等号 = 赋值的方法 添加对象的属性和方法
// (2) 每个属性和方法之间用 分号结束
console.log(obj.uname);
console.log(obj['sex']);
obj.sayHi();
// 3,利用new 构造函数创建对象;
function Star(uname, age, sex) {
this.name = uname;
this.age = age;
this.sex = sex;
this.sing = function(sang) {
console.log(sang);
}
}
var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象
ldh.speak = (language)=>{console.log('English')} //新增对象的方法
// console.log(typeof ldh);
console.log(ldh.name);
console.log(ldh['sex']);
ldh.sing('冰雨');
var zxy = new Star('张学友', 19, '男');
console.log(zxy.name);
console.log(zxy.age);
zxy.sing('李香兰')
// 1. 构造函数名字首字母要大写
// 2. 我们构造函数不需要 return 就可以返回结果
// 3. 我们调用构造函数 必须使用 new
// 4. 我们只要new Star() 调用函数就创建一个对象 ldh {}
// 5. 我们的属性和方法前面必须添加 this
在构造函数创建对象的过程中,new起关键作用,new主要做了如下事情:
1 创建空对象并让this指向该空对象
2 执行构造函数,给上述空对象增加属性
3 返回已经增加了属性的对象
任何函数都可以是构造函数,函数是不是构造函数由new决定
对象的遍历
// js对象通过 for...in...结构遍历
var obj = {name:'jim',age:20,sex:'男'}
for(var k in obj){
console.log(k) //会输出3次 name age sex
console.log(obj[k]) // jim 20 男 //k 是变量,必须要用 obj[k] 访问属性值
}
// in可判断对象中是否存在某个属性 如:
'name' in obj // true
'sayHi' in obj // false
if('sayHi' in obj){
console.log(obj['sayHi'])
}
js内置对象:
数学对象:
属性、方法名 | 功能 |
---|---|
Math.PI | 圆周率 |
Math.floor() | 向下取整 |
Math.ceil() | 向上取整 |
Math.round() | 四舍五入版 就近取整 注意 -3.5 结果是 -3 |
Math.abs() | 绝对值 |
Math.max()/Math.min() | 求最大和最小值 |
Math.random() | 获取范围在[0,1)内的随机值 |
//封装 生成min到max之间的随机整数
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
日期对象
Date 对象和 Math 对象不一样,Date是一个构造函数,所以使用时需要实例化后才能使用其中具体方法和属性。
// 创建日期对象使用 需要 new Date()
// 1 不传参数得到当前时刻的日期对象
var now = new Date()
// 2 传两个或两个以上参数 表示 年 月 日 ...对应的日期时间对象
var someDay = new Date(2021,3)
var someDay = new Date(2021,3,10)
// 3 传一个日期格式化字符串,表示对应日期/时刻 的日期对象
var anotherDay = new Date('2021/3/20 10:10:10')
// 4 传一个数字型参数,此时该参数被认为是时间戳
var date = new Date(2021) //此处得到的日期对象对应1970/1/1 0:0:2(格林威治时间)
getDay() 获取星期几 0-6 0表示周日
getMonth() 获取月份 0-11 0表示1月
getTime() 获取时间戳 距离格林威治时间1970/1/1 0:0:0 的毫秒数
获取时间戳(某个时刻距离基准时刻的总毫秒数)
// 实例化Date对象
var now = new Date();
// 1. 用于获取对象的原始值
console.log(date.valueOf())
console.log(date.getTime())
// 2. 简单写可以这么做
var now = +new Date();
// 3. HTML5中提供的方法,有兼容性问题
var now = Date.now();
利用时间戳实现倒计时
function addZero(num) {
return num < 10 ? '0' + num : num;
}
function countDown(futureTime) {
// 计算目标时刻和当前时刻 的 时间差(间隔的秒数)
var now = +new Date();
var future = +new Date(futureTime);
var time = (future - now) / 1000;
// 将间隔的秒数 换算成 天 时 分 秒
var day = parseInt(time / 60 / 60 / 24);
var hour = addZero(parseInt((time / 60 / 60) % 24));
var min = addZero(parseInt((time / 60) % 60));
var sec = addZero(parseInt(time % 60));
// 将天时分秒 拼接字符串 返回
return day + '天' + hour + '时' + min + '分' + sec + '秒';
}
console.log(countDown('2021-3-3 19:40:00')); // 传参加 ''
数组对象
如何判断某个数据是否为数组?
有两种方法 ① 数据 instanceof Array 得到true则说明是数组 ② Array.isArray(数据) 得到true则说明是数组.
Function instanceof Object
数组增加元素?数组中删除元素?
方法名 | 作用 | 参数/返回值 | 备注 |
---|---|---|---|
push() | 后面追加元素 | 一个或多个数据 返回 最新数组长度 | 改变原数组 |
unshift() | 从前面追加元素 | 一个或多个数据 返回 最新数组长度 | 改变原数组 |
pop() | 删除最后一个元素 | 无参数 返回被删除的元素 | 同上 |
shift() | 删除第一个元素 | 无参数 返回被删除的元素 | 同上 |
reverse() | 翻转数组 | 无 返回反转后的新数组 | 同上 |
sort() | 数组排序 | 无参数时字典排序 function(a,b){return a-b}升序排序,return b-a 降序排序 | 同上 |
indexOf() | 查找元素 | 参数1:要查找的值 参数2:开始查找的索引 返回索引或-1 | 不改变 |
join(‘分隔符’) | 将数组元素拼接成字符串 | 参数:分隔符,无参数时默认用逗号连接, 返回一个字符串 | 不改变 |
toString() | 数组转字符串 | 逗号分隔每一项,返回一个字符串 | |
splice() | 删除、添加元素 | splice(开始删除的位置的索引号,删除个数),返回剩余旧元素组成的数组 | 改变 |
slice() | 截取数组中的一段 | slice(begin,end),索引号start到索引号end,end取不到,返回截取的项目组成的新数组 | 不改变 |
concat() | 拼接两个数组或往数组中追加元素 | 连接两个或多个数组, 不影响原数组,返回新数组 | 不改变 |
字符串对象
重要结论:字符串对象方法均不会改变字符串本身------字符串的不可变性
方法名 | 作用 | 参数/返回值 | 备注 |
---|---|---|---|
indexOf/lastIndexOf() | 返回字符对应的索引 | 参数 字符 返回值:索引 找不到返回-1 | |
charAt( ) | 返回索引对应的字符 | 参数 索引 返回值:对应的字符 | str[索引]也能得到 |
charCodeAt( ) | 返回索引对应的字符的ASCII码 | 参数 索引 返回值:对应的字符的ASCii码 | |
substr( start, length) | 字符串截取 | 参数 起始位置 截取长度 返回截取的字符串 | |
substring(start, end) | 字符串截取 | 同slice 但是不接受负值 | |
slice(start, end) | 字符串截取 | 索引号start到索引号end,end取不到 | |
replace( ) | 字符串替换 | 参数:要替换的旧字符串 新字符串 返回替换后的新字符串 | 默认只替换一次 |
split(’ ') | 字符串转数组 | 参数:分隔符 返回 数组 | |
join(’ ') | 数组转字符串 | arr.join(‘’) 返回字符串 ’ ‘ 中间设置分隔符 |
**图片边框:**border-image
<style>
.panel {
border: 15px solid ;
border-image-width: 51px 38px 20px 132px;
border-image-source: url(./images/border.png);
border-image-slice: 51 38 20 132;
}
.box {
width: 800px;
height: 600px;
}
/* border 影响内容边距 */
/* border-image-width 不影响内容边距 */
/* 调用类 panel 自带边框 */
</style>
<body>
<div class="box panel"></div>
</body>
API获取元素注意点:
getElementsByTagName(‘标签名’) 、getElementsByClassName(‘类名’) 获取过来的都是伪数组或者空数组[ ];
特殊元素获取:document.body //返回body元素;
document.documentElement //返回html元素;
事件源(谁):触发事件的元素;
onfocus-----onblur 获得/失去鼠标焦点时触发;
元素对象.className = ‘类名’ ; 会覆盖元素原先的类; 多类名书写
全选全不选:
input(/this) . checked; 获得表单元素当前选中状态 true/false;
input(/this) . checked = true/false; 设置表单元素当前状态;
button??
获取属性: element.属性 ; element.getAttribute(‘属性’) ; 程序员自己添加的属性称为自定义属性 data-开头
设置属性: element.属性 = ‘值’ ; element.setAttribute(‘属性’, ‘值’); 主要针对于自定义属性
移除属性:removeAttribute(‘属性’)
元素设置索引:元素也是一个对象 element . index = i
节点:
parentNode.children 获取子元素节点集合,使用需要配合 [‘索引’]使用parentNode.children[0];
添加节点 ul.appendChild(’ li ') ul 父级 li 是子级 后面追加元素节点;
添加节点 ul.insertBefore( 新元素li , ul.children[0]); 指定子元素前面添加元素节点;
node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容;
创建元素的三种方式:
<script>
// 三种创建元素方式区别
// 1. document.write() 创建元素 如果页面文档流加载完毕,再调用这句话会导致页面重绘
var btn = document.querySelector('button');
btn.onclick = function() {
document.write('<div>123</div>');
}
// 2.1 innerHTML 字符串拼接创建元素 效率最低
var inner = document.querySelector('.inner');
for (var i = 0; i <= 100; i++) {
inner.innerHTML += '<a href="#">百度</a>'
}
// 2.2 innerHTML 拼接数组创建元素 效率最高
var arr = [];
for (var i = 0; i <= 100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join('');
// 3. document.createElement() 创建元素 效率一般
var create = document.querySelector('.create');
for (var i = 0; i <= 100; i++) {
var a = document.createElement('a');
create.appendChild(a);
}
</script>
var d1/d2 = +new Date(); //测算运行到此处的时间d2-d1
事件注册:
// 1. 传统方式注册事件
// 同一元素,同一事件,后面覆盖前面,前面不执行
btns[0].onclick = function() {
alert('hi');
}
btns[0].onclick = function() {
alert('hao a u');
}
// 2. 事件侦听注册事件 addEventListener
// (1) 里面的事件类型是字符串 必定加引号 而且不带on
// (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
// (3)多个事件处理函数依次执行
btns[1].addEventListener('click', function() {
alert(22);
})
btns[1].addEventListener('click', function() {
alert(33);
})
// document.addEventListener("mousemove", myFunction);
// document.removeEventListener("mousemove", myFunction); 移出事件
DOM事件流:
捕获–>目标阶段–>冒泡
有些事件没有冒泡:onblur onfocus onmouseenter onmouseleave等
事件对象:
- this 是事件绑定的元素(绑定这个事件处理函数的元素) 。
- e.target 是事件触发的元素对象。
- 通常情况下terget 和 this是一致的,但有一种情况不同,那就是在事件冒泡时(事件委托)。
e.preventDefault(); 阻止默认行为
e.stopPropagation(); 阻止冒泡
鼠标事件对象:
e.clientX / e.clientY 鼠标在浏览器窗口可视区的x和y坐标
e.pageX / e.pageY 鼠标在页面文档的x和y坐标
e.screenX / e.screenY 鼠标在电脑屏幕的x和y坐标
键盘事件:
keydown—> keypress—> keyup 涉及字母大小写用keypress, 不涉及字母大小或者功能键写用keydown/up
e.keyCode === 13 判断用户按下确认键
页面(窗口)加载事件(2种):
window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件;
DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。
调整窗口大小事件:
window.onresize 是调整窗口大小加载事件, 当触发时就调用处理函数---->响应式布局。
window.innerWidth 当前屏幕的宽度
清除定时器:
timer = setTimeout(function(){} , 延迟的毫秒数) — > clearTimeout(timer);
timer = setInterval(function(){},间隔的毫秒数 ----> clearInterval(timer);
window对象的location属性:
location.href = ‘http://www.itcast.cn’; 跳转页面
location.reload(true); 刷新
location.assign(‘http://www.itcast.cn’); 跳转页面(重定向页面) 记录历史,可实现后退
location.replace(‘http://www.itcast.cn’); 替换当前页面 不记录历史
同步异步–事件循环:
元素偏移量 offset 系列:
offset
-
offset 可以得到任意样式表中的样式值
-
offset 系列获得的数值是没有单位的
-
offsetWidth 包含padding+border+width 传统盒模型
-
offsetWidth 等属性是只读属性,只能获取不能赋值
-
所以,我们想要获取元素大小位置,用offset更合适
style
-
style 只能得到行内样式表中的样式值
-
style.width 获得的是带有单位的字符串
-
style.width 获得不包含padding和border 的值
-
style.width 是可读写属性,可以获取也可以赋值
-
所以,我们想要给元素更改值,则需要用style改变
元素可视区 client 系列:
元素滚动 scroll 系列:
页面滚动事件 scroll
document.addEventListener('scroll', function() {
// window.pageYOffset 页面被卷去的头部 })
三大系列区别:
1.offset系列 经常用于获得元素位置 offsetLeft offsetTop
2.client经常用于获取元素大小 clientWidth clientHeight
3.scroll 经常用于获取滚动距离 scrollTop scrollLeft
4.注意 页面 滚动的距离通过 window.pageXOffset 获得
动画函数:
function animate(obj, target, callback) {
// console.log(callback); callback = function() {} 调用的时候 callback()
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
// 步长值写到定时器的里面
// 把我们步长值改为整数 不要出现小数的问题
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
// 回调函数写到定时器结束里面
// if (callback) {
// // 调用函数
// callback();
// }
callback && callback();
}
// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
// 当我们点击了返回顶部模块,就让窗口滚动的页面的最上方
goBack.addEventListener('click', function() {
// 里面的x和y 不跟单位的 直接写数字即可
// window.scroll(0, 0);
// 因为是窗口滚动 所以对象是window
animate(window, 0);
});
网页端轮播图:
window.addEventListener('load', function () {
//1,获取元素
var focus = document.querySelector('.focus');
var last = focus.querySelector('.arrow-l');
var next = focus.querySelector('.arrow-r');
var items = focus.querySelectorAll('ul>li');
var ul = focus.querySelector('ul');
var ol = focus.querySelector('ol');
//轮播图宽度
var w = focus.offsetWidth;
//当前轮播图索引号给ol小圆圈
var index = 0;
//当前小圆点索引
var circle = 0;
//2,显示与隐藏
focus.addEventListener('mouseenter', function () {
last.style.display = 'block';
next.style.display = 'block';
clearInterval(timer);
timer = null; // 清除定时器变量
});
focus.addEventListener('mouseleave', function () {
last.style.display = 'none';
next.style.display = 'none';
timer = setInterval(function() {
//手动调用点击事件
next.click();
}, 1000);
})
//3,动态生成ol小圆点:
for (var i = 0; i < items.length; i++) {
var li = document.createElement('li');
// 记录当前小圆圈的索引号 通过自定义属性来做
li.setAttribute('index', i);
ol.appendChild(li);
//4,小圆点高亮切换:
li.onclick = function(){
focus.querySelector('.current').className = '';
this.className = 'current';
//4,小圆点播放对应的轮播图 目标位置:索引号*轮播图宽度
circle = index = this.getAttribute('index');
// index = this.getAttribute('index');
animate(ul, - w * index);
}
}
//页面刷新后,手动点击小圆点之前,默认第一个选中
ol.children[0].className = 'current';
//5,实现无缝连播,在ul的最后面追加第一个li的克隆
var temp = items[0].cloneNode(true);
ul.appendChild(temp);
//6,注册点击右箭头播放下一张事件
next.addEventListener('click', function () {
index++;
//当已经播放到了克隆的照片时,直接快速切换到真的第一张,不用animate函数
if (index > items.length) {
ul.style.left = 0;
index = 1;
}
animate(ul, - w * index);
// 小圆点跟随切换
circle++;
if (circle > items.length - 1) {
circle = 0;
}
// console.log(index,circle);
// 取消当前已有的高亮,并为索引号为cricle的小圆点加上高亮
focus.querySelector('.current').className = '';
// if (index == 4) {
// ol.children[0].className = 'current';
// } else {
// ol.children[index].className = 'current';
// }
ol.children[circle].className = 'current';
})
//6,注册点击左箭头播放下一张事件
last.addEventListener('click', function () {
//当已经播放到了克隆的照片时,直接快速切换到真的第一张,不用animate函数
if (index == 0) {
ul.style.left = - 4 * w +'px';
index = 4;
}
index--;
animate(ul, - w * index);
// 小圆点跟随切换
circle--;
if (circle < 0 ) {
circle = 3;
}
// 取消当前已有的高亮,并为索引号为cricle的小圆点加上高亮
focus.querySelector('.current').className = '';
ol.children[circle].className = 'current';
})
// 7,鼠标不在轮播图区域时自动播放,鼠标移入轮播图停止自动播放
var timer = setInterval(function() {
//手动调用点击事件
next.click();
}, 2000);
})
移动端拖动元素:
-
touchstart、touchmove、touchend可以实现拖动元素
-
但是拖动元素需要当前手指的坐标值 我们可以使用 targetTouches[0] 里面的pageX 和 pageY
-
移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离
-
手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置
拖动元素三步曲:
(1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置
(2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子
(3) 离开手指 touchend:
注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 e.preventDefault();
H5新增classList 属性:
添加类:
element.classList.add(’类名’);
移除类:
element.classList.remove(’类名’);
切换类:
element.classList.toggle(’类名’);
注意:以上方法里面,所有类名都不带点
删除元素本身:
element.remove() --> // remove()方法可以直接删除指定的元素
jQuery:
筛选选择器:
$(li:first) $(‘li:last’) $(‘li:eq(2)’) $(‘li:odd’)奇数 $(‘li:even’)偶数
$(‘li’).parents( )查找所有父级 $(‘li’).parent( )查找亲父级
JavaScript高级:
原型对象—对象原型:
// 1. 构造函数的问题.
function Star(uname, age) {
this.uname = uname;
this.age = age;
// this.sing = function() {
// console.log('我会唱歌');
// }
}
Star.prototype.sing = function () {
console.log("我会唱歌");
};
var ldh = new Star("刘德华", 18);
var zxy = new Star("张学友", 19);
console.log(ldh.sing === zxy.sing);
// 2. 一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象prototype身上
// 4,JS对象可分为两大类:普通对象和函数对象 ,所有对象都有__proto__(对象原型)指向构造函数的 prototype 原型对象 --> 构成原型链的属性 终点是null;
// 所有对象均有原型对象,只有函数对象才有prototype原型对象;
// 5,函数的 prototype对象 等价于 实例对象的 __proto__;
// 6, Star.prototype.constructor === ldh.__proto__.constructor -->都指回构造函数Star
所有函数(Object Array Data…)都可视为Function的实例,Function是他自己的实例:
Function.__proto__ == Function.prototype // true
数据 instanceof 构造函数 对象类型判断
通过原型为数组扩展内置方法:Array.prototype.sum = function() { }
ES5构造函数复合继承:
function Animal(weight,age){
this.weight = weight;
this.age = age;
}
Animal.prototype.breathe = function(){
console.log('空气真好');
}
function Dog(weight,age,name,type){
Animal.call(this,weight,age); //子构造函数继承父构造函数的属性用 父构造函数.call(this);
this.name = name;
this.type = type;
}
Dog.prototype = new Animal(); //★
// Dog.prototype.constructor = Dog;
Dog.prototype.bark = function(){
console.log('wangwangwang!');
}
var d1 = new Dog(10,3,'小黄','金毛');
console.log(d1);
d1.breathe();
d1.bark();
ES5新增数组方法:
arr . forEach(function(e , i , arr本身){ }) 遍历 无返回值
arr . filter(function(e , i , arr){ return value >= 20; }) 过滤 / 查找 返回新数组,不影响原数组 遍历完每一个e
var arr = [12, 66, 4, 88, 3, 7, 12, 66, 58, 79];
var newArr1 = arr.filter(function(e,i){
return arr.indexOf(e) == i
// 数组去重 arr.indexOf(e)返回 e 对应的索引号 / -1
})
arr . some(function(e , i , arr){ return value >= 20; }) 查找 返回布尔值 true / false 查找到就终止循环不再继续
arr . every (function(e , i , arr){ return value >= 20; }) 查找 返回布尔值 true / false 全部符合条件才是true 反之false 与some相对
arr.reduce()方法
语法:arr.reduce( Callback , [initialValue可选] )
Callback–>回调函数
接受4个参数(previousValue,currentValue,index,array)
previousValue:上一次reduce调用回调返回的值, reduce第一次执行回调时为初始值(initialValue)
currentValue:数组中当前被处理的元素, index:当前被处理元素在数组中的索引
array:调用reduce方法的数组, initialValue(初始值)
如果提供了初始值[initialValue可选] -->reduce()方法会从索引为0的元素开始执行,返回值初始值为initialValue
如果没有提供初始值reduce()方法会将索引为0的元素当做初始值,从索引为1的元素开始执行
区别:
- 如果查询数组中唯一的元素, 用some方法更合适, 在some 里面 遇到 return true 就是终止遍历 迭代效率更高
- 在forEach 里面 return 不会终止迭代
获取对象的属性名:
Object.keys(对象) 获取到当前对象中的属性名 K ,返回值是一个数组
trim ( ) 方法去除字符串两端的空格
修改对象的属性名或者属性值:
Object.defineProperty(对象,修改或新增的属性名,{
value:修改或新增的属性的值,
writable:true/false,//如果值为false 不允许修改这个属性值
enumerable: false,//enumerable 如果值为false 则不允许遍历
configurable: false //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性
})
ES6 中新增加了类的概念:
class 关键字,类抽象了对象的公共部分,它泛指某一大类(class),对象特指某一个,通过类实例化一个具体的对象。
类的共有属性放到 constructor 里面,可以接受传递过来的参数,同时返回实例对象,constructor是构造器或者构造函数;只要 new 生成实例时,就会自动调用这个函数。
class Son extends Father{ // 这样子类就继承了父类的属性和方法;
constructor(x, y) {
super(x, y); //使用super调用了父类中的构造函数
this.x = x;
this.y = y;
}
}
//子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用父类的构造函数,super 必须在子类this之前调用
// 手动调用我们的点击事件 不需要鼠标触发
this.lis[index] && this.lis[index].click();
//双击事件 ondblclick
that.lis[index].remove(); --> // remove()方法可以直接删除指定的元素
类里面的共有的属性和方法一定要加this使用 ;
在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
函数内部的this指向:
普通函数调用:函数名() this指向window;
构造函数调用:new 函数名() this指向实例对象;
对象方法调用:对象名. 方法名() this指向该方法所属对象;
事件绑定方法:this指向绑定事件对象;
定时器函数: this指向window;
立即执行函数: this指向window;
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
// console.log(this); obj
return function() {
// console.log(this); window
return this.name;
};
}
};
console.log(object.getNameFunc()())
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
// console.log(this); obj
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()())
改变函数内部this指向:
fn.call(obj,1,2)//此时的this指向的是对象obj,调用fn函数,参数使用逗号隔开
fn.apply(obj,[1,2])//此时的this指向的是对象obj,调用fn函数,参数使用 [数组] 传递
var f = fn.bind(obj, 1, 2); //此处的f是bind返回的新函数
f();//调用新函数 this指向的是对象obj 参数使用逗号隔开
- call 经常做继承.
- apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
- bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.
- call( ) 只有函数对象才能调用,参数一是修改后的this指向,参数2,参数3…使用逗号隔开连接;作用:调用函数 修改this指向
call()、apply()、bind()方法的相同点及区别?
相同点:
- 都是只能被函数调用
- 都能改变this指向
区别:
-
call和apply会直接调用原函数,但是bind只会返回原函数的副本(改变this指向的)
-
call和bind对原函数参数的传递都是作为第2,3,…n个参数写在call及bind方法的参数列表中,而apply对原函数参数的传递是需要包在一个数组中
function fn(a,b){return a+b} fn.call(window,1,2) //3 fn.apply(window,[1,2]) //3 Math.max(1,2,3,45)//45 var x = [4,5,6,7,1] Math.max.apply(Math,x)// 7 fn.call(1,2)//NaN 第1个参数必须是this的新指向 function test(){console.log(this)} var o = {a:1,b:2} test.bind(o,1,2)() //{a:1,b:2} test.call(o) //{a:1,b:2} test.apply(o) //{a:1,b:2} call apply bind典型应用场景 call/apply 构造函数继承,将对象a中的方法应用于对象b bind 绑定函数中的this且不能马上调用函数 给函数传实参,但不马上调用函数
严格模式中的变化:
作用:
让js程序更严谨更安全更高效
定义方式:
<script>
'use strict'
</script>
<script>
function test(){
'use strict'
}
</script>
注意:只能出现在函数及js脚本的开头
-
变量相关:
-
不能未声明就直接对某个变量赋值
-
不能删除已声明的变量
-
-
this 相关:
-
全局函数中的this不再默认指向window而指向undefined,因为该原因构造函数不能没有new直接使用
-
定时器回调函数、事件处理函数、对象方法中的this仍遵循谁调用指向谁原则
-
-
函数相关:
-
禁止使用重名形参
-
不能在非函数体的{}中 如 if 结构的{}中定义函数但在{}外调用
-
高阶函数:
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
闭包:
闭包(closure)指 有权访问 另一个函数作用域中变量 的函数。简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。 作用:延伸变量的作用范围。
闭包的缺陷:
会导致局部变量资源不被释放,相对而言,更占用资源
闭包的应用:
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也成为小闭包 因为立即执行函数里面的任何一个函数都可以使用它的i这变量
(function(i) {
lis[i].onclick = function() {
console.log(i);
}
})(i);
}
//等同于 let 声明变量i 让每个i有自己独立作用域
for (let i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
console.log(i);
};
}
//3秒钟之后,打印所有li元素的内容
for (var i = 0; i < lis.length; i++) {
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(i);
}
递归:
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。即函数内部自己调用自己,使用递归时一定要有退出条件。
// 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
function f(n) {
if (n === 1 || n === 2) {
return 1;
}
return f(n - 1) + f(n - 2);
}
console.log(f(6)); //8
递归应用–>对象浅拷贝&深拷贝:
//1, 浅拷贝 只拷贝对象的最外层(最外层简单数据类型更新相互独立),深层级别(复杂数据类型)只拷贝地址,拷贝完后新数据和原数据仍可能会互相干扰
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
}
};
var o = {};
//方法:Object.assign(target,...source)方法可实现浅拷贝
Object.assign(o, obj);
console.log(o);
o.msg.age = 20;
console.log(obj);
//2, 深拷贝
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18,
study: {
grade: 10,
score: 100,
},
},
color: ['pink', 'red'],
};
var o = {};
// 封装函数 ---> 利用for...in...遍历对象,instanceof 判断复杂数据类型,再利用递归一层一层遍历下去
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
// 判断我们的属性值属于那种数据类型,数组/对象/简单 判断顺序不能变
// 1. 获取属性值 oldobj[k]
var item = oldobj[k];
// 2. 判断这个值是否是数组,是数组就在新对象里面对应位置生成一个空[ ]
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item);
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象, 是对象就在新对象里面对应位置生成一个空{ }
newobj[k] = {};
deepCopy(newobj[k], item);
} else {
// 4. 属于简单数据类型
newobj[k] = item;
}
}
}
// deepCopy(o, obj);
//4, 深拷贝的另一种方式 JSON
o = JSON.parse(JSON.stringify(obj));
console.log(o);
正则表达式:
正则表达式也是对象---->表单验证,文本筛选,检索,替换。。。
//调用RegExp对象的构造函数创建
var regexp = new RegExp(keyword,'g'); 参数可以是一个变量,用于检索目标不确定的情形 'i'忽略大小写 'g'全局
//利用字面量创建
var rg = /123/;
正则替换replace替换文本
div.innerHTML = text.value.replace(/激情|gay/g, '**');
// replace第二个参数可以是一个回调函数
div.innerHTML = text.value.replace(/激情|gay|mm/g, function(e){
console.log(this); //window
console.log(e); //原字符串被替换的文本
return e.toUpperCase(); //变成大写
});
// 正则查找★★:
var keyReg = new RegExp(keyword, 'g'); // 相当于 正则 /关键字/g 关键字 为变量
......replace(keyReg,`<b style="color:blue">${keyword}</b>`)
// 修改元素、对象内容★:
1 var str = document.querySelector('p').innerText;
str = ''; //会修改p的内容么? 不会
2 document.querySelector('p').innerHTML = 'xxx' // 可以修改
3 var p = document.querySelector('p');
p.innerHTML = 'xxx' // 可以修改
4 var o = { name: 'zhangsan', age: 30 };
var n = o.name;
n = 'lisi'; // 不会修改对象 O
o.name = 'lisi'; // 会修改对象 O
ES6语法:
let关键字
let声明的变量只在所处于的块级有效,使用let/const关键字声明的变量才具有块级作用域;
不存在变量提升;
暂时性死区:利用let声明的变量会绑定在这个块级作用域,不会受外界的影响;
const关键字
声明常量,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值。
具有块级作用域;声明常量时必须赋值;
常量赋值后,值不能修改
const PI = 3.14;
PI = 100; // Assignment to constant variable.
const ary = [100, 200];
ary[0] = 'a';
ary[1] = 'b';
console.log(ary); // ['a', 'b'];
ary = ['a', 'b']; // Assignment to constant variable.
解构赋值(★★★)
ES6中允许从数组/对象中提取值,按照对应位置,对变量赋值。
解构赋值就是把数据结构分解,然后给变量进行赋值
//数组解构
let [a, b, c] = [1, 2, 3, 4, 8]; //a=1 b=2 c=3
//对象解构
let person = { name: 'zhangsan', age: 20 };
let { name, age } = person; //name=zhangsan age=20
let {name: myName, age: myAge} = person;//myName=zhangsan myAge=20
let {age: myAge} = person; //myAge=20
// 实例:获取到响应数据的父子级
const { data, data: { data: userinfo } } = await getUserProfile()
变量需要跟属性值对应,即变量要小于等于元素/属性个数,对象解构还需要属性名一致;
方便的去取对象中的属性跟方法
箭头函数(★★★)
函数体中只有一句代码,且代码的执行结果就是返回值(return省略),可以省略大括号
如果形参只有一个,可以省略小括号
const sum = (num1, num2) => num1 + num2;
const fn = v => v;
★★★箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
可以简单理解成,定义箭头函数所在的作用域中this指向谁,箭头函数就指向谁
箭头函数的优点:在于解决了this执行环境所造成的一些问题,如setTimeout和setInterval中使用this所造成的问题
var age = 100;
const obj = {
age: 20,
say: () => {
console.log(this.age);
console.log(age)
}
}
obj.say();//100 //箭头函数this指向的是被声明的作用域里面,而对象没有作用域的,所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域
剩余参数(★★)
剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这种方式很方便的去声明不知道参数情况下的一个函数
function sum (first, ...args) {
console.log(first); // 10
console.log(args); // [20, 30]
}
sum(10, 20, 30)
-------------------------------------------------
let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students;
console.log(s1); // 'wangwu'
console.log(s2); // ['zhangsan', 'lisi']
Array 的扩展方法
1,扩展运算符(展开语法):
将数组或者对象转为用逗号分隔的参数序列
let ary = [1, 2, 3];
...ary // 1, 2, 3
console.log(...ary); // 1,2,3,相当于下面的代码
console.log(1,2,3);
-------------------------------------
//扩展运算符可以应用于合并数组
// 方法一
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];
// 方法二
ary1.push(...ary2);
-------------------------------------
//将类数组或可遍历对象转换为真正的数组
let oDivs = document.getElementsByTagName('div');
oDivs = [...oDivs];
2,构造函数方法:Array.from() :
将伪数组或可遍历对象转换为真正的数组
//定义一个集合
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
//转成数组
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
------------------------------------------------------------
//方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理
let arrayLike = {
"0": 1,
"1": 2,
"length": 2
}
let newAry = Array.from(arrayLike, item => item *2)//[2,4]
注意:如果是对象,那么属性需要写对应的索引
3,实例方法:find()
用于找出“第一个”符合条件的数组成员,如果没有找到返回undefined
let ary = [{
id: 1,
name: '张三'
}, {
id: 2,
name: '李四'
}];
let target = ary.find((item, index) => item.id == 2);//找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个
console.log(target); // {id: 2, name: '李四'}
4,实例方法:findIndex()
用于找出“第一个”符合条件的数组成员的位置,如果没有找到返回-1
let ary = [1, 5, 10, 15, 15];
let index = ary.findIndex((value, index) => value > 10);
// 相当于let index = ary.indexOf(15);
console.log(index); //3
console.log(ary[index]); //15
5,实例方法:includes()
判断某个数组是否包含给定的值,返回布尔值。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
6,实例方法:map()
它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。map()不会对空数组进行检测,不影响原数组,返回一个新数组。
array.map(function(currentValue, [index], [arr]), [thisIndex])
thisValue:可选。对象作为该执行回调时使用,传递给函数,用作"this"的值。
String 的扩展方法
1,模板字符串:
模板字符串中可以解析变量${变量},
可以换行,
可以调用函数${sayHello()}
2,实例方法:startsWith() 和 endsWith()
let str = 'Hello world!';
str.startsWith('Hello') // true
str.endsWith('!') // true
3,实例方法:repeat()
string.repeat(n) 方法表示将原字符串重复n次,返回一个新字符串
Set 数据结构(数组去重★★)
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set本身是一个构造函数,用来生成 Set 数据结构.
const set = new Set([1, 2, 3, 4, 4]); // {1, 2, 3, 4}
const newArry = [...set] // [1, 2, 3, 4] 数组去重
arr.filter(function(e,i){
return arr.indexOf(e) == i
// 数组去重 arr.indexOf(e)返回 e 对应的索引号 / -1
// 数组去重 还可以.findIndex(e,i) 返回索引或者-1 ; .includes(e)返回true/false
})
let arr = [true, 5, 0, false, 6, "", 7, undefined, null, undefined, 1];
console.log(arr.filter((e) => e)); // [true, 5, 6, 7, 1] 数组去空
1,实例方法:
.add(value) ; .delete(value) ; .has(value) ; .clear() ; .forEach() …
Ajax:
基本概念:
URL统一资源定位器(Universal Resource Locator)用于标记网络上某个特定文件位置的特定格式的一个字符串
ajax全称: asynchronous javascript and xml 异步的javacript和xml
ajax其实也是一种请求,通过js代码实现,且发生该请求时页面不会发生整体刷新
ajax缺陷: 无法利用浏览器后退功能;对搜索引擎不友好
实现ajax请求,核心在于浏览器内置的XMLHttpRequest对象
方法名 | 作用 | 注意事项 |
---|---|---|
$.get(url,[data],callback) | 发起get请求 | |
$.post(url,[data],callback) | 发起post请求 | |
$.ajax({url:xxx,type:xxx,data:xxx,success:function(){}}) | 发起请求 | 最灵活,参数也最复杂 |
$(选择器).load(url,callback) |
// 注意:每次调用 $.get() 或 $.post() 或 $.ajax() 的之前,
// 会先调用 ajaxPrefilter 这个函数
// 在这个函数中,可以拿到我们给Ajax提供的配置对象
$.ajaxPrefilter(function(options) {
// 在发起真正的 Ajax 请求之前,统一拼接请求的根路径
options.url = 'http://ajax.frontend.itheima.net' + options.url
})
接口:所谓接口其实就是一个url地址,通过向该地址发起请求,可以得到相应数据
表单的同步提交:
通过点击 submit 按钮,触发表单提交的操作,从而使页面跳转到 action URL 的行为,叫做表单的同步提交。
<form action="/login" target="_blank" method='post' enctype='multipart/form-data'>
<!-- 表单提交也是一种请求 -->
<!-- action注意:当提交表单后,页面会立即跳转(刷新)到 action 属性指定的 URL 地址 -->
//enctype 的值默认为 application/x-www-form-urlencoded;在涉及到文件上传的操作时,必须将 enctype //的值设置为 multipart/form-data
// 表单同步提交的缺点:
① 页面会发生跳转
② 页面之前的状态和数据会丢失
//onsubmit(e) 监听表单的提交事件,e 为表单提交的数据对象 .reset() 重置表单
表单.serialize() 方法是 Jquery 中快速获取表单数据的方法,获得的是编码字符串 username=zs&pswd=1111
模板引擎art-template:
-
下载art-template模板引擎文件(template-web.js)并引入到html中
-
在html中准备 ,且在模板中嵌入各种数据
-
调用template(id,data),生成包含数据的html字符串 data对象
-
将html字符串加入dom中
// {{属性名}} 显示属性值不解析标签 // {{@ 属性名}} 显示属性值解析标签 ---------------------------------------------------- {{regTime | dateFormat | test1}} //过滤器 | 管道符 ---------------------------------------------------- // {{if 表达式1}} xxxx 表达式1为true时显示 // {{else if 表达式2}} yyyy 表达式2为true时显示 ---------------------------------------------------- {{each hobby}} <li>索引是:{{$index}},循环项是:{{$value}}</li> {{/each}}
模板字符串原理:
var data = {name: '张三'} var str = '<div>我是{{name}}</div>' var pattern = /{{([a-zA-Z]+)}}/ var patternResult = pattern.exec(str) // ['{{name}}', 'name', index: 7, input: '<div>我是{{name}}</div>', groups: undefined] str = str.replace(patternResult[0], data[patternResult[1]]) // console.log(str) --> <div>我是张三</div>
xhr对象的基本使用:
//get方式 var xhr = new XMLHttpRequest(); xhr.open('get','http://www.test.cn/api?id=1') xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readystate==4 && xhr.status==200){ console.log(xhr.responseText) } } //get请求携带参数的本质:无论使用 $.ajax(),还是使用 $.get(),又或者直接使用 xhr 对象发起 GET 请求,当需要携带参数的时候,本质上,都是直接将参数以查询字符串的形式,追加到 URL 地址的后面,发送到服务器的。 //post方式 var xhr = new XMLHttpRequest(); xhr.open('post','http://www.test.cn/api?id=1') xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded') xhr.send('name=zhagnsan&age=20'); //查询字符串 xhr.onreadystatechange = function(){ if(xhr.readystate==4 && xhr.status==200){ console.log(xhr.responseText) } }
查询字符串及url编码:
-
url中?后面的部份被称为查询字符串
-
如
http://www.baidu.com?search=c&lang=ch
中search=c&lang=ch
即属于查询字符串,为url携带的参数,多个参数之间用&分割,get请求发送数据时通过查询字符串发送 -
url中不允许包含非英文字符,浏览器会自动对url进行 ulr编码
-
url编码 API
encodeURI(str)
url解码 APIdecodeURI(str)
var str = "中午我回去吃饭"; var str2 = encodeURI(str); //%E4%B8%AD%E5%8D%88%E6%88%91%E5%9B%9E%E5%8...
-
JSON字符串:
JSON -> JavaScript Object Notation,即’javascript对象表示法’。
- 简单来讲就是’javascript对象和数组’的’字符串表示法’。JSON的本质就是’字符串’。
JSON 中 数据的类型可以是数字、字符串、布尔值、null、数组、对象6种类型。
JSON.parse(jsonStr); // json反序列化 json字符串转对象/数组
JSON.stringify(obj2); // json序列化 对象/数组转json字符串
*undefined 或函数不能转化成json字符串,,自动忽略不转化
*JSON数据不能写注释,使用"",不允许''
*最外层必须为[]或{}。
JSON 的作用:在计算机与网络之间存储和传输数据。
// 1,数据对象 转 查询字符串 遍历对象 -> push数组 -> .join('&')转查询字符串
function getQueryStr(obj) {
var arr = [];
for (var k in obj) {
arr.push(k + "=" + obj[k]);
}
return arr.join("&");
}
xhr对象新特性:
-
设置xhr请求时限 xhr.timeout xhr.ontimeout = function(){//请求超时时触发…}
-
使用FormData管理表单数据 var fd = new FormData(form)
-
FormData数据不需要url编码*
-
使用FormData数据发送POST请求,不需要设置Content-Type,不要画蛇添足
-
支持文件读取及上传 fd.append(‘myfile’,document.querySelecotor(‘input[type=“file”]’).files[0])
-
支持数据传输进度的获取 xhr.upload.onprogress = function(e){console.log(e.lengthComputable,e.loaded,e.total)} //了解
-
var files = document.querySelector(“#file1”).files; files[0]
-
文件上传 文本框文件列表对象,伪数据
Axios的基本使用:
document.querySelector("#btn1").addEventListener("click", function () {
var url = "http://www.liulongbin.top:3006/api/get";
var dataObj = { name: "张三", age: 20 };
axios.get(url, { params: dataObj }).then(function (res) {
console.log(res.data);
});
});
document.querySelector("#btn2").addEventListener("click", function () {
var url = "http://www.liulongbin.top:3006/api/post";
var dataObj = { address: "四川", location: "成都市" };
axios.post(url, dataObj).then(function (res) {
console.log(res.data);
});
});
document.querySelector("#btn3").addEventListener("click", function () {
var url = "http://www.liulongbin.top:3006/api/get";
var paramsData = { name: "钢铁侠", age: 35 };
axios({
method: "GET",
url: url,
params: paramsData,
}).then(function (res) {
console.log(res.data);
});
});
document.querySelector("#btn4").addEventListener("click", function () {
var url = "http://www.liulongbin.top:3006/api/post";
var paramsData = { name: "娃哈哈", age: 18, gender: "女" };
axios({
method: "POST",
url: url,
data: paramsData,
}).then(function (res) {
console.log(res.data);
});
});
document.querySelector("#file").onclick = function () {
var files = document.querySelector("#test").files;
if (files.length > 0) {
// var formData = new FormData();
// formData.append("avatar", files[0]);
var formData = { avatar: files[0] };
axios
.post("http://www.liulongbin.top:3006/api/post", formData)
.then(function (res) {
console.log(res);
});
} else {
alert("请选择文件后再上传!");
}
};
同源策略/跨域:
- 什么是同源?同协议 同域名 同端口
- 什么是同源策略? 浏览器安全策略之一 指js代码默认只能和同源网页发生资源交互(cookie访问、dom访问、ajax请求等)
- 什么是跨域?两个非同源的页面之间发生资源交互即存在跨域,跨域页面之间默认无法正常进行ajax请求(请求能发送,目标页面也能接收,但响应数据会被浏览器拦截且报错)
跨域解决方案:
- 方案1:JSONP 浏览器端js实现,但需服务端配合,只支持get请求
- 方案2:CORS 服务端实现(node.js php java等)支持get/post请求,是w3c的标准解决方案
JSONP原理:
是通过
前端:
<script>
// jsonp原理: 动态的创建script标签, 添加src属性 在前段页面中声明一个函数, 从服务器端返回的是函数调用
function show(data) {
console.log(data); // { name: 'zs', age: 22 }
}
let spt = document.createElement('script')
script.src = 'http://127.0.0.1/api/test?callback=show'
document.body.appendChild(spt)
setTimeout(function () {
document.body.removeChild(spt)
}, 1000)
</script>
------------------------------------------------------------
后端:
app.get('/api/test', (req, res) => {
// 1. 得到函数的名称
const funcName = req.query.callback
// 2. 定义要发送到前端的数据对象
const data = { name: 'zs', age: 22 }
// 3. 拼接出一个函数的调用
const str = `${funcName}(${JSON.stringify(data)})`
// 4. 把拼接的字符串,响应给前端一个函数调用 show({ name: 'zs', age: 22 })
res.send(str)
})
// 原理:执行ajax方法时若dataType为'jsonp'则会动态创建script并append到head中,将其src赋值为配置项中url,url后面会追加参数callback=随机函数名 ,随机函数名对应函数即为success函数,接收到接口响应的 随机函数名(数据) 后 执行函数,操作数据; 创建的script完成任务后被动态删除。
防抖:
防抖策略(debounce)是当事件被触发后,延迟 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。有效减 少请求次数,节约请求资源。
//输入框防抖:
var timer = null // 1. 防抖动的 timer
function debounceSearch(keywords) { // 2. 定义防抖的函数
timer = setTimeout(function() {
// 发起 JSONP 请求
getSuggestList(keywords)
}, 500)
}
$('#ipt').on('keyup', function() { // 3. 在触发 keyup 事件时,立即清空 timer
clearTimeout(timer)
// ...省略其他代码
debounceSearch(keywords)
})
节流:
节流策略(throttle),顾名思义,可以减少一段时间内事件的触发频率。
① 鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;
② 懒加载时要监听计算滚动条的位置,但不必每次滑动都触发,可以降低计算的频率,而不必去浪费 CPU 资源;
节流阀为空,表示可以执行下次操作;不为空,表示不能执行下次操作。
//跟随鼠标移动的天使
$(function() {
var angel = $('#angel')
var timer = null // 1.预定义一个 timer 节流阀
$(document).on('mousemove', function(e) {
if (timer) { return } // 3.判断节流阀是否为空,如果不为空,则证明距离上次执行间隔不足16毫秒
timer = setTimeout(function() {
$(angel).css('left', e.pageX + 'px').css('top', e.pageY + 'px')
timer = null // 2.当设置了鼠标跟随效果后,清空 timer 节流阀,方便下次开启延时器
}, 16)
})
})
总结防抖和节流的区别 / 防抖:
如果事件被频繁触发,防抖能保证只有最有一次触发生效!前面 N 多次的触发都会被忽略! 节流:如果事件被频繁触发,节流能够减少事件触发的频率,因此,节流是有选择性地执行一部分事件!
HTTP协议:
HTTP 请求消息:
1,由请求行(request line)、请求头部( header ) 、空行 和 请求体 4 个部分组成。
a,请求行 请求行由请求方式、URL 和 HTTP 协议版本 3 个部分组成,他们之间使用空格隔开。
b,请求头部 用来描述客户端的基本信息,从而把客户端相关的信息告知服务器。
请求头部由多行 键/值对 组成,每行的键和值之间用英文的冒号分隔。
比如:User-Agent 用来说明当前是什么类型的浏览器; Content-Type 用来描述发送到服务器的数据格式;Accept 用来描述客户端能够接收什么类型的返回内容;
c,空行 最后一个请求头字段的后面是一个空行,通知服务器请求头部至此结束。 请求消息中的空行,用来分隔请求头部与请求体。
d,请求体 中存放的,是要通过 POST 方式提交到服务器的数据。
注意:只有 POST 请求才有请求体,GET 请求没有请求体!
HTTP响应消息:
HTTP响应消息由状态行、响应头部、空行 和 响应体 4 个部分组成。
a,状态行 由 HTTP 协议版本、状态码和状态码的描述文本 3 个部分组成,他们之间使用空格隔开;
分类 | 分类描述 |
---|---|
1** | 信息,服务器收到请求,需要请求者继续执行操作(实际开发中很少遇到 1** 类型的状态码) |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
b,响应头部 用来描述服务器的基本信息。响应头部由多行 键/值对 组成,每行的键和值之间用英文的冒号分隔。
c,空行 在最后一个响应头部字段结束之后,会紧跟一个空行,用来通知客户端响应头部至此结束。 响应消息中的空行,用来分隔响应头部与响应体。
d,响应体 中存放的,是服务器响应给客户端的资源内容。
浏览器地址栏输入URL,按下回车后发生了什么?
在地址栏输入URL后
- 输入URL,按回车
- dns解析(浏览器是不能识别url地址的,需解析成ip地址),
- 建立tcp连接,(tcp三次握手)
- 发送Http请求,
- 服务器进行处理并返回Http报文,
- 浏览器渲染页面,
- 断开连接(tcp四次挥手)
1,URL(Uniform Resource Locator),统一资源定位符,用于定位互联网上资源,俗称网址。
域名解析 (DNS解析url对应的ip)
2,域名解析:DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。DNS 是一个网络服务器,我们的域名解析简单来说就是在 DNS 上记录一条信息记录。
在浏览器中输入URL后,首先要进行DNS解析,DNS解析的顺序为:
1、浏览器缓存
2、本地hosts文件
3、系统缓存
4、路由器缓存
5、DNS服务器迭代查询
分析:域名解析实际上是将域名解析为ip地址的过程。
首先,浏览器会检查本地hosts文件中是否有此url的映射关系,如果有就调用这个ip地址映射,完成域名解析。
如果没有,则会查找本地DNS解析器缓存,如果查找到则返回。
如果还是没有,则会查找本地DNS服务器,如果查找到则返回。
最后迭代查询,按根域服务器 ->顶级域(.com)->第二层域(yeahfei.com) ->子域(admin.yeahfei.com)的顺序找到IP地址。
3,tcp三次握手
1.目的:同步客户端和服务端的序列号和确认号,并交换TCP窗口大小信息。防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
通过DNS得到目标的IP地址后,通过TCP协议向服务器发送请求即三次握手。seq初始序号,客户端是x,服务端是y,
分析:通过第3阶段已经拿到了服务器的ip地址,在获取到IP地址后,便会开始建立一次连接,这是由TCP协议完成的,主要通过三次握手进行连接。
第一次握手: 建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;
第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成
4,发送Http请求
建立连接:服务器允许客户端建立连接
接受请求:从网络中读取HTTP报文交给Nginx或者Apache进行规则匹配
处理请求:根据方法,资源,首部和可选的主体部分对请求进行处理
访问资源:寻找存储对象,访问报文中指定的资源
构建响应报文:创建有正确首部的HTTP响应报文
5,服务器处理请求并返回HTTP报文
6,解析渲染页面
浏览器得到页面后会进行展示,如果还包含其他外部资源如图片、视频等等则继续请求其他资源。
分析:如果说响应的内容是HTML文档的话,就需要浏览器进行解析渲染呈现给用户。整个过程涉及两个方面:解析和渲染。在渲染页面之前,需要构建DOM树和CSSOM树。
在浏览器还没接收到完整的 HTML 文件时,它就开始渲染页面了,在遇到外部链入的脚本标签或样式标签或图片时,会再次发送 HTTP 请求重复上述的步骤。在收到 CSS 文件后会对已经渲染的页面重新渲染,加入它们应有的样式,图片文件加载完立刻显示在相的应位置。在这一过程中可能会触发页面的重绘或重排。
1.概念:
重绘:某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的重绘。
回流:某个元素的尺寸发生了变化,则需重新计算渲染树,重新渲染。
2.步骤:
根据 HTML 解析出 DOM 树
根据 CSS 解析生成 CSS 规则树
结合 DOM 树和 CSS 规则树,生成渲染树
根据渲染树计算每一个节点的信息
7,断开连接
数据传送完,需断开tcp连接,此时发起tcp四次挥手。
————————————————
原文链接:https://blog.csdn.net/super__code/article/details/103919092
node js:
Node.js 中根据模块来源的不同,将模块分为了 3 大类:内置模块,自定义模块,第三方模块。
1,require() 方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。
模块作用域:和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块 作用域。防止变量污染。
2,向外共享模块作用域中的成员:
在每个 .js 自定义模块中都有一个 module 对象,它里面存储了和当前模块有关的信息。
在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,作为对外的接口供外界使用。 外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象。
使用 require() 方法导入模块时,导入的结果,永远以 module.exports 指向的对象为准。
由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了 exports 对象。默认情况 下,exports 和 module.exports 指向同一个对象。最终共享的结果,还是以 module.exports 指向的对象为准。
加载某个模块,其实是加载该模块的 module.exports 属性。require() 方法用于加载模块。
内置模块:
//fs 模块
const fs = require('fs')
//读文件
fs.readFile("路径", "utf8", function (err, dataStr) { })
//写文件
fs.writeFile('./files/1.txt', str, function(err) { })
//path模块
const path = require('path')
// __dirname 表示当前文件所处的目录
// 注意: ../ 会抵消前面的路径 ./ 当前目录下
const pathStr = path.join('/a', '/b/c', '../../', './d', 'e')
console.log(pathStr) // \a\d\e
fs.readFile(path.join(__dirname, './files/1.txt'), 'utf8', function(err, dataStr) { })
---------------------------
const fpath = '/a/b/c/index.html'
const fullName = path.basename(fpath) // index.html
const nameWithoutExt = path.basename(fpath, '.html') // index
const fext = path.extname(fpath) // .html
//http模块 创建服务器基本步骤:
// 1. 导入 http 模块
const http = require('http')
// 2. 创建 web 服务器实例
const server = http.createServer()
// 3. 为服务器实例绑定 request 事件,监听客户端的请求
server.on('request', function (req, res) {
console.log('Someone visit our web server.') //有人访问服务器时打印
})
// 4. 启动服务器
server.listen(8080, function () {
console.log('server running at http://127.0.0.1:8080') //启动成功打印
})
// req.url 是客户端请求的 URL 地址
// req.method 是客户端请求的 method 类型
// 调用 res.end() 方法,向客户端响应一些内容
自定义模块:
用户创建的每个 .js 文件,都是自定义模块。
使用 require() 加载自定义模块时,必须指定以 ./ 或 …/ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 …/ 这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。
第三方模块:
Node.js 中的第三方模块又叫做包。
npm平台
初次装包完成后,在项目文件夹下多一个叫做 node_modules 的文件夹和 package-lock.json 的配置文件。
node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包。 注意:今后在项目开发中,一定要把 node_modules 文件夹,添加到 .gitignore 忽略文件中。
package-lock.json配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。
package.json npm 规定,在项目根目录中,必须提供一个叫做 package.json 的包管理配置文件。用来记录与项目有关的一些配置 信息。命令–> npm init -y (只能在英文的目录下成功运行!)
当我们拿到一个剔除了 node_modules 的项目之后,运行 npm install 命令(或 npm i)可以一次性安装所有的package.json包管理工具中的依赖包。
删除包 : npm uninstall+包名 ,会把卸载的包,自动从 package.json 的 dependencies 中移除掉。
包分类:全局包
项目包 —> 被安装到项目的 node_modules 目录中的包,分为 开发依赖包 和 核心依赖包:
—> 开发依赖包(被记录到 devDependencies 节点中的包,只在开发期间会用到)
—> 核心依赖包(被记录到 dependencies 节点中的包,在开发期间和项目上线之后都会用到)
Express
Express作为Web 开发框架第三方包,作用和 Node.js 内置的 http 模块类似,是专门用来创建 Web 服务器的。
req.params / req.query
// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()
// 4. 监听客户端的 GET 和 POST 请求,并向客户端响应具体的内容
app.get('/user', (req, res) => {
// 调用 express 提供的 res.send() 方法,向客户端响应一个 JSON 对象
res.send({ name: 'zs', age: 20, gender: '男' })
})
app.post('/user', (req, res) => {
// 调用 express 提供的 res.send() 方法,向客户端响应一个 文本字符串
res.send('请求成功')
})
app.get('/', (req, res) => {
// 通过 req.query 可以获取到客户端通过查询字符串的形式,发送到服务器的参数:
// 注意:默认情况下,req.query 是一个空对象
console.log(req.query)
res.send(req.query)
})
// 注意:这里的 :id 和 :username是一个动态的参数
app.get('/user/:id/:username', (req, res) => {
// 通过 req.params 对象,可以访问到 URL 中,通过 : 匹配到的动态参数:
console.log(req.params)
res.send(req.params)
})
// 3. 启动 web 服务器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
托管静态资源:
app.use(express.static('files')) // 'files' === './files'
app.use('/files', express.static('./files'))//挂载路径前缀,通过带有 /public 前缀地址来访问
Express 路由:
客户端的请求 与 服务器处理函数 之间的 映射关系。
Express 中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数。如下:
app.METHOD ( PATH, HANDLER )
路由匹配的注意点:
① 按照定义的先后顺序进行匹配
② 请求类型和请求的URL同时匹配成功, 才会调用对应的处理函数
const express = require('express')
const app = express()
// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块
app.use(router)
// 2. 为路由模块添加统一的访问前缀/api **
// app.use("/api", router);
// 注意: app.use() 函数的作用,就是来注册全局中间件
app.listen(80, () => {
console.log('http://127.0.0.1')
})
---------------------------------------------------
// 这是路由模块 另一个JS文件
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()
// 3. 挂载具体的路由
router.get('/user/list', (req, res) => {
res.send('this is list')
})
router.post('/user/add', (req, res) => {
res.send('this is add')
})
// 4. 向外导出路由对象
module.exports = router
Express 中间件
中间件 本质上就是一个 function (req,res,next) { } 处理函数
注意:
① 一定要在路由之前注册中间件
② 客户端发送过来的请求,可以连续调用多个中间件进行处理
③ 中间件函数的形参列表中,必须包含 next 参数,而路由处理函数中只包含 req 和 res
④ 为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码
⑤ 连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象
局部生效的中间件/全局生效的中间件
使用const定义的中间件,只在当前路由中生效的中间,叫做局部生效的中间件。
通过调用 app.use(中间件函数),客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义局部中间件函数
const mw1 = (req, res, next) => {
console.log('调用了局部生效的中间件')
next()
}
// 定义全局中间件函数
app.use((req, res, next) => {
console.log("调用了全局中间件");
next();
});
// 2. 创建路由
app.get('/', mw1, (req, res) => { //调用了局部中间件+全局中间件
res.send('Home page.')
})
app.get('/user', (req, res) => { //只调用了全局中间件
res.send('User page.')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
应用级别的中间件:
通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件。
路由级别的中间件:
绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。
错误级别的中间件:
专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。
注意:错误级别的中间件, 必须注册在所有路由之后!
Express内置的中间件:
① express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)
② express.json 解析 JSON 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)
③ express.urlencoded 解析 URL-encoded 格式的请求体数据
第三方的中间件:
安装–>require导入—>调用app.use()注册使用
数据库:
作用:保存数据;
组织结构:库(database) 一般一个网站对应一个库
->表(table)
->行(row)
->列(column)
安装phpstudy集成包 启动 MySQL
安装navicat -->文件–>新建连接–>MySQL–>端口 localhost / 127.0.0.1 密码 root
数据类型:
int :整数 char 内存大小不可变的字符串 varchar 内存大小可变的字符串 …
数据属性:
AI 自动增长 pk 主键 uq 值唯一 nn 不能为空
SQL语句:
对数据库里的数据进行 增删改查:
关键字大小写不敏感)
1,查 select语句 --> 查询所有列和所有行的数据 --> select * from 表名称
--> 查询指定字段 --> select 列名称,列名称… from 表名称
2,增 insert into语句 --> 增加数据 --> insert into 表名 (字段名1,字段名2…) values (‘字段1的值’,‘字段1的值’…)
a,新增完整的一条数据时,字段省略,字段的值对应完整的表的字段,自增数据用null代替
insert into users values (null,‘tom’,‘20’,‘0’)
b,根据字段名插入数据 ----> insert into users (username,age) values (‘jerry’,‘2’)
c,一次插入多条数据 ----> insert into users (username,age,status) values (‘je’,‘2’,‘1’),(‘rry’,‘2’,‘1’)
3,改 update语句 --> 更新数据 --> update users(表名) set age = 100,status=2 where id=2 **where切记
4,删 delete语句 --> 删除数据 --> delete from users(表名) where id=2 **其余字段数据的id不受影响
delete from users(表名) 删除整个表单数据,之前id继续占用不清零
truncate users 清空整个表单,之前id清零,新增数据的自增属性(id)从0开始
where子句:
select * from users where id>3&&status=0
select * from users where id<=3||status=1
select * from users where username<>‘tom’ username 不等于 tom 的所有用户
order by子句:数据排序
select * from users order by status === select * from users order by status asc 升序排序
select * from users order by id desc 降序排序
高级查询:多重条件
select * from users order by status asc,id desc 多重条件排序用逗号链接条件
select * from users where status=1 order by id desc 查找+排序用空格链接条件
limit: 限定查询条数
select * from users limit 3,2 从第3条数据开始(不包括第三条),查询2个
-->应用分页
数学统计:
– 总和 count(*) – 最大值 max(age) – 最小值 min(age) – 平均值 avg(age)
select count(*) from users
– 查询字段重命名 as
select username as name from users
node.js操作数据库…
前后端身份认证:
1,两种开发模式:
服务端渲染 推荐使用Session认证机制
前后端分离渲染 推荐JWT认证机制
2,HTTP协议的无状态性:
浏览器的每次HTTP请求都是独立的,连续多个请求之间没有之间的关系,浏览器没有记忆功能,不会主动保留每次HTTP请求的状态。突破 HTTP 无状态的限制:Cookie Session token
3,Cookie认证:
现实生活中的"会员卡"身份认证方式,在web开发中的专业术语叫做 Cookie.
存储在用户浏览器中的一般不超过4KB的字符串,它由一个名称(Name)、一个值(Value)和其它几个用 于控制 Cookie 有效期、安全性、使用范围的可选属性组成。
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器(通过添加请求头Cookie的方式),请求成功,浏览器响应会添加一个响应头Set-Cookie。
Cookie的几大特性: ① 自动发送 ② 域名独立 ③ 过期时限 ④ 4KB 限制
客户端存储的是明文,浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造。
4,Session认证:
基于Cookie,“会员卡 + 刷卡认证”的设计理念,就是 Session 认证机制的精髓。
5,Express 中使用 Session 认证:
安装 / 配置 / app.use()注册 express-session 全局中间件
Session 认证机制需要配合 Cookie 才能实现。由于 Cookie 默认不支持跨域访问,所以,当涉及到前端跨域请求后端接 口的时候,需要做很多额外的配置,才能实现跨域 Session 认证。
6, JWT认证:
JWT(英文全称:JSON Web Token)是目前最流行的跨域认证解决方案。 前端,后端为两个以上服务器。
总结:用户的信息通过 Token 字符串的形式,保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用户的身份。
JWT 的三个组成部分,从前到后分别是 Header、Payload、Signature。
⚫ Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。
⚫ Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。
ES6模块化规范:
什么是ES6模块化规范?
node.js 遵循了 CommonJS 的模块化规范。其中:
⚫ 导入其它模块使用 require() 方法
⚫ 模块对外共享成员使用 module.exports 对象
AMD 和 CMD 适用于浏览器端的 Javascript 模块化 (前端)
CommonJS 适用于服务器端的 Javascript 模块化 (后端)
基于node.js使用ES6模块化语法,需要在package.json 的根节点中添加 “type”: “module” 节点
—> 大一统的 ES6 模块化规范
ES6 模块化规范中定义:
- 每个 js 文件都是一个独立的模块
- 导入其它模块成员使用 import 关键字
- 向外共享模块成员使用 export 关键字
默认导出/导入:
默认导出的语法: export default 默认导出的成员 —》向外共享模块中的成员
默认导入的语法: import 接收变量 from ‘模块标识符/路径’ —》接收其他 JS 模块中的成员
-
接收变量–>同声明变量命名规则 为一个对象
-
每个模块中,只允许使用唯一的一次 export default,否则会报错!
-
模块标识符/路径–> 可以是内置模块,第三方模块,自定义模块(必须以 ./ 开头)
按需导出/导入:
按需导出的语法: export 按需导出的成员
按需导入的语法: import { s1,s2 } from ‘模块标识符’
按需导出与按需导入的注意事项 :
① 每个模块中可以使用多次按需导出
② 按需导入的 成员名称s1 必须和按需 导出的名称s1 保持一致
③ 按需导入时,可以使用 as 关键字进行重命名,旧名称无法继续使用
④ 按需导入 可以和 默认导入 一起使用 默认导入+按需导入
import * as 接收变量 from ‘模块标识符/路径’
—> *** 全部**,必须配合as重命名一个接收变量
直接导入并执行模块中的代码:
只想单纯地执行某个模块中的代码(如css),并不需要得到模块中向外共享的成员–>
直接导入, improt ‘模块标识符/路径’ 即可,无需导出