day1
js简介
-
js:javascript
-
js 是一种直译型的脚本语言 script 脚本
-
js 组成部分 ECMAScript + BOM(browser object model) + DOM(document object model)
-
js 历史 为了解决表单验证问题
-
js 前端页面效果 + 后端进行交互!!!!!
2+2+2
js创建
-
内部js
<script> // 内部js </script>
-
外部引入js
<script src="../js/test.js"></script>
-
错误写法
<!-- 错误写法 --> <!-- <script src="../js/"> // 此处不能写js代码 </script> -->
js输出
弹窗
alert("hello");
文档输出
document.write("hello");
可解析标签
document.write("<h1>LIGHT UP THE SKY</h1>");
控制台
用于调试代码
console.log("hello")
变量
- 变量:经常会改变的值
- 常量:不会改变的值
声明变量
声明变量就需要var 关键字
var x ;
var y ;
var a ;
var hello ;
标识符
就是变量、函数、属性或函数参数的名称。
- 第一个字符必须是字母、下划线_或 美元符号
- 剩下其他字符可以是字母、下划线、美元符号或数字
驼峰命名
-
小驼峰命名法
// studentname studentName
-
大驼峰命名法
// studentname StudentName
变量的赋值
-
赋值
var beiZi ; beiZi = '水' ;
-
声明变量并赋值
var studentName = '李肖' , beiZi = '水' ;
变量的数据类型
- 字符串
- 数字
- 布尔值 true false
- undefined 未定义(变量声明了但是没有赋值)
- null 空对象
typeof
typeof 判断数据类型
- 字符串 string
- 数字 number
- 布尔值 boolean
- null object
- undefind undefined
var a = "black";
console.log(typeof a); //string
var b = 3;
console.log(typeof b); //number
var c = true;
console.log(typeof c); //boolean
var d = null;
console.log(typeof d); //object
var e;
console.log(typeof e); //undefined
算数运算符
-
-
-
- /
- %
var count = 3 ;
var price = 9 ;
console.log(count * price) ;
console.log(count + price) ;
console.log(count - price) ;
console.log(count / price) ;
// 余数
console.log(7 % 3) ; //1
比较运算符
- == 判断值是否相等
- === 判断值和数据类型都必须相等 并且 两个条件必须同时满足
- >
- <
- >=
- <=
var a = 3 ;
var b = 3 ;
console.log(a >= b) ; // 大于或者等于 true
console.log(a <= b) ; // 小于或者等于 true
var m = 5 ;
var n = '5' ;
// ==判断只要值是一样就可以
console.log(m == n) ; //true
// ===值和数据类型必须都一样 严格判断
console.log(m === n) ; //false
逻辑运算符
- 与 && 条件必须同时满足
- 或 || 条件只需要满足一个
- 非 ! 取反
短路
-
与运算
只要见到条件为假就直接结束 见假即为假
-
或运算
只要见到条件为真就直接结束 见真即为真
var a = 13;
var b = 4;
var c = 50;
console.log(a > b && b > c); //false
console.log(a < b || b < c); //true
//短路与
console.log(a < b && b < c);//false
//短路或
console.log(a > b || b > c);//false
console.log(!(a > b)); //false
自增自减
-
a++ 先执行表达式,再自增
-
++a 先自增,在执行表达式
-
a-- 先执行表达式,再自减
-
–a 先自减,在执行表达式
var b = 1;
console.log(b++); //1
console.log(++b); //3
var c = 1;
console.log(++c); //2
console.log(c++ + 1); //3
console.log(c++ + ++c); //3+5 = 8
赋值运算符
var a = 17 ;
// a = (a + 2) ;
a = a + 2 ;
console.log(a) ;
// 简写
a += 2 ;
console.log(a) ;
a -=2 ;
console.log(a) ;
a *= 2 ;
console.log(a) ;
a %= 2 ; // a = a % 2 ;
console.log(a) ;
输入框求和案例
- document.getElementById()
<input type="text" id="inp1">
<span>-</span>
<input type="text" id="inp2">
<button id="btn">=</button>
<input type="text" id="result">
<script>
//1.找到按钮
var oButton = document.getElementById("btn");
//2.点击事件
oButton.onclick = function () {
//3.拿到第一个输入框
var oInput1 = document.getElementById("inp1");
console.log(oInput1);
//4.拿到第一个输入框的值
var num1 = oInput1.value;
var oInput2 = document.getElementById("inp2");
console.log(oInput2);
var num2 = oInput2.value;
//测试是否拿到了输入框的值
console.log(num1, num2);
//计算结果
var res = num1 - num2;
console.log(res);
//把结果放到最后一个输入框
var outPut3 = document.getElementById("result");
outPut3.value = res;
};
</script>
作业
// 02 var k=0; var sum = k++ + ++k +k +k++; console.log(sum)
// 03 入职薪水10K,每年涨幅入职薪水的5%,50年后工资多少?
// 04 小明要到美国旅游,可是那里的温度是以华氏度为单位记录的,它需要一个程序将华氏温度(80度)转换为摄氏度,并以华氏度和摄氏度为单位分别显示该温度。(摄氏度与华氏度的转换公式为:摄氏度 = 5/9.0*(华氏度-32)保留3位小数)
// 05 如何交换两个变量的值
// 06 实现加减乘除
// 07 给定一个四位数,分别把这个数字的千位、百位、十位、个位算出来并显示
day02
字符串拼接
+作用
- 求和
- 字符串拼接 多个字符串或者多个变量拼接时都需要使用+
<script>
var a = 'abc';
var b = 'def';
console.log(a + b);
document.write(a + b);
</script>
精度丢失
由于计算机是二进制的,所以有精度缺失问题
var a = 0.1;
var b = 0.2;
console.log(a + b); // 0.30000000000000004
console.log(0.1 + 0.2 == 0.3); //false
console.log(9999999999999999 == 10000000000000001);//true
console.log(0.1 + 0.7); //0.799999
解决办法
将数都扩大成整数
//解决办法
console.log((a * 10 + b * 10) / 10); //0.3
关于Number
数字除了普通数字还具有
- Infinity 无穷大
- NaN (Not a Number) 非法数字
var a = 1 / 0;
console.log(a); //Infinity
var b = "bbb";
console.log(b - 0); //NaN
数据类型转换
强制转换
String()
其他数据类型转换成字符串
var a = 5;
console.log(String(a)); //'5'
var b = true;
console.log(String(b)); //'true'
var c = false;
console.log(String(c)); //'false'
var d = null;
console.log(String(null)); //'null'
var e = undefined;
console.log(String(undefined)); //undefined
var a = Infinity;
console.log(String(a)); //Infinity
var b = 'a' - 0;
console.log(String(b)); //NaN
Boolean()
其他数据类型转布尔值
- 数字除了 0 和NaN ,其他都是true
- 字符串除了空串,其他都是true
- null false
- undefined
//number
var a = 12;
console.log(Boolean(a)); //true
var b = 0;
console.log(Boolean(b)); //false
//string
var c = "acd";
console.log(Boolean(c)); //true
var d = " "; //空格字符
console.log(Boolean(d)); //true
var e = ""; //空字符
console.log(Boolean(e)); //false
//null
var f = null;
console.log(Boolean(e)); //false
//undefined
var g = undefined;
console.log(Boolean(g)); //false
var k = Infinity;
console.log(Boolean(Infinity)); //true
var h = NaN;
console.log(Boolean(h)); //false
Number()
其他数据类型转数字
- 字符串
- 纯数字字符串 直接变为数字
- 非纯数字字符串, NaN
- 布尔值
- true 1
- false 0
- undefined NaN 0
- null 0
//string
var a = "31";
console.log(Number(a)); //31
var b = "2b";
console.log(Number(b)); //NaN
var c = " ";//空格字符串
console.log(Number(c)); //0
var d = ""; //空字符
console.log(Number(d)); //0
//boolean
var e = true;
console.log(Number(e)); //1
var f = false;
console.log(Number(f)); //0
//undefined
console.log(Number(undefined)); //NaN
//null
console.log(Number(null)); //0
隐式转换
算数运算符
通过算数运算符,将类型进行隐式转换
- -,*,/,% 自动转换为Number 数字
-
- 碰到字符串就自动转字符串
- 没有字符串就转成数字
var a = '3';
console.log(a * 1); //3
console.log(a - 0); //3
console.log(a / 1); //3
//碰到字符串,字符串拼接
console.log(a + 44); //'344'
比较运算符
-
有数字时,就转数字进行比较
-
字符串与字符串比较的是ACSII值
-
NaN与任何值不相等 不比较 ,NaN是一个集合
-
undefined==null
-
null ==0 -->false
-
因为a 转为数字时NaN,不与任何值比较
console.log('a' > 3); //FALSE
console.log('a' < 3); //FALSE
console.log('a' == 3); //FALSE
console.log("33" > 4); //true
console.log(true < 1); //false
console.log(false < 1); //true
console.log(true < Infinity); //true
console.log(null < 4); //true
- undefined转为数字为NaN
console.log(undefined < 0);
console.log(undefined == 0);
console.log(undefined > 0);
字符串与字符串比较
console.log("hello" > "hi"); //false
console.log(null == undefined); //true
-
null与数字0比较不转换成字符串
console.log(null == 0); //false
console.log(null > 1); //false console.log(null < 1); //true
isNaN
isNaN()函数判断是否为NaN
可用来判断是否为合法数字
var a = "33";
var b = "2b";
console.log(isNaN(Number(a))); //false
console.log(isNaN(Number(b))); //true
取数字
parseInt
parseFlot
var m = 2; //int 整数
var n = 2.4; //float 浮点数
var fontSize = '20.56.78px 78';
console.log(parseInt(fontSize)); //20
console.log(parseFloat(fontSize)); //20.56
数字的处理
- Number()
- isNaN()
- toFiexed() —保留几位小数
- parseInt()
- parseFlot()
toFixed
保留几位小数
var a = 3.14999;
console.log(a.toFixed(3)); //3.150
Math对象
- abs()绝对值
- round()四舍五入取整
- ceil()向上取整
- floor() 向下取整
- random 产生0-1之间随机数 (包含0 不包含1)
产生某一区间的数字
max
min
- parseInt(Math.random()*(max-min+1))+min
- Math.round(Math.random()*(max-min))+min
- Math.ceil(Math.random()*(max-min)) + min
- Math.floor( Math.random() * (max-min + 1) ) + min
//100以内的随机整数 0-100
var a = Math.random();
a *= 100;
a = Math.round(a);
console.log(a);
//产生一个三位数
//100-999
//100+(0-899)
var a = Math.random() * 900;
a = parseInt(a);
a += 100;
console.log(a);
//bug取不到999
//产生某一区间的随机数
var max = 990;
var min = 999;
var res = min + Math.round(Math.random() * (max - min));
var res = min + Math.ceil(Math.random() * (max - min));
var res = min + Math.ceil(Math.random() * (max - min + 1));
var res = parseInt(Math.random() * (max - min + 1)) + min;
console.log(res);
if语句
三大流程控制语句
-
顺序yuju
-
选择语句
-
循环语句
-
选择结构
- 单分支
- 双分支
- 多分支
判断奇偶
// 产生一个100以内的随机整数,判断奇数偶数
var a = parseInt(Math.random() * 100) ;
// if(a % 2 === 0) {
// alert(a + '是一个偶数')
// }
// else {
// alert(a + '是一个奇数')
// }
// if(条件) 条件 true / false 隐式转化
if(a % 2) {
alert(a + '是一个奇数')
}
else {
alert(a + '是一个偶数')
}
判断能3和7整除
<h3>判断数字是否能同时被3和7整除</h3>
<input type="text" id="inp1">
<button id="btn">判断</button>
<input type="text" id="inp2">
<script>
// 1 点击事件
// 2 拿到输入的值
// 3 判断同时被3和7整除 求模
// 4 把结果给输入框
var oBtn = document.getElementById('btn') ;
oBtn.onclick = function() {
var num = document.getElementById('inp1').value ;
var res ;
if(num % 3 == 0 && num % 7 == 0) {
// document.getElementById('inp2').value = '是'
res = '是';
}
else {
// document.getElementById('inp2').value = '否'
res = '否'
}
document.getElementById('inp2').value = res ;
}
判断数字为整数
- number % 1 == 0
- number == parseInt(number)
day3
switch
var a = 4;
多分支语句
switch (a) {
case 0: {
}
case 1: {
}
case 2: {
}
default: {
}
}
var light = 'red';
switch (light) {
case 'red': alert('stop'); break;
case 'green': {
alert('go');
}
break;
case 'yellow': {
alert('wait')
}
break;
default: {
alert('灯坏了')
}
}
// 简写:大括号里面如果只有一条语句,就可以简写(去掉大括号)
if (light == 'red') { alert('stop'); console.log('stop'); }
else alert('go')
判断分数
var score = 56;
// switch 实现分数的判断
// 60-70 及格
// 70-80 还行
// 80-90 良好
// 90-100 优秀
// 100 666
// 0-60 优雅降级
score = parseInt(score / 10);
switch (score) {
case 10: alert(666); break;
case 9: alert('优秀'); break;
case 8: alert('B'); break;
case 7: alert('C'); break;
case 6: alert('D'); break;
case 5:
case 4:
case 3:
case 2:
case 1:
case 0: alert('优雅降级'); break;
}
switch特殊语法
- swich语句一般适用于值是确定的,而不是范围值
- switch默认穿透,需要结合break语句一起使用 break仅在switch中使用
- case是做严格判断(值和数据类型必须严格一致)
- switch(true) case 比较运算符 特殊语法
var score = 65;
switch (true) {
case score < 60: alert('不及格'); break;
case score >= 60 && score < 70: alert('及格'); break;
}
计算天数
年:<input type="text" id="inp1">
月:<input type="text" id="inp2">
日:<input type="text" id="inp3">
<input type="button" value="计算是当年的第几天" id="btn">
<input type="text" id="inp4">
<script>
var oInp1 = document.getElementById('inp1');
var oInp2 = document.getElementById('inp2');
var oInp3 = document.getElementById('inp3');
var oBtn = document.getElementById('btn');
var oInp4 = document.getElementById('inp4');
oBtn.onclick = function () {
var y = oInp1.value * 1;
var m = oInp2.value * 1;
var d = oInp3.value * 1;
var total = 0;
var erDay = 28;
if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) {
erDay = 29;
}
switch (m) {
// case 1: total = d; break;
// case 2: total = 31 + d; break;
// case 3: total = 31 + erDay + d; break;
// case 4: total = 31 + erDay + 31 + d; break;
// case 5: total = 31 + erDay + 31 + 30 + d; break;
// case 6: total = 31 + erDay + 31 + 30 + 31 + d; break;
// case 7: total = 31 + erDay + 31 + 30 + 31 + 30 + d; break;
// case 8: total = 31 + erDay + 31 + 30 + 31 + 30 + 31 + d; break;
// case 9: total = 31 + erDay + 31 + 30 + 31 + 30 + 31 + 31 + d; break;
// case 10: total = 31 + erDay + 31 + 30 + 31 + 30 + 31 + 31 + 30 + d; break;
// case 11: total = 31 + erDay + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + d; break;
// case 11: total = 31 + erDay + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + d; break;
// 2000 12 12
// total = 0
// 0 + 30 + 31 + 31 + 31 + 30
case 12: total += 30;
case 11: total += 31;
case 10: total += 30;
case 9: total += 31;
case 8: total += 31;
case 7: total += 30;
case 6: total += 31;
case 5: total += 30;
case 4: total += 31; // 31 + erDay + 31 + 5
case 3: total += erDay; // erDay + 31 + 5
case 2: total += 31; // 31 + 5
case 1: total += d; // 5
}
oInp4.value = total;
}
</script>
while
while (条件) {
// 循环要做的事情
}
案例
打印100以内所有的奇数
var i = 1;
while (i < 100) {
if (i % 2) {
console.log(i)
}
i++;
}
打印能同时被3和7整除的数
var i = 1;
while (i < 100) {
if (i % 3 == 0 && i % 7 == 0) {
console.log(i);
}
i++;
}
100以内求和
var i = 1;
var total = 0;
while (i <= 100) {
total += i;
i++;
}
console.log(total)
do-while
var i = 0;
do {
console.log(666);
i++;
} while (i < 10);
区别
- while 是先判断后执行 有可能一次也不会执行
- do while 先执行后判断 至少会执行一次
for
for(初始值;条件;变量变化){
循环体
}
for (var i = 0; i <= 100; i++) {
if (i % 2 == 0) {
console.log(i);
}
}
for (var i = 0; i <= 100; i += 2) {
console.log(i);
}
day4
九九乘法表
<style>
.b {
display: flex;
margin: 0;
}
span {
width: 90px;
text-align: center;
line-height: 30px;
border: 1px solid #000;
margin: 10px;
}
</style>
</head>
<body>
<!-- <p>
<span>1*1=1</span>
</p>
<p>
<span>1*2=2</span>
<span>2*2=4</span>
</p> -->
<script>
// 内 行
// i j
// 1 * 1 = 1
// 1 * 2 = 2 2 * 2 = 4
// 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
for (var j = 1; j <= 9; j++) {
document.write('<p class="b">')
for (var i = 1; i <= j; i++) {
// document.write(i + '×' + j + '=' + i * j + ' ');
document.write('<span>' + i + '×' + j + '=' + i * j + '</span>')
}
document.write('</p>')
}
取数问题
parseInt(上一位的余数 / 本位的数)
-
取个位/十位/百位…
var num = 4532; var qian = parseInt(num / 1000); var bai = parseInt(num % 1000 / 100); var shi = parseInt(num % 100 / 10); var ge = num % 10; var qian = parseInt(num % 10000 / 1000); var bai = parseInt(num % 1000 / 100); var shi = parseInt(num % 100 / 10); var ge = parseInt(num % 10 / 1);
-
取时分秒
var miao = 3661000; var day = parseInt(miao / (3600 * 24)); var hour = parseInt(miao % (3600 * 24) / 3600); var min = parseInt(miao % 3600 / 60); var sec = parseInt(miao % 60 / 1);
循环的选择
- while 可以做更复杂的条件循环
- for 更适合于循环次数比较确定的
产生0-10之间的随机数 ,需要多少次才能出现0?
var flag = true;
var count = 0;
while (flag) {
var num = parseInt(Math.random() * 10);
count++;
if (num == 0) {
flag = false;
}
}
console.log(count);
函数
函数:封装具有同一个功能的代码
- 创造
- 反复的使用
- 需要被使用
函数语法
function 函数名(参数) {
// 实现的功能
}
function washer(clothes, xiyiye) {
console.log('我是被' + xiyiye + '洗干净的' + clothes);
}
// 调用工具
washer('帽子', '蓝月亮');
washer('臭袜子', '李白洗衣粉');
function star(n, m) {
for (var j = 0; j < n; j++) {
for (var i = 0; i < n; i++) {
document.write(m)
}
document.write('<br>')
}
}
star(4, '6');
day5
变量作用域
全局变量
- 函数外面声明的变量是全局变量,在外面或者是函数里面都可以访问
局部变量
在函数里面声明的变量,只能在函数里面使用,函数外面使用会报错
//全局变量
var a = 10;
function test() {
var b = 30;
//全局变量在函数内外都可以使用
console.log(a, b); //10,30
}
test();
console.log(a); //10
//局部变量:只能在函数内使用
console.log(b); // b is not defined
形参和实参
- 形参:形式参数 函数定义时,括号里面写的变量
- 实参:实际参数 函数调用时,括号里面写的值
function sum(num) { //形参
var sum = 0;
for (var i = 0; i <= num; i++) {
sum += i;
}
console.log(sum);
}
var num = parseInt(Math.random() * 10);
//实参
sum(num);
函数形参与实参可以不对等
function sum(a, b) {
return a + b;
}
sum(1, 2, 3, 4);
实参比形参少时,默认undefined
function sum(a, b) {
console.log(a, b); //1,undefined
}
sum(1);
函数作用域和参数问题
-
全局变量:函数外声明的变量
-
局部变量:函数内部声明的变量
-
形参:函数定义时的参数
-
实参:函数调用时的参数
-
形参相当于局部变量
-
函数外和函数内声明了同一个变量时(变量名相同),函数优先使用自己的
var a = 10;
function sum(a) { //形参
//函数优先使用自己的变量
console.log(a);
}
sum(5); //5
函数返回值return
想让函数外面访问函数里面的变量,需要把这个变量return
function rand(min, max) {
//num是函数里面声明的,函数外面无法访问
var num = min + Math.round(Math.random() * (max - min));
//返回值 ---函数执行的结果
return num;
}
console.log(rand(10, 20));
console.log(num); //num is not defined
函数如果只有return 提前结束函数
质数封装
function isZhi(num) {
if (!isNaN(num) && num % 1 == 0) {
if (num == 2) {
return true;
}
for (var i = 2; i < num; i++) {
if (num % i == 0) {
return false;
}
}
return true;
} else {
return "参数错误";
}
}
计算器封装
function calc(num1, num2, opt) {
num2 *= 1;
num2 *= 1;
var res;
switch (opt) {
case '+': res = num1 + num2; break;
case '-': res = num1 - num2; break;
case '*': res = num1 * num2; break;
case '/': res = num1 / num2; break;
default: res = "符号错误,无法计算";
}
return res;
}
function calc(num1, num2, opt) {
num1 *= 1;
num2 *= 1;
switch (opt) {
case '+': return num1 + num2;
case '-': return num1 - num2;
case '*': return num1 * num2;
case '/': return num1 / num2;
default: return "符号错误,无法计算";
}
}
函数声明
具名函数
function fn1() {
console.log(666);
}
fn1();
赋值函数
匿名函数 ,赋值式声明
var fn2 = function () {
console.log("xxx");
}
fn2();
匿名函数自调用
!function (num) {
console.log(num); //3
}(3);
+ function (a) {
console.log(a);
}("BLACK");
函数总结
- 函数可以没有形参
- 函数的形参与实参不对等
- 函数可以没有返回值,返回undefind
- 函数如果有return 提前结束
函数无返回值
返回undefined
function test() {
console.log("xx");
}
var a = test();
console.log(a); //undefind
function test() {
console.log("xx");
return;
console.log("pink");
}
var a = test();
console.log(a); //undefind
系统函数
-
isNaN
-
alert 没有返回值 undefined
-
confirm
-
prompt
-
eval
-
alert
警告作用
var a = alert("BLACKPINK"); console.log(a); //undefined
-
confirm
具有确认与取消按钮
确认 返回true
取消 返回false
var a = confirm("确认删除"); console.log(a);
-
prompt
具有输入框,可输入数据
点击取消,则返回–null
确认,返回输入的数据
var a = prompt("请输入成绩"); console.log(a);
-
eval
console.log(eval('1+2')); //3
计算器封装
function calc(num1, num2, opt) {
var res = eval(num1 + opt + num2);
return res;
}
console.log(calc(3, 4, '+'));
预编译
js运行的两个阶段
-
预编译阶段
-
执行阶段
-
变量的预编译–提升var
-
函数的预编译–提升具名函数
-
js从上而下执行
变量的提升
console.log(a); //undefined,变量的提升
var a = 5;
console.log(a); //5
函数的提升
//具名函数的提升
fn1(); //pink
function fn1() {
console.log("PINK");
}
// 你看到的代码
console.log(a);
var a = 5;
console.log(a)
fn();
function fn() {
console.log(1)
}
// 真正js执行的顺序
//提升
var a;
function fn() {
console.log(1)
}
console.log(a);
a = 5;
console.log(a)
fn();
面试题
console.log(v1); //undefined
var v1 = 100;
function foo() {
console.log(v1); //undefined
var v1 = 200;
console.log(v1); //200
}
foo();
console.log(v1); //100
实际执行顺序
- 函数内部的变量也会有提升
var v1;
function foo(){
var v1;
console.log(v1);
v1 = 200;
console.log(v1);
}
console.log(v1);
v1 = 100;
foo();
console.log(v1);
console.log(add(1, 2));
function add(x, y) {
return x + y;
}
console.log(add);
var add = 10;
console.log(add);
console.log(add(1, 2))
实际执行顺序
var add;
function add(x, y) {
return x + y;
}
console.log(add(1, 2)); //3
console.log(add) //fn..
add = 10
console.log(10);
console.log(add(1, 2)) //error
默认值
形参和实参不对等
function foo() {
// a++;
return 666;
}
console.log(foo(3, 4, 5, 6));
短路赋值
// 短路赋值 见真伪真
var a = 0 || 4;
console.log(a); // 4
var a = 3 || 4;
console.log(a) // 3
// 见假即为假
var b = 3 && 4 && 6 && 0 && 6;
console.log(b);
function add(a, b) {
//当a/b为空字符串,boolean值为false,则返回0
a = a || 0;
b = b || 0;
console.log(a + b);
}
var a = 3;
function test(a) {
return ++a;
}
console.log(test(a)); //4
console.log(a); //3
三目运算符
n % 2 ? console.log('奇数') : console.log('偶数');
质数判断
var num = 7;
for (var i = 2; i < num; i++) {
if (num % i == 0) {
break
}
}
i == num ? console.log('质数') : console.log('合数');
function isZhi(num) {
for (var i = 2; i < num; i++) {
if (num % i == 0) {
break;
}
}
return i == num ? true : false;
}
function isZhi(num) {
for (var i = 2; i < num; i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
总结
- 作用域: 全局变量和局部变量
- 形参和实参 不对等
- 返回值 return
- 函数的声明方式 具名函数 赋值式函数 匿名函数
- 系统函数 isNaN alert confirm prompt eval
- js预编译
- 短路赋值
- 三目运算符
- 质数判断的方法
day6
封装
// min -max 之间的随机数 ,不包含max
function rand(min, max) {
return parseInt(Math.random() * (max - min)) + min;
}
// 获取元素
function $(id) {
return document.getElementById(id)
}
最大公约数
// 最大公约数 4 6
// 4 %2 6 %2 2
// 4 %3 6 %3
// 4 %4 6%4
function gcd(a, b) {
//找到a和b之间更小的数
var min = a < b ? a : b;
//假设法
var res = 1;
for (var i = 2; i <= min; i++) {
if (a % i == 0 && b % i == 0) {
res = i;
}
}
return res;
}
function gcd(a, b) {
//找到a和b之间更小的数
var min = a < b ? a : b;
//倒着取余数
for (var i = min; i >= 1; i--) {
if (a % i == 0 && b % i == 0) {
return i;
}
}
}
递归
递归函数:函数不断地调用自己
- 出口
- 调用自己
求和
function sum(n) {
if (n == 1) {
return 1;
} else {
return sum(n - 1) + n;
}
}
阶乘
function jieCheng(n) {
if (n == 1) {
return 1;
}
return n * jieCheng(n - 1)
}
斐波拉切数列
// 斐波拉切数列
// 1 1 2 3 5 8 13 21
function fb(n) {
if (n == 1 || n == 2) {
return 1;
}
return fb(n - 1) + fb(n - 2);
}
欧几里得算法
求最大公约数
// 欧几里得算法
// 6 24
// 6 % 24 6
// 24 % 6 0 6
function gcd(a, b) {
if (a % b == 0) {
return b;
}
return gcd(b, a % b);
}
数组
数组:存储多个数据
数组声明
字面量声明
//字面量声明
var arr = [1, 2, 3, 4, 5, 6, 7];
实例化对象
// 实例化对象 存储10条数据
var arr = new Array(10);
数组属性
角标:从0开始
长度:length
//字面量声明
var arr = [1, 2, 3, 4, 5, 6, 7];
console.log(arr[3]); //4
//数组长度
console.log(arr.length); //7
console.log(typeof arr); //object
isArray
判断否为数组
var arr = [1, 2, 3, 4, 5, 6, 7];
console.log(Array.isArray(arr)); //true
数组练习
//随机产生0-100之间的数,放入数组中
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = parseInt(Math.random() * 100);
}
console.log(arr);
arr[arr.length] = 'BLACK';
console.log(arr);
var arr2 = [, 5, 6, 7, 8, 9, , , , 10, 20];
console.log(arr2);
//追加10条数据
for (var i = 0; i < 10; i++) {
arr2[arr2.length] = parseInt(Math.random() * 100);
}
console.log(arr2);
数组遍历
- 直接利用for循环
- for in 关键字遍历
- for of 关键字遍历
var arr = [2, 4, 6, 8, 10, 12, 14, , , 0];
// 数组中为空的元素为 undefined
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// 数组中元素为空的元素,不输出
//for in i可以自定义 代表的是角标
for (var i in arr) {
console.log(arr[i]);
}
//数组中为空的元素为 undefined
// for of val可以自定义 代表的是数组的值
for (var val of arr) {
console.log(val);
}
数组方法
- pop 删除数组的最后一个元素并返回删除的元素。
- push 向数组的末尾添加一个或更多元素,并返回新的长度。
- shift 删除并返回数组的第一个元素。
- unshift 向数组的开头添加一个或更多元素,并返回新的长度。
day7
数组的深拷贝和浅拷贝
- 基本数据类型存储的是值—存储在栈内存
- 引用数据类型存储的是地址—存储在堆内存中
基本数据类型
var a = 3;
var b = a;
a = 5;
console.log(a, b); //5,3
a的值改变,不影响b的值
基本数据类型存储的是值,存储在栈内存中
浅拷贝
var arr1 = [1, 2, 3];
// arr1的改变会影响arr2
// var arr1 = [1, 2, 3];
// // 把arr1的地址给了arr2,因此他们指向了同一个地址 --- 浅复制
var arr2 = arr1;
arr1.pop();
console.log(arr1); //[1,2]
console.log(arr2); //[1,2]
两个数组指向的是同一个地址,一个改变,会影响另一个
深拷贝
深复制 arr1和arr2没有关联
- 重新声明一个数组
- 将数组进行遍历,然后赋值
var arr1 = [1, 2, 3];
var arr2 = [];
for (var val of arr1) {
arr2.push(val);
}
arr1.pop();
console.log(arr1); //[1,2]
console.log(arr2); //[1,2,3]
var arr1 = [1, 2];
var arr2 = arr1;
//arr1重新指向了一个地址
arr1 = [8, 9];
console.log(arr1); //[8,9]
console.log(arr2); //[1,2]
函数的值传递和引用传递
-
引用数据类型,实参是把地址传给了函数
function pop(arr) { // 把数组的长度-1 arr.length = arr.length - 1; return arr }
-
普通数据类型,实参这是把值传递给函数使用
// 普通数据类型,实参只是把值传递给函数使用 function fn(n) { n++; return n; } var a = 5; console.log(fn(a)); //6 console.log(a); //5
indexOf封装
function indexOf(arr, val) {
for (var i in arr) {
if (arr[i] === val) {
return i;
}
}
return -1;
}
var arr = [3, 4, 5, 6];
console.log(indexOf(arr, 9));
数组常用方法
splice
splice() 方法用于添加或删除数组中的元素。
**注意:**这种方法会改变原始数组
返回值:
返回删除元素的数组
如果仅删除一个元素,则返回一个元素的数组。 如果未删除任何元素,则返回空数组。
可利用此方法实现删除、插入、替换
参数 | 描述 |
---|---|
index | 必需。规定从何处添加/删除元素。 该参数是开始插入和(或)删除的数组元素的下标,必须是数字。 |
howmany | 可选。规定应该删除多少元素。必须是数字,但可以是 “0”。 如果未规定此参数,则删除从 index 开始到原数组结尾的所有元素。 |
item1, …, itemX | 可选。要添加到数组的新元素 |
-
利用splice删除元素
splice(index,howmany) 删除
var arr = ["a", "b", "c"];
//参数:
// 1:起始下标
// 2:删除元素的个数
// 元素的删除
console.log(arr.splice(1, 2)); //["b","c"]
//改变元素组,删除b,c两个元素
console.log(arr); //[a]
-
利用splice替换元素
splice(index,howmany,val1,val2…) 替换
var arr = ["a", "b", "c"]; //参数: // 起始下标从1开始 // 删除两个元素 // 添加两个新的元素 arr.splice(1, 2, "e", "f"); console.log(arr); //[a,e,f]
-
利用splice插入元素
splice(index,0,val1,val2…) 插入
var arr = ["a", "b", "c"]; //参数含义: // 1.起始下标从1开始 //2.删除0个元素 //3-n:插入的元素 arr.splice(1, 0, "k", "d", "s"); console.log(arr); //["a", "k", "d", "s", "b", "c"]
slice
slice()方法可提取字符串的某个部分,并以新的字符串返回被提取的部分。
注意: slice() 方法不会改变原始数组。
参数
参数 | 描述 |
---|---|
start | 可选。规定从何处开始选取。如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取 |
end | 可选。规定从何处结束选取(结果不包含end)。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。 |
var arr = ["a", "b", "c", "d", "e", "f"];
//从下标1开始截取,不包含下标3
console.log(arr.slice(1, 3)); //["b","c"]
console.log(arr.slice(1)); //["b","c","d", "e", "f"]
//从倒数第二个开始截取
console.log(arr.splice(-2)); //["e","f"]
indexOf
查找数组中是否存在某个值,返回角标
不存在返回-1
var arr = ["a", "b", "c", "d", "e", "f"];
console.log(arr.indexOf("c")); //2
console.log(arr.indexOf("k")); //-1
includes
includes 查找数组中是否存在某个值,返回布尔值
var arr = ["a", "b", "c", "d", "e", "f"];
console.log(arr.includes("d")); //true
console.log(arr.includes("t")); //false
join
把数组变成字符串
参数:分隔符
无参数,分隔符默认为,
var arr = ["a", "b", "c", "d", "e", "f"];
// 无参数,默认分割符,
console.log(arr.join()); //a,b,c,d,e,f
console.log(arr.join("-")); //a-b-c-d-e-f
concat
把数组或者字符串拼接起来
concat() 方法用于连接两个或多个数组或普通类型元素。
参数
参数 | 描述 |
---|---|
array2, array3, …, arrayX | 必需。该参数可以是具体的值,也可以是数组对象。可以是任意多个。 |
var arr = ["a", "b", "c"];
var b = ["d", "e"];
//连接数组
console.log(arr.concat(b)); //["a", "b", "c","d","e"]
//连接基本类型元素
console.log(arr.concat("hello")); //["a", "b", "c","hello"]
//不会改变元素组
reverse
数组反转
var arr = ["a", "b", "c"];
//会改变元素组
arr.reverse();
console.log(arr); //["c","b","a"]
总结
方法 | 说明 |
---|---|
splice | 实现数组 删除/替换/插入元素(改变原元素组) |
slice | 截取数组 |
indexOf | 查找数组中是否存在某个值,返回角标 |
includes | 查找数组中是否存在某个值,返回布尔值 |
join | 把数组变成字符串 |
concat | 把数组或者字符串拼接起来 |
reverse | 改变原数组 反向 (改变原数组) |
arguments
arguments 接收到的实参列表 ,是一个伪数组
有数组的状态,可以拿到长度和角标,但是没有数组的方法(无pop,push,shift等数组方法)
function fn() {
//传过来的实参,变成一个数组
console.log(arguments); //[1,2,3]
console.log(arguments.length); //3,实参个数
}
fn(1, 2, 3);
数组方法封装
push封装
function push(arr) {
//第一个参数是数组,不需要遍历,i从1开始
//遍历arguments实参数组[arr,"a","b"],第一个实参是数组本身,不需要遍历,i从1开始
for (var i = 1; i < arguments.length; i++) {
arr.push(arguments[i]);
}
return arr;
}
var arr = [3, 4, 5];
push(arr, "a", "b");
console.log(arr); //[3, 4, 5,"a","b"]
join封装
join: 数组:arr[1,2,3] —>字符串 1&2&3
function join(arr, fuhao) {
//连接符号默认为,
fuhao = fuhao || ",";
//定义字符串进行拼接
var res = '';
//遍历数组
for (var i in arr) {
console.log(arr[i]);
// 字符串拼接上遍历的数组元素
res += arr[i];
//如果数组元素不是最后一个,添加上分割符
// 最后一个,后面没有分割符发
i < arr.length - 1 ? res += fuhao : res;
}
return res;
}
var arr = [1, 2, 3];
console.log(join(arr, "&")); // 1&2&3
concat封装
function concat(arr1, arr2) {
var newArr = [];
//遍历实参列表
for (var i = 0; i < arguments.length; i++) {
var tempArr = arguments[i];
//如果参数是数组,则遍历数组,将数组元素一个一个添加到newArr中
if (Array.isArray(tempArr)) {
//遍历数组
for (var val of tempArr) {
//将值添加入新数组 newArr
newArr.push(val);
}
} else {
//如果参数是普通元素,直接添加到newArr
newArr.push(tempArr);
}
}
return newArr;
}
var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
//连接数组
console.log(concat(arr1, arr2)); //[1,2,3,4,5,6]
//连接普通元素
console.log(concat(arr1, "hi")); //[1,2,3,"hi"]
排序
冒泡排序
var arr = [9, 8, 7, 6, 5];
// 排序
// 第一趟 拿到最大值9放在最后面
// 9 8 8 9 7 6 5
// 9 7 8 7 9 6 5
// 9 6 8 7 6 9 5
// 9 5 8 7 6 5 9
// 第二趟 拿到倒数第二大的值
// 8 7 7 8 6 5 9
// 8 6 7 6 8 5 9
// 8 5 7 6 5 8 9
// 8 9
// 第三趟
// 7 6 6 7 5 8 9
// 7 5 6 5 7 8 9
// 7 8
// 8 9
// 第四趟
// 6 5 5 6 7 8 9
// 6 7
// 7 8
// 8 9
// 冒泡排序:相邻的两个值进行比较,大的往后排
// 比较的趟数
for (var i = 0; i < arr.length - 1; i++) {
// 相邻的比较
for (var j = 0; j < arr.length - 1 - i; j++) {
//大于前面的值则与前面的值进行交互
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
选择排序
- 从第一个开始,与数组其他的值进行比较
- 如果小于自身,进行替换
- 从而保证当前位置总是相对于数组其他元素的最小值
// 选择排序,拿第一个位置上的 数与后面所有的数进行比较,拿到最小值
// 比较的趟数
for (var j = 0; j < arr.length - 1; j++) {
// 拿第j个位置上的数与后面所有的数进行比较
for (var i = j + 1; i < arr.length; i++) {
//大于数组其他元素,进行替换
if (arr[j] > arr[i]) {
var temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
console.log(arr)
选择排序改进
较少交换的次数
将min标记为当前最小的角标
// 减少比较排序的交换次数
// 比较的趟数
for (var j = 0; j < arr.length - 1; j++) {
// 假设第j个数是最小的
var min = j;
for (var i = j + 1; i < arr.length; i++) {
if (arr[min] > arr[i]) {
// 找到最小数的角标
min = i;
}
}
if (min != j) {
var temp = arr[min];
arr[min] = arr[j];
arr[j] = temp;
}
}
数组去重
-
方法一
创建一个新的数组,把不重复的数丢进去
function quChong(arr) { //创建新数组 var newArr = []; for (var i = 0; i < arr.length; i++) { //如果新数组不包含当前遍历的元素,进行添加push if (!newArr.includes(arr[i])) { newArr.push(arr[i]); } } return newArr; }
-
方法二
- 遍历数组
- 拿每个元素与后面所有的元素比较,如果相等,则重复,利用splice删除
- 不相等,继续遍历下一个
var arr = [1, 4, 2, 4, 2, 1, 1, 1, 5, 3]; for (var j = 0; j < arr.length; j++) { // 拿点前遍历的arr[j]与后面所有的数做比较 //如果相等,则重复,利用splice删除 for (var i = j + 1; i < arr.length; i++) { if (arr[j] === arr[i]) { //利用splice方法删除当前元素 // 数组会塌陷 !!!!!! arr.splice(i, 1); i--; } } } console.log(arr)
数据统计
方法一
- 首先将数组去重
- 遍历去重后的数组,将遍历的元素,在先前的数组中进行计数
var arr = [3, 3, 3, 2, 2, 5, 6, 3, 0, 6];
//数组去重
function noRepeat(arr) {
var newArr = [];
for (var val of arr) {
if (!newArr.includes(val)) {
newArr.push(val);
}
}
return newArr;
}
function tongJi(arr) {
//去重数组
var newArr = noRepeat(arr);
//遍历原数组
for (var val of newArr) {
var count = 0;
for (var value of arr) {
if (value == val) {
count++;
}
}
document.write(val + "出现了" + count + "次" + "<br/>");
}
}
tongJi(arr);
方法二
- 首先遍历数组元素
- 从当前元素的后一个开始,看是否有相同的,如果相同则重复,计数器+1,并将其删除(splice)
- 每次遍历一个元素,计数器都从1开始
var arr = [3, 3, 3, 2, 2, 5, 6, 3, 0, 6];
//数组统计
//找到相同的则删除一个 ,并计数
for (var i = 0; i < arr.length; i++) {
var count = 1;
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
count++;
//删除当前找到的,重复的
arr.splice(j, 1);
//防止数组塌陷!!
j--;
}
}
document.write(arr[i] + "出现了" + count + "次" + "<br/>");
}
方法三
-
首先将数组排序
[3, 3, 3, 2, 2, 5, 6, 3, 0, 6]
排序后:
[0, 2, 2, 3, 3, 3, 3, 5, 6, 6]
-
然后遍历数组,如果后面的值与其相同,则计数器+1,否则,后面无相同的数据,数组下标从当前数组+count(计数器)开始,遍历下一个元素
//数组统计
svar arr = [3, 3, 3, 2, 2, 5, 6, 3, 0, 6];
arr.sort();
console.log(arr);
for (var i = 0; i < arr.length;) {
var count = 0;
for (var j = i; j < arr.length; j++) {
if (arr[i] === arr[j]) {
count++;
} else {
break;
}
}
document.write(arr[i] + "出现了" + count + "次" + "<br/>");
i += count;
}
day8
回顾
1.数据类型
基本数据类型 number string boolean null undefined
引用数据类型 array object
区别
基本数据类型存储在栈内存中,变量实际上存储的是值
引用数据类型存储在堆内存中,变量实际上存储的是地址
数组的深拷贝和浅拷贝
浅拷贝 拷贝的是地址 --- 共享一个地址
深拷贝 拷贝的是值 -- 不会共享地址
3 函数的值传递和引用传递
值传递指的是普通数据类型
引用传递指的是引用数据类型 传递的实际上是地址(形参和实参共享一个地址)
4数组的基本方法
pop
push
shift unshift
5.数组常用方法
splice 删除 替换 插入
slice[startindex,endindex) 截取数组
indexOf
includes
join 数组转字符串
concat
reverse
6.数组的方法的封装
arguments 接收实参的方式 伪数组
函数的引用传递
需要改变原数组,就操作原数组 如果不需要改变原数组,就声明一个新的数组
7.数组的排序
冒泡排序 相邻的做比较 j j+1
选择排序
i j(i+1) 作比较 一边比较一边交换
i j(i+1) 作比较只记录角标 最后才做交换
8.数组的去重
方法一:
把不重复的值放进新数组
创建新数组
遍历原数组,判断原数组中的值在新数组中是否存在,不存在就push
if (!newArr.includes(arr[i]))
方法二:
把重复的删掉
拿原数组中的每一个值与后面的每一个值进行比较,如果重复就删除后面的值
for(var i = 0 ; i < arr.length ; i++)
for(var j = i + 1 ; j < arr.length ; j++)
数组塌陷 j--
作业
-
找到数组的最大值和最小值
function minAndMaxFromArr(arr) { // 需要返回两个值,因此返回一个数组 var newArr = []; // 假设第一个值是最小的,然后拿后面的每一个值与第一个做比较,记录较小的这个值的角标 var min = 0; var max = 0; //遍历数组 for (var i in arr) { // 找最小值的角标 if (arr[min] > arr[i]) { min = i } // 找最大值的角标 if (arr[max] < arr[i]) { max = i; } } newArr.push(arr[min]) // var max = 0; // for (var i in arr) { // if (arr[max] < arr[i]) { // max = i // } // } newArr.push(arr[max]); return newArr } console.log(minAndMaxFromArr([4, 1, 2, 7, 5, 3]))
数组插入一个值
实现数组的插入值:找到插入的位置,找角标
- 最前面 插入的值比第一个更小
- 最后面 插入发值比最后一个还大
- 中间某个位置 插入的值比前一个大,比后一个小
// 实现数组的插入值:找到插入的位置,找角标
// 最前面 插入的值比第一个更小
// 最后面 插入发值比最后一个还大
// 中间某个位置 插入的值比前一个大,比后一个小
function insert(arr, n) {
var index;
if (n < arr[0]) {
index = 0;
}
if (n > arr[arr.length - 1]) {
index = arr.length
}
for (var i = 0; i < arr.length; i++) {
if (n >= arr[i] && n <= arr[i + 1]) {
index = i + 1;
break;
}
}
arr.splice(index, 0, n)
}
var arr = [2, 4, 7, 8, 9, 9, 10];
insert(arr, 9);
console.log(arr)
统计
//数组去重
function noRepeat(arr) {
var newArr = [];
for (var i in arr) {
if (!newArr.includes(arr[i])) {
newArr.push(arr[i])
}
}
return newArr
}
// 先去重得到新数组
// 拿新数组中的每一个值与原数组中的每一个值进行比较,如果相等就计数
// function countArr(arr) {
// var newArr = noRepeat(arr);
// console.log(newArr);
// // 拿新数组中的每一个值与原数组中的每一个值进行比较,如果相等就计数
// for (var i in newArr) {
// var count = 0;
// for (var j in arr) {
// if (newArr[i] === arr[j]) {
// count++;
// }
// }
// document.write(newArr[i] + '出现的次数是' + count + '次' + '<br>')
// }
// }
// // 3 2 5 6 0
// var arr = [3, 3, 3, 2, 2, 5, 6, 3, 0, 6];
// countArr(arr)
方法二
// 第二种方法
// 拿数组中的每一个值与后面的每一个值进行比较
// function countArr(arr) {
// for (var i = 0; i < arr.length; i++) {
// var count = 1;
// for (var j = i + 1; j < arr.length; j++) {
// if (arr[i] === arr[j]) {
// arr.splice(j, 1);
// j--; //防止数组塌陷
// count++;
// }
// }
// document.write(arr[i] + '出现的次数是' + count + '次' + '<br>')
// }
// }
// var arr = [3, 3, 3, 2, 2, 5, 6, 3, 0, 6];
// countArr(arr)
方法三
// 0,2,2,3,3,3,3,5,6,6
// 先进行排序
// 再排序后的数组中的不重复的值拿出来统计
function countArr(arr) {
arr.sort();
console.log(arr);
// [0, 2, 2, 3, 3, 3, 3, 5, 6, 6]
for (var i = 0; i < arr.length;) {
var count = 0;
for (var j = i; j < arr.length; j++) {
if (arr[i] === arr[j]) {
count++
}
else {
break;
}
}
document.write(arr[i] + '出现的次数是' + count + '次' + '<br>');
i += count;
}
}
var arr = [3, 3, 3, 2, 2, 5, 6, 3, 0, 6];
数组扩展方法
forEach:迭代方法
- sort(function(a,b){return a-b}):排序
- forEach(function(val,i,arr){}:遍历数组,没有返回值
- map 同一种规律去改变数组中的每一个值 map(function(val,i,arr){return val…}) 一定要有返回值
- some:判断数组中是否有一个值满足条件 返回的是布尔值 some(function(val,i,arr){return val > 18})
- filter:过滤器 把数组中满足条件的值筛选出来 返回的是新的数组 filter(function(val,i,arr){return val > 18})
- reduce:reduce 把数组当中的值变成一个值(比如求和) reduce(function(total,val,i,arr){return total += val})
var arr = [3, 2, 12, 45, 65, 5, 51];
// 按照ASCII升序排列
// arr.sort();
// console.log(arr);
// // sort括号里面,是一个实参,是一个匿名函数
// arr.sort(function (a, b) { return b - a });
// console.log(arr)
var sum = 0;
arr.forEach(function (val, i, arr) {
console.log(val)
sum += val;
})
console.log(sum)
var arr = [1, 2, 3, 4];
var res = arr.map(function (val, i, arr) {
return val += 2
})
console.log(res); //[2,4,5,6] 将数组中的值都+2
var arr = [16, 26, 38, 8];
var res = arr.every(function (val, i, arr) {
return val > 18
});//判断数组中每个值是否都大于18,
console.log(res) //false
var arr = [16, 26, 38, 8];
var res = arr.some(function (val, i, arr) {
return val > 20
}) //判断数组中是否有值大于20,大于返回true,都小于返回false
console.log(res) //true
var arr = [16, 26, 38, 8];
var res = arr.filter(function (val, i) {
return val > 20
}); //过滤数组中,都大于20的值,返回的依然是数组
console.log(res) //[26,38]
var arr = [16, 26, 38, 8];
var res = arr.reduce(function (sum, val, i, arr) {
return sum += val;
}) //求和,sum形参为初始值,0
console.log(res)
回调函数
回调函数:函数作为参数
//自行封装的forEach方法,
//参数:1.要遍历的数组,第二个参数,一个函数,此函数可包含两个参数,分别是数组中的每个值,数组下标数组本身
function forEach(arr, fn) {
for (var i in arr) { //循环遍历数组
// 函数实际调用的地方 实参
fn(arr[i], i, arr)
}
}
var newArr = [];
forEach([1, 2, 3], function (a, i) {
console.log(a)
newArr[i] = a;
})
function rand(min, max, fn) {
var num = min + parseInt(Math.random() * (max - min));
// console.log(n);
// document.write(n)
// 实际调用函数的地方
fn()
}
// 这个匿名函数是声明的地方
rand(10, 20, function () {
console.log(6)
})
every封装
数组中只要有一个不满足条件,返回ture,都满足条件才返回false
<script>
function every(arr, fn) {
for (var i in arr) {
if (!fn(arr[i], i, arr)) { //传值给fn,如果fn一旦不满足条件,则是true
return false
}
}
return true
}
var res = every([1, 2, 3], function (val, i, arr) {
return val > 1
})
console.log(res)
</script>
二维数组
// var arr = [1, 2, 4, 5];
var arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// 取值
// console.log(arr[0][0]);
//遍历
// arr.forEach(function (val) {
// // console.log(val);
// val.forEach(function (item) {
// console.log(item)
// })
// })
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr[i].length; j++) {
console.log(arr[i][j])
}
}
字符串
字符串声明
- 字面量声明
- 对象声明
// 字符串的声明方式 字面量声明
var str = 'hello';
// var str = new String('hello');
console.log(str);
// 长度
console.log(str.length);
// 角标
console.log(str[2]);
// 字符串的长度和角标对应的值只能读不能改写
// str[1] = 'w'; // 错误
字符串遍历
var str = 'hesvdv';
// for (var i = 0; i < str.length; i++) {
// console.log(str[i])
// }
// for (var i in str) {
// console.log(str[i])
// }
// for (var val of str) {
// console.log(val)
// }
字符串方法
-
charAt 角标对应的字符
-
charCodeAt 角标对应的字符对应的ASCII
-
String.fromCharCode 把ASCII转成对应的字符
-
indexOf
-
includes
-
toUpperCase
-
toLowerCase
<script>
var str = 'hahahaW';
console.log(str.charAt(2)) //h
console.log(str[2]) //h
console.log(str.charCodeAt(0)) //104
console.log(String.fromCharCode(104)); //h
console.log(str.toUpperCase()) //HAHAHAW
console.log(str.toLowerCase()) //hahahaw
console.log(str.indexOf('a')) //1
console.log(str.indexOf('ah')); // 1
console.log(str.includes('ah')); //true
</script>
验证码
准备工作
<script>
// var arr = [0,1,2,3,4,5,6,6]
var numArr = [];
//0-9d的数组
for (var i = 0; i < 10; i++) {
numArr.push(i)
}
//A-Z大写字母
var bigArr = []
for (var i = 65; i <= 90; i++) {
var str = String.fromCharCode(i);
bigArr.push(str)
}
//a-z小写字母
var smallArr = []
for (var i = 97; i <= 122; i++) {
var str = String.fromCharCode(i);
smallArr.push(str)
}
var arr = numArr.concat(bigArr, smallArr)
console.log(arr)
// 验证码必须包含小写,大写和数字
</script>
作业
// 1 数组的去重
// 2 数组的统计
// 3 数组的迭代方法 回调函数
// 4 动态生成表格 forEach ********************
// 5 验证码及验证(不区分不大小写,必须有数字和字母)
// 6 自己封装 toUpperCase toLowerCase
day9
回顾
// 数组
// 引用数据类型 堆和栈
// 深复制和浅复制
// 函数的值传递和引用传递
// 基本方法
// pop push unshift shift
// 常用方法
// splice slice indexOf includes join concat reverse
// 迭代方法
// forEach map every some filter reduce sort
// 排序
// 去重
// 统计
// 多维数组 动态表格的生成
// 回调函数
// 字符串的声明方式
// 字面量声明,直接声明
var str = '';
var arr = [];
// 实例化对象
var str = new String('hello');
var arr = new Array();
// 角标和长度
// 数组的角标和长度可读可写
// 字符串的角标和长度可读不可写
// 字符串遍历 for for in for of
// 字符串的基本方法
// charAt(i) [i]
// charCodeAt(i) ASCII
// String.fromCharCode(ASCII) 字符
// 字符串的常用方法
// indexOf 查找字符串
// includes
// toUpperCase
// toLowerCase
大小写封装
function upperCase(str) {
// 字符串本身无法修改,搞一个新的字符串做拼接 +
var res = '';
for (var i = 0; i < str.length; i++) {
// res += str.charAt(i)
var code = str.charCodeAt(i);
// 判断小写
if (code >= 97 && code <= 122) {
code -= 32;
res += String.fromCharCode(code)
} else {
res += str.charAt(i)
}
}
return res
}
验证码
<span id="showCode"></span>
<input type="text" id="inp">
<input type="button" id="refresh" value="刷新">
<br>
<input type="button" value="验证" id="btn">
<script>
$('showCode').innerHTML = code(4);
$('refresh').onclick = function () {
$('showCode').innerHTML = code(4);
}
$('btn').onclick = function () {
var codes = $('showCode').innerHTML;
var inpValue = $('inp').value;
if (codes.toLowerCase() === inpValue.toLowerCase()) {
// alert('验证成功')
// 页面的跳转
location.href = 'http://www.baidu.com'
} else {
$('showCode').innerHTML = code(4);
alert('验证失败')
}
}
var numArr = [];
for (var i = 0; i < 10; i++) {
numArr.push(i)
}
var bigArr = [];
for (var i = 65; i <= 90; i++) {
bigArr.push(String.fromCharCode(i))
}
var smallArr = [];
for (var i = 97; i <= 122; i++) {
smallArr.push(String.fromCharCode(i))
}
var allArr = numArr.concat(bigArr, smallArr);
console.log(allArr);
function $(id) {
return document.getElementById(id);
}
//随机生成0-n的随机数
function rand(n) {
return parseInt(Math.random() * n)
}
// 数字,小写,大写各取一个,随机再取剩下的
// 打乱顺序 -- 字符串不能改变 数组可以改变
// 把生成的验证码放进数组
function code(n) {
n = n || 4;
var arr = [];
arr.push(numArr[rand(numArr.length)]);
arr.push(bigArr[rand(bigArr.length)]);
arr.push(smallArr[rand(smallArr.length)]);
for (var i = 0; i < n - 3; i++) {
arr.push(allArr[rand(allArr.length)]);
}
// 打乱数组
for (var i = 0; i < arr.length; i++) {
//随机交换顺序
var num = rand(arr.length);
var temp = arr[i];
arr[i] = arr[num];
arr[num] = temp;
}
console.log(arr)
return arr.join('');
}
</script>
字符串方法
- indexOf
- includes
- toUpperCase
- toLowerCase
- substring[startIndex,endIndex) 截取字符串
- substr(index,howmany) 截取字符
- replace 替换
- trim 去掉首尾的空格
- split 字符串转为数组
var str = 'good good study , day day up';
var res = str.substring(1, 3);
console.log(res) //oo
var res = str.substr(2, 3);
console.log(res) // od空格
var str = '卧槽,字符串真简单';
var res = str.replace('卧槽', '**');
console.log(res); // **,字符串真简单
var str = ' dvdv csv a ';
console.log(str.trim()); // dvdv csv a
var str = 'hello';
var str = 'good&good&study';
console.log(str.split('&')) // ["good", "good", "study"]
随机5位数
// 随机5位以内的数,输出每一位分别是多少?
// 0 - 100000 0 - 99999
// var arr = ['个','十','百','千','万']
var str = parseInt(Math.random() * 100000) + '';
console.log(str);
// var arr = ['万', '千', '百', '十', '个']
var arr = ['个', '十', '百', '千', '万']
for (var i = 0; i < str.length; i++) {
console.log(arr[str.length - 1 - i] + ':' + str[i])
}
切割字符串
// 1 拿到问号后面的一串
var str = 'http://127.0.0.1:5500/day09/1.php?user=www&pwd=123123&repwd=123123&code=asda';
var str2 = str.split('?')[1];
console.log(str2);
var arr = str2.split('&');
console.log(arr);
// ["user=www", "pwd=123123", "repwd=123123", "code=asda"]
for (var i in arr) {
arr[i] = arr[i].split('=')
}
// arr = arr.map(function (val) {
// return val.split('=')
// })
// console.log(arr)
压缩统计
<script>
// 压缩统计 'asdasdsfda' => 'a3s3d3f1'
function countStr(str) {
// 放最终统计的结果
var res = '';
// 字符串去重
// 创建一个新的字符串,不重复的字符放进新的字符串
var str2 = '';
for (var i in str) {
if (!str2.includes(str[i])) {
str2 += str[i]
}
}
// 统计
for (var i in str2) {
var count = 0;
for (var j in str) {
if (str2[i] === str[j]) {
count++;
}
}
res += str2[i] + count;
}
return res
}
console.log(countStr('asdasdsfda'))
</script>
对象
引用数据类型:数组和对象
- 数组是有序的,对象是无序的
- 对象的键名不能重复,后面覆盖前面
字面量声明
// 字面量声明
var obj = {
// 键名 :键值
name: '思文',
age: '28',
hobby: '思文',
say: function () {
console.log('我不高兴了')
},
name: '丁旭',
};
// 读数据
console.log(obj.name);
obj.say();
console.log(obj['name']);
对象遍历
for (var i in obj) {
console.log(i);
// i是一个变量 obj[i]
console.log(obj[i])
}
// var arr = [1, 2, 3];
// for (var i in arr) {
// console.log(i) //字符串
// }
// var arr = {
// length: 10,
// push: function () { },
// pop: function () { }
// }
// arr.length
// ar.pop()
实例化对象
var obj = new Object();
obj.name = 'jennie';
obj.age = 28;
obj.say = function () {
console.log(this.name);
}
// 在对象里面,this指点对象本身
obj.say();
var arr = new Array();
arr[0] = 1;
arr[1] = 2;
数组+对象
var data = [
{
name: '丁旭',
age: '28'
},
{
name: '丁旭2',
age: '282'
},
{
name: '丁旭3',
age: '283'
}
]
data.forEach(function (val) {
for (var key in val) {
console.log(val[key])
}
})
动态生成商品列表
<body>
<div class="search">
<input type="text" id="inp">
<input type="button" value="搜索" id="btn">
</div>
<ul class="list" id="list">
</ul>
<script>
var data = [
{
goods: '手机',
price: '20¥',
title: '黑鲨4 磁动力升降肩键',
img: '../images/1.png'
},
{
goods: '手机',
price: '10¥',
title: '小米11 青春版 轻薄',
img: '../images/1.jpg'
},
{
goods: '电视',
price: '200¥',
title: '小米全面屏电视65英寸 E65X',
img: '../images/1.png'
},
{
goods: '电视',
price: '100¥',
title: '小米电视4A 70英寸',
img: '../images/2.jpg'
},
{
goods: '衣服',
price: '20¥',
title: '衣服真显瘦',
img: '../images/3.jpg'
},
{
goods: '帽子',
price: '10¥',
title: '帽子真百搭',
img: '../images/1.png'
},
{
goods: '衣服2',
price: '200¥',
title: '衣服真显瘦',
img: '../images/1.png'
},
{
goods: '帽子2',
price: '100¥',
title: '帽子真百搭',
img: '../images/1.png'
}
]
renderUl(data);
$('btn').onclick = function () {
var wd = $('inp').value;
// 筛选数据 把满足条件的数据筛选出来 filter
var res = data.filter(function (val) {
return val.goods.includes(wd)
})
console.log(res);
renderUl(res)
}
function renderUl(data) {
var res = '';
data.forEach(function (val) {
// res += '<li><img src="../images/1.jpg" alt=""><h3>电视</h3><p>电视真好汗啊</p><strong>9999¥</strong></li>'
// ``模板字符串
res += `
<li>
<img src="${val.img}" alt="">
<h3>${val.goods}</h3>
<p>${val.title}</p>
<strong>${val.price}</strong>
</li>
`
})
$('list').innerHTML = res;
}
function $(id) {
return document.getElementById(id)
}
表单验证
<p>用户要求:名字只能包含数字、字母,数字不可以开头,长度不低于6,不长于12 [0-9 a-z A-Z]</p>
<p>
<label for="user">用户名</label>
<input id="user" name="user" type="text">
<span id="user_span">错误提示</span>
</p>
<p>密码6-12位 不能包含特殊字符 (弱 z/1/A 中 包含两种 强 包含三种)</p>
<p>
<label for="pwd">密码</label>
<input id="pwd" name="pwd" type="text">
<span id="pwd_span"></span>
</p>
<p>确认密码 两次输入相同</p>
<p>
<label for="repwd">确认密码</label>
<input id="repwd" name="repwd" type="text">
<span id="repwd_span"></span>
</p>
<p>随机4位验证码(点击切换验证码)</p>
<p>
<label for="code">验证码</label>
<input id="code" name="code" type="text">
<span id="random_code"></span>
<button id="refresh">刷新验证码</button>
<span id="code_span"></span>
</p>
<button id="btn">注册</button>
<script>
var numArr = [];
for (var i = 0; i < 10; i++) {
numArr.push(i)
}
var bigArr = [];
for (var i = 65; i <= 90; i++) {
bigArr.push(String.fromCharCode(i))
}
var smallArr = [];
for (var i = 97; i <= 122; i++) {
smallArr.push(String.fromCharCode(i))
}
var allArr = numArr.concat(bigArr, smallArr);
console.log(allArr);
$('btn').onclick = function () {
// 验证用户名
var uname = $('user').value;
// 是否输入
if (!uname) {
$('user_span').innerHTML = '输入不能为空';
$('user_span').style.color = 'red'
return
}
// 输入长度
if (uname.length < 6 || uname.length > 12) {
$('user_span').innerHTML = '长度必须在6-12之间';
$('user_span').style.color = 'red'
return
}
// 输入的第一个是不是数字
if (!isNaN(uname[0])) {
$('user_span').innerHTML = '第一位不能是数字';
$('user_span').style.color = 'red'
return
}
// 是否有非法字符 遍历整个用户名
for (var i = 0; i < uname.length; i++) {
if (allArr.indexOf(uname[i]) == -1) {
$('user_span').innerHTML = '只能由数字和字母组成';
$('user_span').style.color = 'red'
return
}
}
$('user_span').innerHTML = '√';
$('user_span').style.color = 'green'
// 验证密码
// 验证码
}
function $(id) {
return document.getElementById(id)
}
day11
延时器改为定时器
函数递归,自调用
延时器相对定时器,更加灵活,延时器可以指定,第一次为某个时间出发,之后,当这个延时器结束后,再隔多长时间触发
//第一次调用
setTimeout(fn, 4000);
function fn() {
//操作
console.log(666);
//再次调用自己
setTimeout(fn, 2000); //每两秒调用自己
}
fn();
function fn() {
//操作
console.log(666);
//再次调用自己
setTimeout(fn, 2000); //每两秒调用自己
}
定时器bug
- 异步 定时器与延时器异步程序
- 定时器叠加问题
- 全局变量和局部变量理解
定时器叠加问题
定时器会进行叠加,每次点击按钮,都会触发一个定时器,以至于–速度变快
var btn = document.getElementById("btn");
btn.onclick = function () {
var timer = setInterval(function () {
btn.innerHTML--;
}, 1000);
}
需要在点击时,关闭前一个定时器
var btn = document.getElementById("btn");
btn.onclick = function () {
clearInterval(timer);
var timer = setInterval(function () {
btn.innerHTML--;
}, 1000);
}
此时关闭前一个定时器失败,因为timer为局部变量,相当于一下代码
btn.onclick = function () {
var timer;
clearInterval(timer);
timer = setInterval(function () {
btn.innerHTML--;
}, 1000);
}
需将定时器变为全局变量
var btn = document.getElementById("btn");
var timer;
btn.onclick = function () {
clearInterval(timer);
timer = setInterval(function () {
btn.innerHTML--;
}, 1000);
}
BOM
js:ECMAscript(基础语法,所有的浏览器都支持基础语法) + BOM(浏览器) + DOM(标签)
BOM : browser object model
window对象
window对象 属于浏览器的内置对象 打开一个浏览器也就生成一个window对象
不同的浏览器窗口不共享window对象
- 变量本质上是window对象的自定义属性
- 函数本质上是window对象的自定义方法
<script>
//window的自定义属性
var a = 'hello';
console.log(a);
console.log(window.a);
//window的自定义方法
function fn() {
console.log(666);
}
fn();
window.fn();
</script>
BOM常见属性
navigator
浏览器名称等相关信息
userAgent属性
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
可用其判断手机型号
<script>
console.log(navigator);
console.log(navigator.userAgent); // Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
var type = navigator.userAgent;
if (type.includes('Android')) {
console.log('安卓');
}
if (type.includes('iPhone')) {
console.log('爱疯');
}
if (type.includes('Windows')) {
console.log('Windows电脑');
}
</script>
history
历史记录
DOM
- document (整个文档)
- document.documentElement (html)
- document.doctype(文档类型)
- document.head (头部)
- document.body (body)
- document.title (可读可写)
console.log(document);
console.log(document.documentElement);
console.log(document.doctype);
console.log(document.head);
console.log(document.body);
console.log(document.title);
day12
BOM的方法
window.open
打开页面(会在新窗口打开页面【与a标签的target=_blank一致】)
setTimeout(function () {
//在新窗口中打开 自动拦截
window.open("http://www.mi.com");
}, 2000);
window.close()
关闭当前窗口
setTimeout(function () {
close();
}, 2000);
Window对象的事件
window.onfocus
获取焦点事件
window.onfocus = function () {
console.log("获取");
}
window.onblur
失去焦点
window.onblur = function () {
console.log("失去焦点");
}
页面打开的时候,页面没有焦点(既没有失去,也没有获取)
window.onscroll
滚动条滚动事件
滚动事件是一个频繁触发的事件
window.onscroll = function () {
console.log(666);
}
window.resize
window.onresize = function () {
console.log(666);
}
高频率触发事件,性能差 解决方法:每300毫秒监听一次
window.onscroll = function () {
setTimeout(function () {
console.log(666);
}, 3000)
}
onload
等页面资源加载完毕之后,再执行函数
<script>
//等页面所有资源加载完毕之后,再执行函数
window.onload = function () {
console.log(document.getElementById("test").innerHTML);
}
</script>
</head>
<body>
<div id="test">BLACK</div>
<script>
//先加载
console.log(document.getElementById("test").innerHTML + "**");
</script>
DOM
DOM:document object model 有兼容问题
页面的几种宽高
- 浏览器的宽高
- 页面的实际宽高
- 页面被卷取的宽高
clientWidth
浏览器可用宽度(可视宽高)
- clientWidth
- clientHeight
<style>
body {
height: 3000px;
width: 3000px;
}
</style>
<script>
//浏览器可视宽高(跟随窗口大小)
console.log(document.documentElement.clientWidth);
console.log(document.documentElement.clientHeight);
</script>
scrollHeight
页面实际宽高
<script>
//浏览器可视宽高
console.log(document.documentElement.clientWidth);
console.log(document.documentElement.clientHeight);
//页面实际宽高
console.log(document.documentElement.scrollHeight + "*"); //3000
console.log(document.documentElement.scrollWidth + "*"); //2000
</script>
scrollTop
被卷取的高度/宽度
- scrollTop
- scrollLeft
//被卷取的高度/宽度
console.log(document.documentElement.scrollTop);
console.log(document.documentElement.scrollLeft);
兼容问题
浏览器的不同,或文档类型未申明时,获取方式不同
有时需要用document.docuentElement,有时需要docuent.body.clientHeight…
文档申明
<!DOCTYPE html>
兼容写法
//兼容写法
var root = document.documentElement || document.body;
console.log(root.scrollHeight);
console.log(root.clientHeight);
console.log(root.scrollTop);
返回顶部
<script>
//回到顶部
window.onload = function () {
var oA = document.getElementById("back");
var root = document.documentElement || document.body;
oA.onclick = function () {
//定时器需要被关闭
var t = setInterval(function () {
root.scrollTop -= 50;
//滚动条到达顶部时,清除定时器
if (root.scrollTop <= 0) {
clearInterval(t);
//滚动条的位置置零,否则可能 影响下一次点击事件
root.scrollTop = 0;
}
}, 10);
}
}
</script>
<body>
<!-- 阻止a标签的跳转 -->
<a href="javascript:;" id="back">返回顶部</a>
</body>
Dom元素获取
获取元素
getElementById
通过id获取元素
获取第一次出现的元素
<div id="a">1</div>
<div id="a">2</div>
<script>
console.log(document.getElementById("a")); // <div id="a">1</div>
</script>
getElementsByClassName()
通过类名获取
获取的数组(伪数组)
即使只有一个元素,获取的也是伪数组
<p class="p">c</p>
<p class="p">d</p>
<script>
//得到的是一个数组 (伪数组,长度不可修改)
//length = 2
console.log(document.getElementsByClassName('p')); //HTMLCollection(2) [p.p, p.p]]
console.log(document.getElementsByClassName('p')[0].innerHTML); //c
</script>
getElementsByTagName
通过标签获取元素
获取的也是伪数组
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
var oLis = document.getElementsByTagName('li');
console.log(oLis); //HTMLCollection(5) [li, li, li, li, li
getElementsByName
获取到的也是伪数组
有兼容问题,并且并不是所有标签都有name属性
<input type="text" name="username">
<input type="text" name="pwd">
<script>
//同样得到的是数组
console.log(document.getElementsByName('pwd'));
querySelector
ES6新增
拿到第一次出现的元素
<div id="a">a</div>
console.log(document.querySelector('#a'));
console.log(document.querySelector('#a').innerHTML); //a
querySelector永远只能查到一个元素
<div>1</div>
<div>2</div>
<script>
console.log(document.querySelector('div')); //<div>1</div>
querySelectorAll
通过选择器,查询到所有符合条件的DOM
返回的是数组
<div>1</div>
<div>2</div>
<script>
console.log(document.querySelectorAll('div')); //NodeList(2) [div, div]
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
//获取第三个li
var oLi = document.querySelector('ul li:nth-child(3)');
console.log(oLi);
//拿到 2,3,4,5 li
// var oLis = document.querySelectorAll('ul li +li'); //兄弟选择器
var oLis = document.querySelectorAll('ul li:not(:first-child)'); //兄弟选择器
console.log(oLis);
DOM内容操作
- value
- innerHTML 可解析标签
- innerText (无法解析标签)
<h1>xx</h1>
<script>
var oH1 = document.querySelector('h1');
oH1.innerHTML = '<span>新增的</span>';
</script>
<h1>xx</h1>
<script>
var oH1 = document.querySelector('h1');
oH1.innerText = '<span>新增</span>'
</script>
DOM属性操作
atrributes
对象
拿到所有的属性
<a href="#" target='_blank' title='鼠标悬停显示' kkk='哈哈哈'></a>
<script>
var ele = document.querySelector('a');
//对象
// NamedNodeMap {0: href, 1: target, 2: title, 3: kkk, href: href, target: target, title: title, kkk: kkk, length: 4}
console.log(ele.attributes);
console.log(ele.attributes.title); //title='鼠标悬停显示'
console.log(ele.attributes.target); //target='_blank'
console.log(ele.attributes.kkk); //kkk='哈哈哈'
直接写法
元素.属性
- 自有属性可直接获取
- 自定义属性拿不到,不可获取 undefined
- 获取class属性,需要用 className(ele.className)
<a href="#" target='_blank' title='鼠标悬停显示' kkk='哈哈哈'></a>
<script>
var ele = document.querySelector('a');
//直接拿到值
console.log(ele.title); //鼠标悬停显示
//不可以获取自定义属性
console.log(ele.kkk); //undefined
getAttribute
元素.getAtrribute(‘属性’)
- 可获取自定义属性
<a href="#" target='_blank' title='鼠标悬停显示' kkk='哈哈哈'></a>
<script>
var ele = document.querySelector('a');
console.log(ele.getAttribute('title')); //鼠标悬停显示
console.log(ele.getAttribute('kkk')); //哈哈哈
获取属性总结
- attributes 拿带所有属性,对象
- attributes.属性 可以拿到任意的属性 (属性 = 属性值)
- getAttribute(‘属性’)
- 可以拿到所有的属性值
- ele.属性
- 只能拿到自有属性 ele.className ,ele.id
- 拿不到自定义属性
设置类属性
#red {
color: red;
}
</style>
</head>
<body>
<a href="javascript:;" class='head' target='_blank' title='鼠标悬停显示' kkk='哈哈哈'>xx</a>
<script>
var a = document.querySelector('a');
a.onclick = function () {
a.id = 'red';
//直接替换原来的class属性
a.className = 'test';
}
.head {
font-size: 40px;
}
.b {
background-color: pink;
}
</style>
</head>
<body>
<a href="javascript:;" class="head" title='鼠标悬停显示' kkk='哈哈哈'>xx</a>
<script>
var a = document.querySelector('a');
a.onclick = function () {
//(替换或新增)会直接替换原来的class属性值
// a.className = 'b';
//添加类名 += ' 类名'
a.className += ' b'
}
classList
问题引入
实现移除一个类名
<p class="a b c">哈哈</p>
<script>
var oP = document.getElementsByTagName('p')[0];
oP.className -= 'c'; // <p class="NaN">哈哈</p>
ele.classList
- 是一个数组
var oP = document.getElementsByTagName('p')[0];
//字符串(可替换或拼接 )
console.log(oP.className); //a b c
//数组(可删除、新增、替换。。。)
console.log(oP.classList); //DOMTokenList(3) ["a", "b", "c", value: "a b c"]
使用
- add(类名) 新增类
- remove() 移除类
- replace(旧类名,新类名) 替换类
<p class="a b c">哈哈</p>
<script>
var oP = document.getElementsByTagName('p')[0];
//添加类名
oP.classList.add('black');
//删除类名
oP.classList.remove('c');
//替换(将black类替换成pink类)
oP.classList.replace('black', 'pink');
ele.自定义属性
var bn = document.getElementById('bn');
//不会添加到dom元素上,但是有,是一个属性
bn.index = 'black';
console.log(bn.index); //black
//因为没有添加到dom元素上,所以获取不到
console.log(bn.getAttribute('index')); //null
console.log(bn.getAttribute('test')); //pink
循环绑定事件
事件是异步的
<ul>
<li class="item active">所有</li>
<li class="item">好评</li>
<li class="item">有图</li>
<li class="item">差评</li>
</ul>
<script>
var oLis = document.querySelectorAll('li');
for (var i = 0; i < oLis.length; i++) {
//循环绑定点击事件,而for循环已经执行完,是同步事件,i = 4
//事件的执行是异步的, oLis[i].onclick 还是同步的
oLis[i].onclick = function () {
//函数里面的代码是异步的
//在事件里面,this指代触发事件的对象
// console.log(this);
// 事件是异步的
console.log(i); //都是4
}
}
console.log(666);
先打印666
点击事件后执行
简单选项1
<ul>
<li class="item active">所有</li>
<li class="item">好评</li>
<li class="item">有图</li>
<li class="item">差评</li>
</ul>
<script>
var oLis = document.querySelectorAll('li');
for (var i = 0; i < oLis.length; i++) {
oLis[i].onclick = function () {
//遍历循环,取消之前的active类
for (var i = 0; i < oLis.length; i++) {
oLis[i].classList.remove('active');
}
//点谁给谁添加active类
//使用this,动态添加active类
this.classList.add('active');
};
}
选项卡
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
.tab_hd {
display: flex;
width: 300px;
margin: 0 auto;
margin-top: 50px;
}
.tab_hd li {
width: 100px;
text-align: center;
line-height: 2;
background-color: #ddd;
}
.tab_bd {
width: 300px;
margin: 0 auto;
font-size: 30px;
}
.tab_bd li {
width: 300px;
height: 100px;
background-color: pink;
}
.tab_bd li {
/* 隐藏选项卡 */
display: none;
}
.tab_hd .active {
background-color: pink;
}
.tab_bd .show {
display: block;
}
</style>
</head>
<body>
<ul class="tab_hd">
<li class="active">1</li>
<li>2</li>
<li>3</li>
</ul>
<ul class="tab_bd">
<li class="show">1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var oLis1 = document.querySelectorAll('.tab_hd li');
var oLis2 = document.querySelectorAll('.tab_bd li')
//循环绑定事件
for (var i = 0; i < oLis1.length; i++) {
//所有头部选项卡添加一个自定义属性,设置角标
oLis1[i].index = i; //此方法不会添加到dom属性上,通过getAttribute获取不到
oLis1[i].onclick = function () {
//移除之前的类名
for (var j = 0; j < oLis1.length; j++) {
oLis1[j].classList.remove('active');
//移除之前所有的 .show
oLis2[j].classList.remove('show');
}
this.classList.add('active');
console.log(this.getAttribute('index'));
//this对应的li要显示,找角标
oLis2[this.index].classList.add('show');
}
}
</script>
DOM自有属性
- 元素设置自有属性,会在dom标签上显示
- 设置自定义属性,不会在dom标签上显示,只能读取
<p id='a' class="b">哈哈</p>
<script>
var oP = document.querySelector('p');
//会自动添加到标签上面并且显示
oP.title = '鼠标悬停显示';
//自定义属性,也会添加属性,但是不会显示在标签上
oP.haha = '哈哈哈哈';
console.log(oP.haha); //哈哈哈哈
var obj = new Object();
obj.name = 'pink';
setAttribute
给dom设置自有属性,并且会在dom标签上显示
<p id='a' class="b">哈哈</p>
var oP = document.querySelector('p');
oP.setAttribute('hehe', "呵呵");
<p id='a' class="b">哈哈</p>
<script>
var oP = document.querySelector('p');
//在dom元素上显示
oP.title = '鼠标悬停显示';
//自定义属性,在dom上不显示,但是可以读到
oP.haha = '哈哈哈哈';
console.log(oP.haha); //哈哈哈哈
//在标签上添加hehe属性
oP.setAttribute('hehe', "呵呵");
console.log(oP.hehe); //undefined
console.log(oP.getAttribute('hehe')); //hehe
console.log(oP.getAttribute('haha')); //null
removeAttribute
删除自定义属性
oP.removeAttribute('hehe');
dom属性小结
-
获取所有的标签属性 attributes
-
获取单个的属性值
- getAttribute() 可以获取到自定义属性和自有属性
- 获取自有属性:ele.属性 (ele.id,ele.title)
- 获取自定义属性 getAttribute()
-
添加属性
-
setAttribute()可以同时设置自定义属性和自有属性
-
设置自有属性 ele.id = ‘a’
-
设置自定义属性
- ele.setAttribute() 会在标签上显示。 必须通过getAttribute得到
- ele.自定义属性 = 属性值 。属性不会在标签上显示,可以读取
-
dom特殊属性
-
disabled 按钮禁用
- true
- false
-
checked 复选框选中
true
false
按钮倒计时
<input type="button" value='按钮'>
btn.onclick = function () {
//按钮禁用
btn.disabled = true;
this.value = 10;
var timer = setInterval(function () {
btn.value--;
if (btn.value == 0) {
clearInterval(timer);
this.disabled = false;
btn.value = '按钮';
}
}, 1000);
}
复选框选中
var checkBox = document.querySelector('input[type="checkbox"]')
setTimeout(function () {
checkBox.checked = true;
}, 2000)
select
<select id="sec">
<option value="1">1</option>
<option value="2" selected>2</option>
<option value="3">3</option>
</select>
全选
<body>
全选<input type="checkbox" class="checkAll"> <br>
JENNIE<input type="checkbox" class="checkOne">
LISA<input type="checkbox" class="checkOne">
JISOO<input type="checkbox" class="checkOne">
ROSIE<input type="checkbox" class="checkOne">
<script>
var checkAll = document.querySelector('.checkAll');
var checkOnes = document.querySelectorAll('.checkOne');
checkAll.onclick = function () {
for (var i = 0; i < checkOnes.length; i++) {
checkOnes[i].checked = this.checked;
}
};
//反选---当子选项有一个不选择,全选按钮取消,全选时,全选按钮勾选
for (var i = 0; i < checkOnes.length; i++) {
//给每个checkbox绑定事件
checkOnes[i].onclick = function () {
//判断是不是每个都选中
for (var j = 0; j < checkOnes.length; j++) {
if (!checkOnes[j].checked) {
break;
}
}
//全部选中
//数组全部遍历完,所有checkOne都为选中状态
console.log(j == checkOnes.length);
// if (j == checkOnes.length) {
// //全新框勾选
// checkAll.checked = true;
// } else {
// checkAll.checked = false;
// }
// checkAll.checked = j == checkOnes.length ? true : false;
checkAll.checked = j == checkOnes.length;
}
}
day13
回顾
//BOM事件
// focus
// blur
// scroll 高频率触发事件
// onload 页面资源加载完毕
// resiaze 高频率触发的事件
//节流和防抖 每300ms监听一次
//淘宝适配 flexible.js
//动态设置了meta
//动态设置dpr
//防止屏幕过大,导致的文字过大
//onresize的延迟监听
//移动端适配的实现
//动态设置 根元素 html的字体大小 (100vw/7.5)rem
//js实现:document.documentElement.font-size = clientWidth/7.5;
// DOM节点 head body title documentElement doctype
//元素的获取
//ES6元素获取 querySelector querySelectorAll
//DOM元素的内容 value innerText innerHTML
//DOM元素的属性
// 自有属性 src href className classList
//自定义属性 setAttribute(data,dataValue)
//给对象添加自定义属性 ele.data = dataValue
//DOM元素的特殊属性--表单
//disabled
//checked
//selected
//全选和反选
//选项卡
到达底部判断
var root = document.documentElement || document.body;
window.onscroll = function () {
console.log(88);
if (root.scrollTop >= (root.scrollHeight - root.clientHeight)) {
alert('到达底部');
}
}
选项卡foreach
<ul class="tab_hd">
<li class="active">1</li>
<li>2</li>
<li>3</li>
</ul>
<ul class="tab_bd">
<li class="show">1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var oLis1 = document.querySelectorAll('.tab_hd li');
var oLis2 = document.querySelectorAll('.tab_bd li');
//使用forEach遍历
//此处没有异步,角标bug
oLis1.forEach(function (val, j) {
val.onclick = function () {
//清除原有
oLis1.forEach(function (item, i) {
item.classList.remove('active');
oLis2[i].classList.remove('show');
});
val.classList.add('active');
oLis2[j].classList.add('show');
}
})
</script>
角标不会因为异步而发生改变。这与foreach封装方法有关
//i处在函数内部,属于局部变量
function foreach(arr, callback) {
for (var i = 0; i < arr.length; i++) {
callback(arr[i],i,arr);
}
}
选项卡
<style>
* {
padding: 0;
margin: 0;
list-style: none;
}
.tab_hd {
display: flex;
width: 300px;
margin: 50px auto;
}
.tab_hd li {
width: 100px;
text-align: center;
line-height: 2;
background-color: #eee;
}
.tab_hd .active {
background-color: pink;
}
.tab_bd {
width: 300px;
height: 300px;
margin: 50px auto;
}
.tab_bd li {
width: 300px;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
background-color: #f00;
}
.tab_bd li {
display: none;
}
.tab_bd .show {
display: flex;
}
</style>
</head>
<body>
<ul class="tab_hd">
<li class="active">11</li>
<li>22</li>
<li>33</li>
</ul>
<ul class="tab_bd">
<li class="show">111111</li>
<li>222222</li>
<li>333333</li>
</ul>
<script>
var oLis1 = document.querySelectorAll('.tab_hd li');
var oLis2 = document.querySelectorAll('.tab_bd li');
// 循环绑定点击事件
oLis1.forEach(function (val, j) {
val.onclick = function () {
oLis1.forEach(function (item, i) {
item.classList.remove('active');
oLis2[i].classList.remove('show');
})
val.classList.add('active');
oLis2[j].classList.add('show');
}
})
// 全局变量 操作同一个i
// for (var i = 0; i < oLis2.length; i++) {
// // i产生覆盖
// console.log(i)
// }
// var i = 0;
// while (i < oLis2.length) {
// console.log(i);
// i++
// }
function forEach(arr, fn) {
for (var i in arr) {
fn(arr[i], i, arr)
}
}
// i处在函数内部,属于局部变量
forEach(oLis2, function (val, i) {
// 内部声明了一个i
console.log(i)
})
</script>
DOM样式修改
style.样式
ele.style.样式—修改行内样式,驼峰名法
var oH = document.querySelector('#h');
oH.style.background = 'red';
// 驼峰命名法
oH.style.fontSize = '30px';
cssText
也是添加行内样式,会覆盖之前的行内样式
oH.style.cssText = 'background:red;font-size:30px';
添加类名
.a {
background: red;
font-size: 30px;
}
oH.classList.add('a')
获取样式
-
style获取
只能获取行内样式,拿不到css中的样式
console.log(oH.style.background);
-
getComputedStyle(ele.width)
console.log(getComputedStyle(oH).width) console.log(getComputedStyle(oH)['backgroundColor']) console.log(getComputedStyle(oH)['background'])
兼容IE8及以下获取样式的方法
oH.currentStyle.width console.log(oH.currentStyle.width)
兼容写法
// 封装获取样式的兼容简写
function getStyle(ele, property) {
if (getComputedStyle) {
return getComputedStyle(ele)[property]
}
return ele.currentStyle[property]
}
dom操作
查询
- getElementById
- getElementByClassName
- getElementByTagName
- querySelector
- querySelectorAll
找父元素
- parentNode
找子元素
-
childNodes 所有的子节点(包含换行、文本和注释)
-
children 所有的标签节点
-
firstChild 找到第一个结点
-
firstElementChild 找到第一个标签节点
-
lastChild 找到最后一个结点
-
lastElementChild 找到最后一个标签节点
找兄弟
- 相邻兄弟节点
- previousSibling
- nextSibling
- 相邻标签节点
- previousElementSibling
- nextElementSibling
节点
// 节点类型 nodeType 返回数字
// 标签 1
// 文本 3
// 注释 8
//属性节点 2
// 节点名字 nodeNae
// 标签 SPAN 返回标签名称大写
// 文本 #text
// 注释 #comment
// 节点的值 nodeValue
// 标签 null
// 文本 文本的内容
// 注释 注释的内容
克隆节点
// cloneNode 克隆节点
// 默认是false ,也就是只克隆节点
var res = h.cloneNode();
console.log(res); //<h1 id="h"></h1> 没有克隆里面的子元素
dom操作
新增
// 创建标签
// innerHTML 替换原来的所有内容
// document.createElement 创建标签
// document.createTextNode 创建文本结点
// 新增
// appendChild 最后新增子元素
// insertBefore(new,old),把new结点插入到old前面
删除
// dom删除
// innertHTML = ''; 删除所有子元素
// remove() 删除包括自己
// removeChild(ele) 删除指定子元素,不能删除孙子 (只能删除子元素)
替换
// DOM 修改
// replaceChild(new,old) 替换 只能替换子节点 ,不能替换孙子
day14
留言板
<body>
<div class="bacground">
<div class="head">留言板</div>
<input class="name" type="text" placeholder="请输入您的昵称">
<textarea class="content" placeholder="请保持言论文明......"></textarea>
<button class="btn">留言</button>
<h3>大家在说</h3>
<ul class="text">
<!-- <li>
<div class="message-head">
<span class="photo">系统</span>
<p class="time">2019-9-27 0:47:38</p>
</div>
<p class="liuyan">测试留言</p>
<div class="reply">
<p>测试回复</p>
</div>
<button class="delete">Delete</button>
<button class="answer">Answer</button>
</li> -->
</ul>
</div>
<!-- 弹窗 -->
<div class="popup">
<div class="pop-content">
<div class="pop-head">回复板</div>
<textarea class="pop-reply" placeholder="请保持言论文明......"></textarea>
<button class="pop-btn1">回复</button>
<button class="pop-btn2">取消</button>
</div>
</div>
<script>
$('.btn').onclick = function () {
//判断留言和昵称是否为空
var nickName = $('.name').value;
var content = $('.content').value;
if (!nickName || !content) {
alert('请输入内容');
return;
}
$('.name').disabled = true;
// 清空内容
$('.content').value = '';
//按钮倒计时
toTime();
//创建留言
createContent(nickName, content);
};
// /删除留言
function delContent() {
//遍历所有 留言
var dels = document.querySelectorAll('.delete');
dels.forEach(function (val, i) {
val.onclick = function () {
// 删除留言
this.parentNode.remove();
}
});
}
// 回复
function replayContent() {
//给每个回复按钮点击 事件
var reps = document.querySelectorAll('.answer');
// 遍历绑定事件
reps.forEach(function (val, i) {
// val代表 回复按钮
val.onclick = function () {
//弹窗出现
$('.popup').style.display = 'flex';
//点击取消
$('.pop-btn2').onclick = function () {
$('.popup').style.display = 'none';
//清空留言板内容
$('.pop-reply').value = '';
return;
}
// 点击了回复
$('.pop-btn1').onclick = function () {
//判断回复内容是否为空
var replay = $('.pop-reply').value;
if (!replay) {
alert('留言不能为空');
return;
}
//找到留言的地方
// 回复按钮的前一个的前一个兄弟
// console.log(this);
// val代表正在 遍历的回复按钮
var rep = val.previousElementSibling.previousElementSibling;
// console.log(rep);
var p = document.createElement('p');
p.innerText = replay;
// 将p加到对应的留言区中
rep.insertBefore(p, rep.firstElementChild);
// 将回复关闭
$('.popup').style.display = 'none';
//清空留言板内容
$('.pop-reply').value = '';
}
}
})
}
// 创建留言
function createContent(nickName, content) {
var oLi = document.createElement('li');
oLi.innerHTML = `
<div class="message-head">
<span class="photo">${nickName}</span>
<p class="time">${dateFormat()}</p>
</div>
<p class="liuyan">${content}</p>
<div class="reply">
</div>
<button class="delete">Delete</button>
<button class="answer">Answer</button>
`;
$('.text').insertBefore(oLi, $('.text').firstElementChild);
//删除留言
delContent();
//回复
replayContent();
}
function dateFormat() {
var date = new Date();
var res = + date.getFullYear() + "-" + date.getMonth() + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ":" + date.getSeconds();
return res;
}
// 倒计时
function toTime() {
$('.btn').disabled = true;
var count = 10;
var timer = setInterval(function () {
$('.btn').innerHTML = --count + '秒后留言';
if (count == 0) {
clearInterval(timer);
$('.btn').innerHTML = '留言';
$('.btn').disabled = false;
}
}, 300);
}
function $(qs) {
return document.querySelector(qs);
}
</script>
盒子宽高问题
- getComputedStyle(oDiv)[‘width’] :200px 返回盒子CSS设置的宽度,带px
- clientWidth :width+padding
- offsetWidth(offsetHeight) :width+padding+border
- offsetLeft (offsetTop):距离最近的具有定位的祖先元素的距离
.a {
width: 200px;
height: 200px;
padding: 30px;
border: 10px solid #000;
margin: 100px;
}
.box {
width: 400px;
height: 400px;
background-color: red;
position: relative;
left: 50px;
}
</style>
</head>
<body>
<div class="box">
<div class="a"></div>
</div>
<script>
// 盒子的宽高问题
var oDiv = document.querySelector('.a')
//CSS设置的宽度
console.log(getComputedStyle(oDiv)['width']); //200px
//clientWidth:width+padding
console.log(oDiv.clientWidth); //260
//offsetWidth:width+padding+border
console.log(oDiv.offsetWidth); //280
//offsetLeft 相对于最近的具有定位的父元素的距离
console.log(oDiv.offsetLeft); //100
事件
事件:事件发生并得到处理。
事件的三要素
- 事件源
- 事件类型
- 事件处理函数
绑定事件的方式
- 行内js (0级事件)
- οnclick=function(){} (0级事件)
- 事件监听 (2级事件)
方式一
var oP = document.querySelector('p');
oP.onclick = function () {
console.log(this.innerText);
}
行内JS
<p onclick="fn(this)">1</p>
function fn(a) {
console.log(999);
console.log(a.innerText);
}
<body>
<p onclick="fn(this)">1</p>
<script>
function fn(a) {
console.log(999);
// console.log(a.innerText);
}
//同时出现时,fn只是指向,这个函数会覆盖fn,因为同是0级事件
var oP = document.querySelector('p');
oP.onclick = function () {
console.log(666);
// console.log(this.innerText);
}
</script>
事件监听
事件不会发生覆盖。dom2级事件
<p onclick="fn(this)">1</p>
var oP = document.querySelector('p');
// 添加事件监听
// 参数:1.事件类型,事件处理函数
oP.addEventListener('click', function () {
console.log(444);
console.log(this.innerText);
});
oP.onclick = function () {
console.log(666);
}
先打印666,在打印444
oP.addEventListener('click', function () {
console.log(444);
console.log(this.innerText);
});
oP.addEventListener('click', function () {
console.log(1010);
});
oP.onclick = function () {
console.log(666);
}
IE
oP.attachEvent('onclick', function () {
console.log('IE');
});
兼容
// 兼容写法
// 参数:dom,事件类型,监听函数
function addEvent(ele, type, fn) {
if (window.addEventListener) {
ele.addEventListener(type, fn);
} else {
ele.attachEvent('on' + type, fn);
}
}
使用
var oP = document.querySelector('p');
addEvent(oP, 'click', function () {
console.log('xxx');
})
dom0级事件和2级事件
在dom0级事件处理中,后定义的事件处理会覆盖前面的。
在dom2级事件处理中,对一个按钮点击的事件处理就没有被覆盖掉。所以,dom0级和dom2级事件处理,在形式上和功能上都是有差异的。这就是dom0级和dom2级事件最简单也最常用的不同之处了
0级事件:行内js与ele.onclick = function(){}
2级事件:dom.addEventListener
事件的移除
-
DOM 0级事件的移除 onclick = null
-
DOM 2级事件的移除 在绑定事件监听时必须使用具名函数,移除时使用removeEventListener(移除事件类型,函数),需要一个一个的移除
removeEventListener(‘click’,fn);
<p id="p">11</p>
<script>
// 事件的移除
p.onclick = function () {
console.log(888);
}
p.addEventListener('click', fn1);
p.addEventListener('click', fn2);
function fn1() {
console.log(222);
}
function fn2() {
console.log(333);
}
// 五秒后事件移除
setTimeout(function () {
// 事件的移除,不能清除2级事件(addEventListener)
p.onclick = null;
// 移除监听事件:参数:移除的事件类型,函数
p.removeEventListener('click', fn1);
p.removeEventListener('click', fn2);
}, 5000)
</script>
兼容写法
function removeEven(ele, type, fn) {
if (window.removeEventListener) {
ele.removeEventListener(type, fn);
} else {
// IE解除事件方式
ele.detachEvent('on' + type, fn);
}
}
使用
removeEven(p, click, fn1);
事件对象
事件对象:浏览器用于记录事件发生的整个过程
获取事件对象的方式:事件处理函数的第一个参数
<h1 id="h">2333</h1>
document.onclick = function (e) {
console.log(e);
console.log(e.target); //html
console.log(222);
}
h.onclick = function (e) {
console.log(e);
console.log(e.target); //h1
console.log(333);
}
IE写法
IE不需要参数,直接使用event
h.onclick = function () {
console.log(event);
}
关于targetIE写法
- 普通浏览器
h.onclick = function (e) {
console.log(e.target); //h1
}
- IE
h.onclick = function (e) {
var ev = e || event;
// console.log(ev.target);
// IE中target使用srcElement
console.log(ev.srcElement);
}
兼容写法
h.onclick = function (e) {
var ev = e || event;
}
target兼容写法
IE浏览器event没有target属性
h.onclick = function (e) {
var ev = e || event;
var target = ev.target || ev.srcElement;
console.log(target);
}
target
事件对象的target能够准确记录哪个元素的事件被触发
<p>111</p>
<h1>blackpink</h1>
<div>hahahah</div>
document.onclick = function (e) {
var ev = e || event;
console.log(ev.target);
}
tagName
元素的tagName为元素标签大写
<h1 id="h1" class="black">blackpink </h1>
tagName-->H1
自定义属性
通过自定义属性判断触发元素
<div pink='black'>hahahah</div>
document.onclick = function (e) {
var ev = e || event;
var target = e.target;
// 通过自定义属性判断元素
if (target.getAttribute('pink') == 'black') {
console.log(target.innerHTML);
}
}
事件机制和事件流
事件流:事件流描述的是从页面中接受事件的顺序
IE和网景提出两种相反事件流概念
-
IE的时间里是事件冒泡流(event bubble)
即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点
-
网景是事件捕获流
不具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件
-
事件冒泡:事件由内而外触发
-
事件捕获:事件由外而内触发
事件流包含三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段
事件冒泡
<div class="a">
<div class="a1">
</div>
</div>
<script>
// 事件机制和事件流
// 事件冒泡
// 先具体元素,后不具体元素
var oA = document.querySelector('.a');
var oA1 = document.querySelector('.a1');
oA.onclick = function () {
alert('父元素');
}
oA1.onclick = function () {
alert('子元素');
}
document.onclick = function () {
alert('文档对象');
}
</script>
事件捕获
事件捕获:事件由外而内触发
事件监听的第三个参数为true(默认为false,事件冒泡)
// 事件捕获:
// 事件监听的第三个参数,设置为true
a.addEventListener('click', function () {
alert('父');
}, true)
a1.addEventListener('click', function () {
alert('子');
}, true)
document.addEventListener('click', function () {
alert('html');
}, true)
阻止事件冒泡
// 阻止事件冒泡:不希望a1的点击事件向外扩散
var a = document.querySelector('.a');
var a1 = document.querySelector('.a1');
a.onclick = function () {
console.log('a');
}
a1.onclick = function (e) {
var ev = e || event;
// 阻止事件冒泡
ev.stopPropagation();
console.log('a1');
}
兼容写法
// 阻止事件冒泡:不希望a1的点击事件向外扩散
var a = document.querySelector('.a');
var a1 = document.querySelector('.a1');
a.onclick = function () {
console.log('a');
}
a1.onclick = function (e) {
var ev = e || event;
console.log('a1');
// 阻止事件冒泡
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
// 兼容IE
ev.cancelBubble = true;
}
}
事件委托
现阶段弊端
- 事件处理函数越多,执行效率越低
- 如果一个标签在未来才会出现,不能绑定事件
委托
- 可以对未生成的DOM绑定事件
- 事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件
事件委托基于事件冒泡
原本,
<div class="a">
<p id="p">11</p>
</div>
动态添加h1标签,并给其绑定事件
setTimeout(function () {
var a = document.querySelector('.a');
var h1 = document.createElement('h1');
h1.innerText = '2333';
a.appendChild(h1);
//给新添加的元素绑定事件
var oH1 = document.querySelector('h1');
oH1.onclick = function () {
console.log(this.innerText);
}
}, 2000)
后动态生成元素无法绑定事件
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
</ul>
var oLis = document.querySelectorAll('li');
oLis.forEach(function (val, i) {
val.onclick = function () {
alert(this.innerText)
}
})
setTimeout(function () {
var ul = document.querySelector('ul');
var li = document.createElement('li');
li.innerHTML = '2s后出现的';
ul.appendChild(li);
}, 4000)
优点
- 减少事件处理函数
- 给未来的元素添加事件绑定
<ul>
<p>1o1</p>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 4s后新增元素
setTimeout(function () {
var li = document.createElement('li');
li.innerHTML = 'KKK';
ul.appendChild(li);
}, 4000)
//给其父元素绑定事件
ul.onclick = function (e) {
var ev = e || event;
var target = ev.target || ev.srcElement;
if (target.tagName == 'LI') {
console.log(target.innerHTML);
}
// 获取目标元素
console.log(target);
}
</script>
模拟select
普通版
<div class="select">
<div class="text">1</div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<script>
$('.text').onclick = function (e) {
var ev = e || event;
// 阻止冒泡,否则会触发document.click,会一瞬间关闭
e.stopPropagation();
// 弹出Ul ,选项
$('ul').style.display = 'block';
var oLis = document.querySelectorAll('li');
oLis.forEach(function (val, i) {
//给每个Li绑定事件
val.onclick = function () {
$('.text').innerHTML = this.innerHTML;
$('ul').style.display = 'none';
}
})
//点击旁边空白处的关闭
document.onclick = function () {
$('ul').style.display = 'none';
}
}
事件委托版
事件委托 一般委托给触发事件的最外面的一层
document.onclick = function (e) {
var ev = e || event;
var target = e.target || e.srcElement;
//判断点击的元素,进行相应的操作
if (target.className == 'text') {
// 点击.text,select进行了选择
$('ul').style.display = 'block';
// 返回,不执行最后的 $('ul').style.display = 'none';
return
}
if (target.tagName == 'LI') {
//li点击
$('.text').innerHTML = target.innerHTML;
}
// 点击空白处隐藏,(除了点击.text,其他元素点击都隐藏)
$('ul').style.display = 'none';
}
事件委托留言板
document.onclick = function (e) {
var ev = e || event;
var target = ev.target || ev.srcElement;
if (target.className == 'btn') {
// 点击留言按钮
var nickName = $('.name').value;
var content = $('.content').value;
if (!nickName || !content) {
alert('留言/用户名不能为空')
return;
}
// 用户名不可更改
$('.name').disabled = true;
// 清空留言板
$('.content').value = '';
// 倒计时10s
timeOut(10);
//创建留言
creatContent(nickName, content);
}
// 点击删除留言
if (target.className == 'delete') {
// 删除留言
target.parentNode.remove();
}
if (target.className == 'answer') {
replayContent(target);
}
}
// 回复留言
//ele为当前点击的回复按钮
function replayContent(ele) {
$('.popup').style.display = 'flex';
$('.pop-btn1').onclick = function () {
var repContent = $('.pop-reply').value;
// 清空留言板
$('.pop-reply').value = '';
if (!repContent) {
alert('留言不能为空');
}
// 创建回复
// 找到留言的地方
var reps = ele.previousElementSibling.previousElementSibling;
var p = document.createElement('p');
p.innerHTML = repContent;
reps.insertBefore(p, reps.firstElementChild);
$('.popup').style.display = 'none';
}
$('.pop-btn2').onclick = function () {
$('.popup').style.display = 'none';
// 清空留言板
$('.pop-reply').value = '';
}
}
// 创建留言
function creatContent(nickName, content) {
//创建
var li = document.createElement('li');
li.innerHTML = `
<li>
<div class="message-head">
<span class="photo">${nickName}</span>
<p class="time">${getTime()}</p>
</div>
<p class="liuyan">${content}</p>
<div class="reply">
</div>
<button class="delete">Delete</button>
<button class="answer">Answer</button>
</li>
`;
$('.text').insertBefore(li, $('.text').firstElementChild);
}
// 时间格式化
function getTime() {
var date = new Date();
return `
${date.getFullYear()}-${date.getMonth()}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}
`;
}
// 倒计时
function timeOut(time) {
// 倒计时
$('.btn').disabled = true;
var count = time;
var timer = setInterval(function () {
$('.btn').innerHTML = count-- + '秒后留言';
if (count < 0) {
clearInterval(timer);
$('.btn').innerHTML = '留言';
$('.btn').disabled = false;
}
}, 500);
}
function $(ele) {
return document.querySelector(ele);
}
</script>
day15
表单事件
- 表单事件
- onsubmit 表单提交
- onblur 失去焦点
- onfoucs 获取焦点
- onchange 失去焦点,并且发生改变才会触发
- oninput 输入就会触发 (频繁触发事件)
- 方法
- focus()获取焦点
- blur()失去焦点
<form action="#">
<input type="text" name='username'> <span id="tips">还可以输入<i>6</i>个字符</span><br>
<input type="password" name="pwd">
<button type="submit">提交</button>
</form>
<script>
$('form').onsubmit = function (e) {
var e = e || event;
// 阻止默认事件
e.preventDefault();
console.log(888);
}
// 失去焦点
$('input[type="text"]').onblur = function () {
if (this.value == '') {
alert('报错');
}
}
// 值改变
$('input[type="password"]').onchange = function () {
console.log(this.value);
}
// 只要输入就会触发 频繁触发
$('input[type="text"]').oninput = function () {
if (this.value.length > 6) {
// 显示前6个字符
// console.log($('input[type="text"]').value.substring(0, 7));
$('input[type="text"]').value = $('input[type="text"]').value.substring(0, 6);
// 光标移到下一行
$('input[type="password"]').focus();
// this.blur(); 失去焦点
return;
}
$('#tips i').innerHTML = 6 - this.value.length;
}
function $(el) {
return document.querySelector(el);
}
preventDefault()
阻止默认事件
$('form').onsubmit = function (e) {
var e = e || event;
// 阻止默认事件
e.preventDefault();
console.log(888);
}
键盘事件
- onkeydown 键盘按下触发 ,按着不动会持续触发
- e.key 键盘上的值
- e.keyCode 键值对应的ASCII值
- e.altKey 如果按下的是ctrl则返回true,否则false
- e.ctrlKey
- e.shiftKey
- onkeyup
- onkeypress
大部分时候onkeydown和onkeypress是一样的效果
但是 onkeypress不支持部分功能键(上下左右键/ctrl/shift/alt)
keyCode兼容写法
var keyCode = e.keyCode|| e.which // 兼容IE8及以下
document.onkeydown = function (event) {
// 获取键值对应的ASCII码值 键值
// IE8及以下 e.witch
var keyCode = event.keyCode || event.which
// console.log(event);
// console.log(event.key);
// console.log(event.keyCode);
// 特殊的键
// console.log(event.ctrlKey); //按ctrl则true,否则为false
// console.log(event.shiftKey);
// console.log(event.altKey);
// console.log(event.keyCode);
// ctrl+回车
if (event.ctrlKey && event.keyCode == 13) {
console.log('回车');
}
}
document.onkeyup = function (e) {
console.log(e.keyCode);
}
// 键盘抬起事件
window.onkeypress = function (e) {
console.log(e.keyCode);
}
</script>
键盘控制方块移动
<style>
.a {
width: 200px;
height: 200px;
background-color: springgreen;
position: absolute;
border: 10px solid pink;
}
</style>
</head>
<body>
<div class="a"></div>
<script>
var root = document.documentElement || document.body;
// 最大宽度
// var maxW = root.clientWidth - parseFloat(getComputedStyle($('.a')).width);
// console.log(maxW);
// 最大高度
// var maxH = root.clientHeight - parseFloat(getComputedStyle($('.a')).height);
var maxW = root.clientWidth - $('.a').offsetWidth;
var maxH = root.clientHeight - $('.a').offsetHeight;
document.onkeydown = function (e) {
var e = e || event;
var keyCode = e.keyCode || e.which;
//右 37
// 上38
// 左 39
//下40
//上
var top = $('.a').offsetTop;
var left = $('.a').offsetLeft;
// 限制
if (top < 0) {
top = 0;
}
// 上
if (keyCode == 38) {
// $('.a').style.top = (top - 10) + 'px';
top -= 10;
}
// 下
if (keyCode == 40) {
top += 10;
// $('.a').style.top = (top + 10) + 'px';
}
// 左
if (keyCode == 37) {
left -= 10;
// $('.a').style.left = (left - 10) + 'px';
}
// 右
if (keyCode == 39) {
left += 10
// $('.a').style.left = (left + 10) + 'px';
}
if (top < 0) top = 0;
if (left < 0) left = 0
if (top > maxH) top = maxH;
if (left > maxW) left = maxW;
$('.a').style.cssText = `left:${left}px;top:${top}px`;
}
function $(ele) {
return document.querySelector(ele);
}
</script>
控制方块一直移动
<style>
.a {
width: 200px;
height: 200px;
background-color: springgreen;
position: absolute;
border: 10px solid pink;
}
</style>
</head>
<body>
<div class="a"></div>
<script>
var timer;
var root = document.documentElement || document.body;
// 最大宽度
// var maxW = root.clientWidth - parseFloat(getComputedStyle($('.a')).width);
// console.log(maxW);
// 最大高度
// var maxH = root.clientHeight - parseFloat(getComputedStyle($('.a')).height);
var maxW = root.clientWidth - $('.a').offsetWidth;
var maxH = root.clientHeight - $('.a').offsetHeight;
document.onkeydown = function (e) {
var e = e || event;
var keyCode = e.keyCode || e.which;
//右 37
// 上38
// 左 39
//下40
//上
var top = $('.a').offsetTop;
var left = $('.a').offsetLeft;
clearInterval(timer);
timer = setInterval(function () {
// 上
if (keyCode == 38) {
// $('.a').style.top = (top - 10) + 'px';
top -= 10;
}
// 下
if (keyCode == 40) {
top += 10;
// $('.a').style.top = (top + 10) + 'px';
}
// 左
if (keyCode == 37) {
left -= 10;
// $('.a').style.left = (left - 10) + 'px';
}
// 右
if (keyCode == 39) {
left += 10
// $('.a').style.left = (left + 10) + 'px';
}
// 限制
if (top < 0) top = 0;
if (left < 0) left = 0
if (top > maxH) top = maxH;
if (left > maxW) left = maxW;
$('.a').style.cssText = `left:${left}px;top:${top}px`;
}, 100)
}
function $(ele) {
return document.querySelector(ele);
}
</script>
Day16
回顾
// DOM的增删改
// 节点和元素或者标签(对象)
// 属性操作
// 自有属性
// 自定义属性
// 特殊的属性
// className(字符串 ) classList
// checked disabled selected
// 事件
//事件的概念:三要素:事件源 事件类型 事件处理函数
// 事件绑定的方式
// 行内JS 事件监听 addEvenlistener DOM0级 2级事件(2级事件后执行)
// 事件的移除 null removeEventListener(具名函数)
// 事件对象
// 事件对象的获取 事件处理函数的第一个参数 /event
// target
//preventDefault()阻止默认事件
// keyCode/key
// 事件机制和事件流(三个阶段)
// 事件冒泡和事件捕获
// 事件委托
// 概念:子元素和父元素触发同类型事件,此时直接给父元素绑定(子元素会冒泡给父元素)
// 优点:
// 原理:基于事件冒泡(子元素的事件也会触发父元素的事件)
// 事件类型
// 键盘事件
// keyCode ctrlKey shiftKey altKey
// onkeydown
// onkeyup
// 案例:键盘控制移动方向
// 鼠标事件
// 其他事件
//留言板
购物车
轮播图
鼠标事件
-
click 单击
-
dbclick 单击
-
mouseenter 鼠标移入 支持的是事件捕获
-
mouseleave 鼠标移开
-
mouseover 鼠标移入 支持事件冒泡
-
mouseout 鼠标移开
-
mousemove 鼠标移动
-
mousedown 鼠标按下
-
mouseup 鼠标抬起
鼠标移入随机颜色
<script>
document.onmouseover = function () {
document.body.style.background = randColor();
}
function randColor() {
var r = parseInt(Math.random() * 256);
var g = parseInt(Math.random() * 256);
var b = parseInt(Math.random() * 256);
return `rgb(${r},${g},${b})`;
}
</script>
鼠标坐标
- e.x和e.y ==e.clientX e.clientY 距离浏览器实际边缘的距离
- e.pageX e.pageY 距离页面实际边缘的距离
- e.offsetX e.offsetY 距离事件源边缘的距离
// e.x和e.y == e.clientX e.clientY 距离浏览器边缘的距离
// e.pageX e.pageY 距离页面实际边缘的距离
// e.offsetX e.offsetY 距离事件源边缘的距离
a.onclick = function (e) {
console.log(e.x, e.y);
console.log(e.clientX, e.clientY);
console.log(e.pageX, e.pageY);
console.log(e.offsetX, e.offsetY)
}
鼠标跟随
day18
day19
运动
加速运动
var speed = 5;
var end = 1000;
document.onclick = function () {
var current = box.offsetLeft;
var t = setInterval(function () {
current += speed;
// 变速运动 (速度逐渐变快)
speed += 5;
if (current >= end) {
box.style.left = end + 'px';
clearInterval(t);
}
box.style.left = current + 'px';
}, 100);
}
变加速运动
var speed = 5;
var end = 1000;
document.onclick = function () {
var current = box.offsetLeft;
var t = setInterval(function () {
current += speed;
// 变速运动 (速度主键变快)
speed += 5;
if (current >= end) {
box.style.left = end + 'px';
clearInterval(t);
}
box.style.left = current + 'px';
}, 100);
}
缓冲运动
思路
- 速度跟随元素的移动而不断的变化
- 确认一个缓冲因子
- 速度 = 元素剩下的距离 /缓冲因子
<style>
div {
width: 100px;
height: 100px;
background-color: #f00;
position: absolute;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
// 匀速
// 加速 : 匀加速和变减速
// 减速 :匀减速和变减速以及缓冲运动
var end = 1100;
var t = setInterval(function () {
//获取当前位置
var current = box.offsetLeft;
// 30 缓冲因子
var speed = (end - current) / 30;
//向上取整,以至于一致为0.几,移动变慢,到达不了目的地
speed = Math.ceil(speed);
console.log(speed);
current += speed;
box.style.left = current + 'px';
if (current >= end) {
clearInterval(t);
box.style.left = end + 'px';
}
}, 10)
</script>
es5严格模式
js的语法存在一些不严谨的地方
开启严格模式
-
为脚本开启严格模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句
"use strict";
(或'use strict';
) -
为函数开启严格模式
要给某个函数开启严格模式,得把
"use strict";
(或'use strict';
)声明一字不漏地放在函数体所有语句之前。
严格模式下:
-
函数/变量必须先声明后使用
-
普通函数的this指向为undefined
-
函数里面不允许出现同名参数的出现
-
普通模式下变量如果没有var, 那么就是全局变量
a = 5;
console.log(a); //5
function aa() {
b = 4;
}
aa();
console.log(b); //4
开启严格模式后,必须先声明后使用
'use strict';
a = 5;
console.log(a); //a is not defined
-
严格模式下this指向问题
普通模式
function aa() { console.log(this); } aa(); //Window对象
严格模式
'use strict'; function aa() { console.log(this); } aa(); //undefined
-
严格模式下,函数里面不允许同变量的出现
普通模式,值进行覆盖,为最后一次
function bb(a, a) { console.log(a); } bb(3, 4); //4
严格模式下,
'use strict'; function bb(a, a) { console.log(a); } bb(3, 4); //Duplicate parameter name not allowed in this context
this指向
- 一般情况下,this指向window对象
- 普通函数,this指向window对象
- 事件处理函数中,this指向的绑定事件的对象
- 在对象里面,this指向对象本身
一般情况,this指向windown对象
console.log(this); //window对象
普通函数里,this指向window对象(非严格模式)
function add() {
console.log(this);
}
add(); //window对象
事件处理函数中,this指向的是绑定事件的对象
// this指向的是绑定此事件的dom元素
btn.onclick = function () {
console.log(this); //<button id='btn'>按钮</button>
}
在对象里面,this指向对象本身
var obj = {
name: 'Jennie',
age: 18,
sayHi: function () {
console.log(this); //{name:'Jennie',age:19,sayHi:f}
console.log(this.name); //jennie
}
}
obj.sayHi();
ES6新增语法
变量的声明方式
-
var
-
let
-
不会产生预编译,变量必须先声明,后使用
-
变量名不允许重复
-
块级作用域
-
-
const 常量
let
无预编译
console.log(a); //undefined
var a = 5;
console.log(a); //Cannot access 'a' before initialization
let a = 5;
变量名不允许重复
var
var a = 5;
var a = 6;
console.log(a); //6
let
let a = 3;
let a = 4;
console.log(a); // Identifier 'a' has already been declared
块级作用域
var
{
var a = 3;
}
console.log(a); //3
{
console.log(a); //3
}
let
let m = 10;
{
console.log(m); //10
let n = 20;
}
console.log(n); //n is not defined
{
console.log(n); //n is not defined
}
{
let b = 10;
}
{
let b = 20;
console.log(b); //20
}
for应用let
for (var i = 0; i < 3; i++) {
}
console.log(i); //3
let应用到for循环时,是有块级作用域的
for (let i; i < ops.length; i++) {
}
console.log(i); //i is not defined
<p>1</p>
<p>2</p>
<p>3</p>
<script>
var ops = document.querySelectorAll('p');
var ops = document.querySelectorAll('p');
// 每个i都是3,
// 从始至终都声明了一个变量i
for (var i = 0; i < ops.length; i++) {
ops[i].onclick = function () {
alert(i);
}
}
console.log(i); //3
//相当于
<p>1</p>
<p>2</p>
<p>3</p>
var i = 0;
ops[i].onclick = function () { };
i++;
ops[i].onclick = function () { };
i++;
ops[i].onclick = function () { };
// 事件的绑定函数异步的,此时i已经为3
</script>
let
<p>1</p>
<p>2</p>
<p>3</p>
var ops = document.querySelectorAll('p');
for (let i = 0; i < ops.length; i++) {
ops[i].onclick = function () {
alert(i);
}
}
//因为let的作用域是块级的,因此有三个变量i,分别为i=0,i=1,i=2
//相当于
{
let i = 0;
ops[i].onclick = function(){
alert(i); //0
}
}
{
let i=1;
ops[i].onclick = function(){
alert(i); //1
}
}
{
let i=2;
ops[i].onclick = function(){
alert(i); //2
}
}
const
常量是块级范围的,非常类似用let 语句定义的变量。但常量的值是无法(通过重新赋值)改变的,也不能被重新声明。
-
const声明时,必须被赋值
-
const的值不可被改变(值为引用类型时,值可变【本质上存储的是地址】)
// const是块级作用域
{
const a = 3;
}
console.log(a); //a is not defined
const值不可被改变
const a = 3;
a = 4; // Assignment to constant variable.
声明时必须赋值
const a; // Missing initializer in const declaration
const为引用类型时
const a = [1, 2, 3]; //引用的是地址
a.pop(3);
console.log(a); //[1,2]
const a = [1, 2, 3]; //引用的是地址
// 改变了地址,报错
a = [1, 2, 3]; //Assignment to constant variable.
函数参数默认值
function test(a, b) {
// 给默认值0
a = a || 0;
b = b || 0;
console.log(a, b);
}
test();
//给参数默认值
function test(a = 0, b = 0) {
console.log(a, b);
}
test();
箭头函数
- 具名函数
- 赋值式声明函数 不会产生预编译
- 声明函数的时候,尽量使用赋值式声明方式
形式一:把function去掉 ,变为=> 只有赋值式声明函数可改成箭头函数
var fn = () => {
console.log(666);
}
fn();
//带有参数
var fn = (a, b) => {
console.log(a + b);
}
fn(3, 4); //7
形式二:当函数体只有一句代码,且有返回值时
var add = (a = 0, b = 0) => a + b;
console.log(add(5, 6)); //11
形式三:当函数有且仅有一个参数,可以省略括号
var test = a => {
console.log(a);
}
test(3); //3
形式四:多参数…arrr
使用箭头函数,无arguments
必须使用…arr接收不可定的多个参数
var fn = (...arr) => {
// console.log(arguments); //报错
console.log(arr);
}
fn(1, 2, 3, 4); //[1,2,3,4]
展开运算符
用于数组和对象
数组
var arr = [1, 2, 3];
var arr2 = [0, ...arr, 4];
arr.pop();
console.log(arr2); //[0,1,2,3,4]
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, ...arr2];
console.log(arr3); //[1,2,3,4,5,6]
对象
const obj1 = {
name: 'cc',
age: '18'
}
const obj2 = {
hobby: '干饭',
say: function () {
console.log(666);
}
}
obj1.name = 'Jennie';
const obj3 = { ...obj1, ...obj2 };
console.log(obj3);
解构
解构赋值
解构赋值:把数组或对象中的值存储在普通变量中
交换变量的值
// 解构赋值
let a = 3;
let b = 4;
[a, b] = [b, a];
console.log(a, b); //4,3
数组的解构
深复制
const arr = [1, 2, 3, 4];
let [a, b, c, d] = arr;
console.log(a, b, c, d); //1,2,3,4
let [m, , n] = [2, 3, 4, 5, 6];
console.log(m, n); //2 4
let [a, b, c, d] = [1, 2, [3, 4], 5];
console.log(c); //[3,4]
let [a, b, [c, m], d] = [1, 2, [3, 4], 5];
console.log(a, b, c, m, d); //1,2,3,4,5
对象的解构
const obj = {
name: 'jennie',
age: 18,
hobby: 'dance'
}
let { name: a, age: b, hobby: c } = obj;
console.log(a, b, c); //jennie,18,dance
const obj = {
name: 'jennie',
age: 18,
hobby: 'dance'
}
let { name, age, hobby } = obj;
console.log(name, age, hobby); //jennie,18,dance
const obj = {
name: 'jennie',
age: 18,
hobby: 'dance',
obj2: {
sex: '女',
say: function () {
console.log(666);
}
}
}
let { name, age, hobby, obj2: { sex, say } } = obj;
console.log(sex);
say(); //6666
day21
回顾
1.数组有序
pop() push() shift() unshift() 改变原数组
indexOf() includes() join() concat() splice() slice()
forEach() map() filter() some() every() reduce() sort
2.对象 无序
键值对 键名:键值
属性
方法
对象可以自定义属性
DOM对象--标签
3.ES6
模板字符串
变量的声明 let const
函数参数的默认值 fn(a=0,b=0)
箭头函数 ()=>{} a=>{} (a,b)=>a+b 没有arguments (...arr)
解构 交换变量的值 解构赋值
cookie
cookie:会话跟踪技术 用于存储数据
cookie 有前提的可以跨页面访问
- 基于http/https protocol;
- 同源策略(安全):不同的网站之间不能访问cookie
- cookie存在客户端
创建cookie
默认到期时间 :会话结束(浏览器关闭)
cookie的key是唯一的,后面会覆盖前面的
document.cookie = 'username=jennie';
路径 path
path=/ ——此站点下的所有路径都可访问
默认情况下,cookie 属于当前页面。
document.cookie = `username=dd;path=/`;
过期时间 expires
var date = new Date(); // 东八区的时间,比标准时间快了8个小时
document.cookie = `username=dd;path=/;expires=${date}`;
封装cookie
// 获取所有的cookies
// 返回值是JOSN格式的数据 [{},{}]
function getCookies() {
const cookies = document.cookie;
let arr = cookies.split('; ');
arr = arr.map(val => {
const item = val.split('=');
return {
name: item[0],
content: item[1]
}
});
return arr
}
// 添加cookie ,默认时间7天
function setCookie(name, content, days = 7) {
// 创建的时间实际上是东八区的时间,比标准时间快了8个小时
const date = new Date();
date.setHours(date.getHours() - 8);
// 设置到期时间
date.setDate(date.getDate() + days);
document.cookie = `${name}=${content};path=/;expires=${date}`
}
function $(e) {
return document.querySelector(e)
}
cookie注册登录案例
<input type="text" id="username" autocomplete="off">
<br><br>
<input type="text" id="userpwd" autocomplete="off">
<br><br>
<button id="btn">登录</button>
<script>
$('#btn').onclick = function () {
const uname = $('#username').value;
const upwd = $('#userpwd').value;
// 不为空
if (uname && upwd) {
// document.cookie = `${uname}=${upwd};path=/;expires=${new Date()}`
// 实际上存储用户名的时候,不需要存密码
document.cookie = `login_user=${uname};path=/;expires=${new Date()}`;
location.href = '04other.html'
}
}
function $(e) {
return document.querySelector(e)
}
</script>
<h1>你好,欢迎
<!-- <span id="user">QQ</span> -->
<span>
<a href="">注册</a>
<a href="">登录</a>
</span>
</h1>
<script>
const cookies = document.cookie;
console.log(cookies)
// 坑: 分号加空格
let arr = cookies.split('; ');
console.log(arr);
// ["usernames=ee", "username=ee", "yy=666", "uu=222", "login_user=aa"];
// arr = arr.map(val => {
// return {
// name: val.split('=')[0],
// content: val.split('=')[1]
// }
// })
arr = arr.map(val =>
({
name: val.split('=')[0],
content: val.split('=')[1]
})
)
console.log(arr);
//验证用户名,如果在cookie中找到 对象 name:login_user,返回具有一个值的数组(数组中的值为一个对象)
const res = arr.filter(val => val.name === 'login_user')[0];
console.log(res);
if (res) {
$('#user').innerHTML = res.content;
} else {
alert('请登录')
}
function $(e) {
return document.querySelector(e)
}
// [
// {
// name: 'login_user',
// content: 'aa'
// },
// {
// name: 'username',
// content: 'ee'
// },
// {
// name: 'usernames',
// content: 'ee'
// },
// {
// name: 'uu',
// content: '222'
// },
// ]
注册
<script>
// 1 点击事件
// 获取输入发用户名和密码
// 1,非空
// 2,判断用户名是否重复
// 3,把用户名和密码存入cookie
// 获取所有的cookie --- 字符串
const cookies = document.cookie;
console.log(cookies) // usernames=ee; yy=666; uu=222; login_user=aa; username=ee
// 把字符串变成数组
let arr = cookies.split('; ');
console.log(arr)
// map 改变数组中的值 --- 每个值都加1
arr = arr.map(val => {
const item = val.split('=');
return {
name: item[0],
content: item[1]
}
})
console.log(arr)
// JSON
// [
// {
// name: 'login_user',
// content: 'aa'
// },
// {
// name: 'username',
// content: 'ee'
// },
// {
// name: 'usernames',
// content: 'ee'
// },
// {
// name: 'uu',
// content: '222'
// },
// ]
$('#btn').onclick = function () {
const uname = $('#username').value;
const upwd = $('#userpwd').value;
// 验证非空
if (uname && upwd) {
// 判断重复 -- 找到用户名
const res = arr.some(val => val.name === 'user_' + uname);
// const res = arr.filter(val => val.name === uname)[0]; // obj
console.log(res);
if (res) {
alert('用户名已被注册')
} else {
alert('恭喜你注册成功');
// 把用户名和密码存入cookie
// document.cookie = `${uname}=${upwd};path=/;expires=${new Date()}`
setCookie('user_' + uname, upwd);
location.href = './06登录.html'
}
// const res = arr.filter(function (val) {
// return val.name == uname
// })
}
}
function $(e) {
return document.querySelector(e)
}
</script>
登录
<script>
// 登录
// 点击事件
// 1 获取用户名和密码
// 2 非空
// 3 用户名是否存在
// 3.1 不存在 alert()
// 3.2 存在
// 3.2.1 判断密码
$('#btn').onclick = function () {
const uname = $('#username').value;
const upwd = $('#userpwd').value;
if (uname && upwd) {
// 判断用户名是否存在
const arr = getCookies();
const res = arr.filter(val => val.name === 'user_' + uname)[0]; // obj
console.log(res) //{name:"yy",content:"222"}
if (res) {
// 判断密码
if (res.content === upwd) {
alert('登录成功');
// 表明当前登录的用户
setCookie('login_user', uname);
location.href = './07列表.html'
} else {
alert('用户名或者密码错误')
}
} else {
alert('用户名不存在')
}
}
}
</script>
用户列表
<h1>
logo
<span id="login_user">欢迎aa</span>
<span id="noLogin">
<a href="./06登录.html">登录</a>
<a href="./05注册.html">注册</a>
</span>
</h1>
<table>
<thead>
<tr>
<th>用户名</th>
<th>密码</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script src="../js/cookie.js"></script>
<script>
// 1 判断用户是否登录
// 没有登录 显示注册
// 已经登录
// 显示用户名
// 显示用户列表
const arr = getCookies();
const res = arr.filter(val => val.name === 'login_user')[0];
console.log(res)
if (res) {
$('#login_user').classList.add('show');
$('#login_user').innerHTML = res.content;
// 显示列表
let fragment = document.createDocumentFragment();
const arr1 = arr.filter(val => val.name.includes('user_'));
console.log(arr1)
arr1.forEach(val => {
const oTr = document.createElement('tr');
oTr.innerHTML = `
<td>${val.name.replace('user_', '')}</td>
<td>${val.content}</td>
`
fragment.appendChild(oTr);
})
$('tbody').appendChild(fragment)
} else {
$('#noLogin').classList.add('show')
}
</script>
删除cookie
删除cookie就是设置cookie的有效期
// 删除cookie 有效期
document.cookie = `bb=333;path=/;expires=${new Date(-1)}`;
document.cookie = `login_user=;path=/;expires=${new Date('2020,1,1')}`
cookie小结
cookie 会话跟踪技术
cookie 存储在客户端 会被用户删除
cookie 实现不同页面的数据共享(处在同源策略的情况下)
cookie缺点
- 容易被用户直接 删除或屏蔽
- cookie一般只能存储50条左右 4kb
- cookie在页面向服务器发送请求的时候,会一起被携带过去 影响页面的响应速度
cookie 一般用于存储一些不重要的信息
- 注册的用户名和密码 重要信息
- 登录的用户名和密码 不重用信息
localStorage
WebStorage h5提供的一种取代cookie的存储技术
-
sessionStorage 会话 浏览器关闭就删除
-
localStorage 本地 永久有效
-
setItem(key,value) 设置
-
getItem() 获取
-
removeItem() 删除
-
clear() 清空
window.localStorage
localStorage.setItem("key", "value");
var lastname = localStorage.getItem("key");
localStorage.removeItem("key");
day22
回顾
数组常用方法
splice 删除,替换,新增
join 数组变字符串
forEach map filter some every
字符串
split 切割成数组
replace 替换
includes
对象
{name:'cc',age:18}
//对象的遍历 for in
//键名不会重复
JSON数据格式
[{},{}]
cookie 有前提的实现跨页面的数据共享
前提:基于http,同源策略
存储在客户端
缺点:
可以被用户删除
容量小 <4kb
cookie会随着服务器请求一起发送,影响页面的响应速度
WebStorage
两种API 对象
localStorage 永久存储
sessionStorage 页面关闭
方法
setItem(key,value)
getItem(key)
removeItem()
clear()
localStorage案例
注册
<input type="text" id="username">
<br> <br>
<input type="text" id="userpwd">
<br><br>
<input type="button" value="注册" id="btn">
<script>
// 收集常见的js报错及原因
// 博客
$('#btn').onclick = () => {
const uname = $('#username').value;
const upwd = $('#userpwd').value;
if (uname && upwd) {
// 判断用户名是否已经被注册
if (localStorage.getItem('user_' + uname)) {
alert('用户名已被注册')
} else {
localStorage.setItem('user_' + uname, upwd);
location.href = './02login.html'
}
}
}
function $(e) {
return document.querySelector(e)
}
</script>
登录
<input type="text" id="username">
<br> <br>
<input type="text" id="userpwd">
<br><br>
<input type="button" value="登录" id="btn">
<script>
$('#btn').onclick = () => {
const uname = $('#username').value;
const upwd = $('#userpwd').value;
if (uname && upwd) {
// 判断用户名是否已经被注册
const res = localStorage.getItem('user_' + uname);
if (res) {
if (res === upwd) {
alert('登录成功');
localStorage.setItem('login_user', uname);
location.href = './03list.html'
} else {
alert('错误')
}
} else {
alert('用户名不存在')
}
}
}
function $(e) {
return document.querySelector(e)
}
</script>
列表
<h1>
logo
<span id="login_user">欢迎aa</span>
<span id="noLogin">
<a href="./02login.html">登录</a>
<a href="./01reg.html">注册</a>
</span>
</h1>
<table>
<thead>
<tr>
<th>用户名</th>
<th>密码</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tbody">
<!-- <tr>
<td>qq</td>
<td>11</td>
<td>删除</td>
</tr> -->
</tbody>
</table>
<script>
const res = localStorage.getItem('login_user');
if (res) {
login_user.classList.add('show');
login_user.innerHTML = res;
} else {
noLogin.classList.add('show');
}
console.log(localStorage)
const fragment = document.createDocumentFragment();
//遍历localStorage对象
for (let key in localStorage) {
if (key.includes('user_')) {
const oTr = document.createElement('tr');
oTr.innerHTML = `
<td>${key.replace('user_', '')}</td>
<td>${localStorage[key]}</td>
<td class="del">删除</td>
`
fragment.appendChild(oTr)
}
}
tbody.appendChild(fragment);
tbody.onclick = e => {
var e = e || event;
const target = e.target || e.srcElement;
if (target.className === 'del') {
// 删除DOM节点
target.parentNode.remove();
const names = target.previousElementSibling.previousElementSibling.innerHTML;
// 删除对应的数据
localStorage.removeItem('user_' + names);
}
}
</script>
sessionStorage
- 依然可以实现不同页面的数据共享,但是不能切换窗口
- 一般应用于敏感账号的一次性登录
<!-- 页面跳转,但是属于同一页面 -->
<a href="./01.test.html">共享数据</a>
<!-- 不同页面,不可共享数据 -->
<a href="./test.html" target="_blank">不可进行数据共享</a>
<script>
sessionStorage.setItem('uname', 'Jisoo Kim');
// 三秒后跳转页面,但仍属于同一页面
setTimeout(() => {
location.href = './test.html';
}, 3000)
</script>
总结
- cookie <4kb 可以设置路径和到期时间 ,用于存储不太重要的数据
- sessionStorage <5MB 属于有效期为关闭页面
- localStorage 长期存储,除非手动删除
JSON
- JSON.stringify(obj) 将数组/对象转为JSON格式字符串
- JSON.parse(string) 把数组或对象类型的字符串变为原来的数组或对象
<input type="text" id="username">
<br><br>
<input type="text" id="usertel">
<br><br>
<input type="text" id="useremail">
<br><br>
<input type="text" id="userpwd">
<br><br>
<button id="btn">注册</button>
<script>
btn.onclick = function () {
// 手机号唯一
const uname = username.value;
const utel = usertel.value;
const uemail = useremail.value;
const upwd = userpwd.value;
const obj = {
uemail: uemail,
uname: uname,
upwd: upwd
}
// JSON.stringify() 把数组或者对象变成字符串
// JSON.parse() 把数组或者对象类型的字符串变成原来的数组或者对象
//key:user_电话号,value,为字符串类型的对象
localStorage.setItem('user_' + utel, JSON.stringify(obj));
console.log(localStorage.getItem('user_' + utel));
//取
let res = localStorage.getItem('user_' + utel);
//将对象形式 的字符串,转为对象
res = JSON.parse(res);
console.log(res)
}
</script>
中文编码
-
encodeURIComponent 对中文进行编码 对部分符号也会编码
-
decodeURIComponent 解码成中文
-
cookie或localStorage 当中尽量不要存储中文,用中文就需进行编码
//编码
let encode = encodeURIComponent('张三');
console.log(encode); //%E5%BC%A0%E4%B8%89
//解码
console.log(decodeURIComponent(encode)); //张三
// 在cookie或者localStorage 当中尽量不要存储中文,实在需要用到中文,就进行编码
// encodeURIComponent 对中文进行编码 对部分符号也会编码
// decodeURIComponent 解码成中文
const names = encodeURIComponent('hello,');
localStorage.setItem(names, '222')
console.log(decodeURIComponent(names))
Ajax
ajax : asynchonous javacript and xml / json 异步无刷新技术
xml/json 是一种数据格式
ajax 实现数据交互
前端 画页面 渲染数据 没有权限直接改数据
后端 处理数据 – 给前端
进程 谷歌浏览器
线程 打开不同的页面,不同的线程
- 多线程
- 单线程
谷歌内核 多线程
javascript 单线程 js的作用是操作DOM
任务队列
js的运行机制
所有的同步任务都在主线程上排列运行,遇到异步任务,会挂在任务队列中,等到所有的主线任务执行完毕之后,再去看任务队列,如果有任务到时间完成就到主线上去执行此任务
event loop 事件循环
异步程序:
- 定时器和延时器
- 事件处理函数
- ajax
ajax创建
状态码 readyState
- 0:(未初始化) 还没有调用open()方法
- 1:(启动) 已经调用open()方法,但还没有调用send()方法。
- 2:(发送) 已经调用send()方法,但还没有接收到响应。
- 3:(接收) 已经接收到部分响应数据。
- 4:(完成) 已经接收到全部的响应数据,且可以在客户端使用了。
响应值 status
- 1xx
- 2xx
- 3xx
- 4xx
- 5xx
const xhr = new XMLHttpRequest();
xhr.open("get",'路径',true);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState==4&&xhr.status==200){
//const res = xhr.responseText;
}
}
// 创建数据请求
const xhr = new XMLHttpRequest();
console.log(xhr.readyState)
// get 数据请求的方式
// 请求数据的路径
// 异步请求
xhr.open('get', '../daeta/1.txt', true);
console.log(xhr.readyState)
// 发送请求
xhr.send();
console.log(xhr.readyState)
// 监听状态的改变 state 状态
xhr.onreadystatechange = function () {
console.log(xhr.readyState)
// readystate 状态的改变
// status 后端返回的结果
if (xhr.readyState == 4 && xhr.status == 200) {
// 服务器返回的数据
const res = xhr.responseText;
console.log(res)
}
}
ajax模拟注册
数据
[
{
"uname": "jennie",
"pwd": 111
},
{
"uname": "jisoo",
"pwd": 222
},
{
"uname": "lisa",
"pwd": 333
},
{
"uname": "rosie",
"pwd": 444
}
]
<h1>注册</h1>
用户名: <input type="text" id='username'>
<br><br>
密码:<input type="text" id="userpwd">
<br><br>
<button id="btn">注册</button>
<script src="./js/ajax.js"></script>
<script>
btn.onclick = () => {
let uname = username.value.trim();
let upwd = userpwd.value.trim();
if (uname && upwd) {
// ajax请求
ajax({
url: './data/reg.json',
success: data => {
// 成功后要做的事
console.log(data);
//筛选看是否有重名
const res = data.some(val => val.uname == uname);
console.log(res);
if (res) {
alert('用户名已存在');
} else {
// 跳转登录
location.href = './login.html';
}
}
})
} else {
alert('请输入用户名或密码')
}
}
</script>
登录
<script>
btn.onclick = () => {
let uname = username.value;
let upwd = userpwd.value;
if (uname && upwd) {
ajax({
url: './data/reg.json',
success: data => {
// 数据筛选,得到筛选的用户对象
data = data.filter(val => val.uname == uname)[0];
console.log(data);
if (data) {
if (data.pwd == upwd) {
// 登录成功
alert('登录成功');
// 存储当前登录的用户信息
localStorage.setItem('login_user', data.uname);
// 跳转商品列表
location.href = './list.html';
} else {
alert('用户名或密码错误');
}
} else {
alert('用户不存在')
}
}
})
} else {
alert('请输入用户名或密码')
}
}
</script>
商品列表渲染 ajax
数据
[
{
"goodsName": "娃哈哈AD钙奶",
"img": "./img/1.jpg",
"desc": "好喝哈哈哈哈",
"price": 50
},
{
"goodsName": "手机",
"img": "./img/a.jpg",
"desc": "全面屏设计,双扬声器,立体",
"price": 999
},
{
"goodsName": "小米电视",
"img": "./img/b.jpg",
"des": "超大屏,海量内容",
"price": 10000
}
]
<script>
// 查看用户是否登录
if (localStorage.getItem('login_user')) {
// 有用户登录
$('#login').classList.add('show');
$('#user').innerHTML = localStorage.getItem('login_user');
// 商品列表展示
ajax({
url: './data/goods.json',
success: data => {
console.log(data);
// 显示搜索框
$('.search').classList.add('searchshow');
// 得到数据,渲染商品列表
let fragment = new DocumentFragment();
// 遍历商品数据
data.forEach(val => {
let li = document.createElement('li');
li.innerHTML = `
<img src="${val.img}" alt="">
<h2>${val.goodsName}</h2>
<p>${val.desc}</p>
<span id='price'>¥${val.price}</span>
`;
fragment.appendChild(li);
});
$('ul').appendChild(fragment);
// 搜索框绑定点击事件
$('.search button').onclick = () => {
let content = $('.search input').value;
let searchContent = data.filter(val =>
val.goodsName.includes(content)
);
// 清空ul里的内容,重新渲染
$('ul').innerHTML = '';
console.log(searchContent);
let fragment = new DocumentFragment();
// 遍历商品数据
searchContent.forEach(val => {
let li = document.createElement('li');
li.innerHTML = `
<img src="${val.img}" alt="">
<h2>${val.goodsName}</h2>
<p>${val.desc}</p>
<span id='price'>¥${val.price}</span>
`;
fragment.appendChild(li);
});
$('ul').appendChild(fragment);
}
}
})
} else {
// 用户未登录,显示登录注册
$('#noLogin').classList.add('show');
}
</script>
城市三级联动
数据
[
{
"province": "湖北省",
"children": [
{
"city": "武汉市",
"children": [
"江岸区",
"江汉区",
"硚口区",
"汉阳区",
"武昌区"
]
},
{
"city": "孝感市",
"children": [
"孝南区",
"应城市",
"安陆市",
"汉川市"
]
}
]
},
{
"province": "江苏省",
"children": [
{
"city": "南京市",
"children": [
"玄武区",
"白下区",
"鼓楼区"
]
},
{
"city": "宿迁市",
"children": [
"宿城区",
"宿豫区"
]
}
]
},
{
"province": "山西省",
"children": [
{
"city": "太原市",
"children": [
"小店区",
"迎泽区",
"杏花岭区"
]
},
{
"city": "大同市",
"children": [
"南郊区",
"光临县"
]
}
]
}
]
<body>
<h1>全国城市三级联动</h1>
<script src="./js/ajax.js"></script>
<h2>
<select id="province">
<option>省份</option>
</select>
<select id="city">
<option>地级市</option>
</select>
<select id="area">
<option>市、县级市</option>
</select>
</h2>
<script>
// 首先加载省
ajax({
url: "./data/city.json",
success: data => {
console.log(data);
data.forEach(val => {
let option = document.createElement("option");
option.innerHTML = val.province;
$('#province').appendChild(option);
});
}
});
// // 省绑定事件
$("#province").onchange = function () {
// console.log(this);
console.log(this.value);
let pro = this.value;
ajax({
url: "./data/city.json",
success: data => {
// data.forEach(val => {
// let option = document.createElement("option");
// option.innerHTML = val.province;
// $('#province').appendChild(option);
// });
// console.log(data);
//得到选择的省的数组对象(即包含其孩子children)
let prov = data.filter(val => {
return val.province == pro;
})[0];
$("#city").innerHTML = '<option>地级市</option>';
$("#area").innerHTML = '<option>地区</option>';
// 如果有对应的市
if (prov) {
prov.children.forEach(val => {
let option = document.createElement("option");
option.innerHTML = val.city;
$('#city').appendChild(option);
});
}
$("#city").onchange = function () {
console.log(this);
let city = this.value;
let area = prov.children.filter(val => {
return val.city === city;
})[0];
$("#area").innerHTML = '<option>地区</option>';
if (area) {
area.children.forEach(val => {
console.log(val);
let option = document.createElement("option");
option.innerHTML = val;
$('#area').appendChild(option);
})
}
}
}
});
}
// 箭头函数,this为window
// $('#province').onchange = () => {
// console.log(this);
// console.log(this.value);
// }
aJax封装
// 参数:对象类型
// path:请求路径
// method: get/post
// dataType:返回数据的类型 json(从后端的到josn格式的字符串,再转为具体数组/对象,返回给前端)/text
// success: 成功后的回调函数 data ,请求后得到的数据
function ajax(option) {
const { path, method = "get", dataType = "json", success } = obj;
const xhr = new XMLHttpRequest();
xhr.open(method, path, true);
xhr.send();
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
let res = xhr.responseText;
if (dataType == 'json') {
// 从请求初(后端)得到json格式字符串,再转为具体数组/对象,返回,给成功后的调用函数使用
res = JSON.parse(xhr.responseText);
}
success && success(res);
}
}
}
day23
网站开发的流程
//需求
//原型图
//UI出设计图 同时后端设计数据库和接口
//前端页面
//前端把静态页面变成动态的页面 需要数据
//接口 --前端想要的数据
//要求前端传过去的数据
//前后端联调 调接口
//测试
//上线 域名, 服务器 --网站对外发布
//狭义的服务器:超级计算机
// 广义的服务器:服务器上所需要的环境 -- 服务器和数据库
// phpstudy_pro 集成了这些环境 apache-服务器 mysql-数据库
php实现动态交互性网站的服务端脚本语言
php能做什么
- 写html js php写代码
- 可以读写数据库的内容
- 读写cookie
- 读写服务器上的文件
php运行在服务器上
vscode live server 是一个假的服务 不能进行逻辑处理
文件夹必须放在 小P WWW文件夹下
// 访问PHP文件
// IP地址访问
// 127.0.0.1/
// localhost
登录的接口
路径 : 192.168.56.23/login.php
前端需要携带的数据
username : string
userpwd : string
后端返回的数据
{
status : true ,
msg : '登录成功'
}
php语法
输出
echo
<?php
header('content-type:text/html;charset=utf-8');
echo('hello world');
echo '<br/>';
echo 'BLACKPINK';
?>
var_dump
<?php
header('content-type:text/html;charset=utf-8');
var_dump('hello'); //string(5) "hello"
var_dump(3); //int(3)
?>
变量和数据类型
变量的声明使用$
数据类型 int float string null object array
$a = 10;
$b = 3.4;
$c = 'test';
var_dump($a); //int(10)
var_dump($b); //float(3.4)
var_dump($c); //string(4) "test"
数组
php中的数组分为两种
- 数值数组 ——js的数组
- 关联数组——js的对象的形式
$arr = [3,4,5,6];
echo $arr; //Array
// array(4) { [0]=> int(3) [1]=> int(4) [2]=> int(5) [3]=> int(6) }
var_dump($arr);
关联数组
$objArr = ['name'=>'jennie',"age"=>18];
// echo $objArr; //Arr
//array(2) { ["name"]=> string(6) "jennie" ["age"]=> int(18) }
var_dump($objArr)
运算符
- 算数运算符
- 比较运算符
- 逻辑运算符
- 自增自减
- 并置运算符 . ,相当于js中的 +,拼接字符串
- 双引号实现类似js的模板字符串 ``
$a = 3+4;
echo $a; //7
$a = 3>4;
// echo $a; //7
var_dump($a);
并置运算符
$a = 'I love ';
$b = 'BLACKPINK';
echo ($a.$b); //I love BLACKPINK
双引号
$a = 3;
$b = 4;
$c = $a+$b;
echo("$a+$b 的值是:$c"); //3+4 的值是:7
数组的遍历
数值数组
$arr = [3,4,5,6];
for($i=0;$i<count($arr);$i++){
echo $i;
echo $arr[$i];
echo '<br/>';
}
0 3
1 4
2 5
3 6
关联数组遍历
$arr = ['name'=>'jennie','age'=>18,'pwd'=>'test123'];
//jennie18test123
foreach($arr as $val){
echo $val;
}
$arr = ['name'=>'jennie','age'=>18,'pwd'=>'test123'];
// namejennie
// age18
// pwdtest123
foreach ($arr as $key=>$val){
echo $key;
echo $val;
echo '<br/>';
}
json转换
json_encode
$obj = ['name'=>'jennie','age'=>18,'pwd'=>'test123'];
// {
// "name": "jennie",
// "age": 18,
// "pwd": "test123"
// }
echo json_encode($obj);
json_decode
表单
action跳转
使用form表单的action会跳转到请求地址。
get请求的执行效率更高
post数据量更大
post请求更安全
get获取数据列表
post修改数据
get方式
form.html
注意name要与后端接口一致
<form action="../php/first.php" method="get">
用户名 <input type="text" name='username'>
<br><br>
密码:<input type="text" name='password'>
<br><br>
<button type="submit">注册</button>
</form>
first.php
<?php
header('content-type:text/html;charset=utf-8');
// get方式接收前端数,name一致
$uname = $_GET['username'];
$password = $_GET['password'];
//转为php关联数组(类似对象)
$obj =['name'=>$uname,'pwd'=>$password];
// 转为json格式字符串
echo json_encode($obj); //{"name":"\u738b\u4e94","pwd":"123"} 中文字符编码原因
?>
post方式
html
<form action="../php/first.php" method="post">
用户名 <input type="text" name='username'>
<br><br>
密码:<input type="text" name='password'>
<br><br>
<button type="submit">注册</button>
</form>
php
<?php
header('content-type:text/html;charset=utf-8');
// post方式接收前端数,name一致
$uname = $_POST['username'];
$password = $_POST['password'];
//转为php关联数组(类似对象)
$obj =['name'=>$uname,'pwd'=>$password];
// 转为json格式字符串
echo json_encode($obj);
?>
ajax方式
get方式
- get方式需要在请求地址后拼接 ?参数名1=参数值1&参数名2=参数值2
html
用户名 <input type="text" id='username'>
<br><br>
密码:<input type="text" id='password'>
<br><br>
<button id='btn'>注册</button>
<script>
btn.onclick = function () {
let uname = username.value;
let upwd = password.value;
// 发送请求给后端
const xhr = new XMLHttpRequest();
// 参数
let param = `username=${uname}&password=${upwd}`;
// get方式拼接参数
xhr.open('get', '../php/get.php?' + param);
xhr.send();
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
// 请求成功,得到数据
// 返回的是Json格式字符串(中文还未转码)
let res = xhr.responseText; //string {"name":"\u738b\u4e94","pwd":"123"}
console.log(res);
// 转为javascript中的对象
res = JSON.parse(res); //obj {name: "王五", pwd: "123"}
console.log(res);
}
}
}
</script>
php
post方式
- post方式必须设置请求头 xhr.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded; charset=utf-8”)
- send时传参
html
用户名 <input type="text" id='username'>
<br><br>
密码:<input type="text" id='password'>
<br><br>
<button id='btn'>注册</button>
<script>
btn.onclick = function () {
let uname = username.value;
let upwd = password.value;
// 发送请求给后端
const xhr = new XMLHttpRequest();
// 参数
let param = `username=${uname}&password=${upwd}`;
// 请求头改为post
xhr.open('post', '../php/post.php');
// 设置请求头
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
// send时发送参数
xhr.send(param);
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
// 请求成功,得到数据
// 返回的是Json格式字符串(中文还未转码)
let res = xhr.responseText;
console.log(res); //string {"uname":"\u91d1\u667a\u59ae","upwd":"111"}
// 转为javascript中的对象
res = JSON.parse(res);
console.log(res);//object{uname: "金智妮", upwd: "111"}
}
}
}
</script>
php
<?php
header('content-type:text/html;charset=utf-8');
$uname = $_POST['username'];
$upwd = $_POST['password'];
$obj = ['uname'=>$uname,'upwd'=>$upwd];
// 返回josn格式字符串 对象
echo(json_encode($obj));
?>
ajax封装
//ajax 向服务器请求数据
// 1.创建一个请求 new
// 2.打开连接 open
// 3.发送连接 send
// 3.1携带数据
// 3.2 方式 get/post 携带数据的区别
// 4.等待 onreadystatechange =function (){}
/**
*
* @param {对象} options
* type:请求方式 get/post
* path:请求路径,url
* dataType:后端返回数据格式
* data:前端向后端传递的数据,对象格式
* success:成功后的回到函数 ,data形参:请求返回的数据
*/
function ajax(options) {
const {
type = 'get',
path,
dataType = 'json',
data,
success
} = options;
//解析传过来的参数(遍历对象)
let params = '';
for (let key in data) {
params += `${key}=${data[key]}&`;
}
//将最后一个多余的&删除
params = params.substring(0, params.length - 1);
const xhr = new Request();
// 判断数据请求方式
//get方式
if (type.toLowerCase() == 'get') {
xhr.open('get', path + '?' + params);
xhr.send();
} else if (type.toLowerCase() == 'post') {
//post方式
xhr.open('post', path);
// 设置请求头
xhr.setRequestHeader('Content-type', 'application/x-www-form- urlencoded;charset=utf-8');
xhr.send(params);
}
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
// 请求成功
let data = xhr.responseText;
// dataType为josn时,将最后的结果数据转为json
if (dataType.toLowerCase() == 'json') {
data = JSON.parse(res);
}
// 执行成功后的回调函数
sussess && success(data);
}
}
}
day24
php数据库连接
<?php
header('content-type:text/html;charset=utf-8') ;
// php连数据库
$host = 'localhost:3306';
$user = 'root';
$pwd = 'root';
// 注意:不是连接名,而是数据库的名字
$dbname = 'test';
// 创立数据库连接
$conn = mysqli_connect($host , $user , $pwd , $dbname);
// if($conn) {
// echo 'ok';
// } else {
// echo 'err';
// }
if(!$conn) {
// 结束整个php
die('数据库连接失败');
}
echo 'ok';
?>
php的sql查询
// 定义sql语句 username字段名
$sql = "select * from user_info where username = '$uname'";
// 执行sql语句 执行之后会得到一个结果集
$res = mysqli_query($conn , $sql);
// 需要把结果集变成数组
$arr = mysqli_fetch_array($res) ; //得到的是一条数据
// echo json_encode($arr);
// 定义sql语句
$sql = "select * from user_score";
// 执行sql语句 执行之后会得到一个结果集
$res = mysqli_query($conn , $sql);
$list = [];
while($data = mysqli_fetch_array($res)) {
// 把需要的数据留下来
$temp = [];
$temp['uid'] = $data['uid'];
$temp['username'] = $data['username'];
$temp['html'] = $data['html'];
$temp['css'] = $data['css'];
$temp['js'] = $data['js'];
$temp['php'] = $data['php'];
array_push($list , $temp) ;
}
删除/新增,返回影响行数
// 定义sql语句
$sql = "delete from user_score where uid = $id ";
// 执行sql语句 执行之后会得到一个结果集
mysqli_query($conn , $sql);
$rows = mysqli_affected_rows($conn) ;
if($rows > 0) {
$obj['flag'] = true ;
$obj['msg'] = '删除成功' ;
} else {
$obj['flag'] = false ;
$obj['msg'] = '删除失败' ;
}
登录
- 前端传用户名/密码给后端
- 后端
- 根据用户名查询数据库
- 无结果集,用户不存在,直接 返回status=false,msg=用户名不存在
- 有结果集,说明用户存在,比对用户输入的密码与查询到的密码,比对正确,status=true。否则status=false,msg=用户名/密码输入错误。
php
<?php
header('content-type:text/html;charset=utf-8') ;
// php连数据库
$host = 'localhost:3306';
$user = 'root';
$pwd = 'root';
// 注意:不是连接名,而是数据库的名字
$dbname = 'test';
// 创立数据库连接
$conn = mysqli_connect($host , $user , $pwd , $dbname);
if(!$conn) {
// 结束整个php
die('数据库连接失败');
}
// 登录的接口
// 1 连数据库
// 2 接受前端传来的用户名和密码
// 2.1 收到了
// 2.2 收不到 返回结果
// 3 去数据库查数据
// 3.1 查询用户名
// 3.1.1 不存在 返回结果
// 3.1.2 存在
// 3.1.2.1 找密码 返回结果
// 接受前端传来的数据
$uname = $_POST['username'] ;
$upwd = $_POST['userpwd'] ;
// $uname = 'aa' ;
// $upwd = '11' ;
// 返回给前端的结果
$obj = [];
// 判断数据是否接受成功
if($uname && $upwd) {
// 查询用户名是否存在
// 定义sql语句 username字段名
$sql = "select * from user_info where username = '$uname'";
// 执行sql语句 执行之后会得到一个结果集
$res = mysqli_query($conn , $sql);
// 需要把结果集变成数组
$arr = mysqli_fetch_array($res) ;
// echo json_encode($arr);
// 判断是否有结果
if($arr) {
// 判断密码
if($arr['userpwd'] == $upwd) {
$obj['status'] = true ;
$obj['msg'] = '登录成功' ;
} else {
$obj['status'] = false ;
$obj['msg'] = '用户名或密码错误' ;
}
} else {
$obj['status'] = false ;
$obj['msg'] = '用户名不存在' ;
}
} else {
$obj['status'] = false ;
$obj['msg'] = '前端数据错误' ;
}
//转为josn字符串格式给前端
echo json_encode($obj);
?>
html
<input type="text" id="user">
<input type="text" id="pwd">
<button id="btn">登录</button>
<script src="../js/tool.js"></script>
<script>
// 登录的接口
// 路径 '../php/login.php'
// 参数
// 请求方式 post
// 参数名字
// username string
// userpwd string
// 后端返回的数据 json
// {status : true , msg : '登录成功'}
// {status : false , msg : '登录失败'}
$('#btn').onclick = () => {
const uname = $('#user').value;
const upwd = $('#pwd').value;
if (uname && upwd) {
ajax({
path: '../php/login.php',
data: {
username: uname,
userpwd: upwd
},
type: 'post',
successCB: data => {
console.log(data);
// 解构
const { msg, status } = data;
if (status) {
console.log(msg);
localStorage.setItem('login_user', username);
} else {
alert(msg);
}
}
})
}
}
</script>
注册
php
<?php
header('content-type:text/html;charset=utf-8') ;
// php连数据库
$host = 'localhost:3306';
$user = 'root';
$pwd = 'root';
// 注意:不是连接名,而是数据库的名字
$dbname = 'test';
// 创立数据库连接
$conn = mysqli_connect($host , $user , $pwd , $dbname);
if(!$conn) {
// 结束整个php
die('数据库连接失败');
}
// 注册的接口
// 1 连数据库
// 2 接受前端传来的用户名和密码
// 2.1 收到了
// 2.2 收不到 返回结果
// 3 去数据库查数据
// 3.1 查询用户名
// 3.1.1 已存在 返回结果
// 3.1.2 不存在
// 3.1.2.1 继续去数据库添加数据 insert into
// 接受前端传来的数据
$uname = $_POST['username'] ;
$upwd = $_POST['userpwd'] ;
// $uname = 'yys钱' ;
// $upwd = '11' ;
// 返回给前端的结果
$obj = [];
// 判断数据是否接受成功
if($uname && $upwd) {
// 查询用户名是否存在
// 定义sql语句
$sql = "select * from user_info where username = '$uname'";
// 执行sql语句 执行之后会得到一个结果集
$res = mysqli_query($conn , $sql);
// 需要把结果集变成数组
$arr = mysqli_fetch_array($res) ;
// echo json_encode($arr);
// 判断是否有结果
if(!$arr) {
// 插入语句
$sql = "insert into user_info (username , userpwd) VALUES ('$uname' , '$upwd')" ;
// 执行
mysqli_query($conn , $sql) ;
// 受影响的行数
$rows = mysqli_affected_rows($conn) ;
// echo $rows;
// 受影响的行数大于0
if($rows > 0) {
$obj['status'] = true ;
$obj['msg'] = '注册成功' ;
} else {
$obj['status'] = false ;
// 数据没有插入成功
$obj['msg'] = '数据库注册失败' ;
}
} else {
$obj['status'] = false ;
$obj['msg'] = '用户名已被注册' ;
}
} else {
$obj['status'] = false ;
$obj['msg'] = '前端数据错误' ;
}
// JSON_UNESCAPED_UNICODE 防止中文编码
echo json_encode($obj , JSON_UNESCAPED_UNICODE);
?>
html
<input type="text" id="user">
<input type="text" id="pwd">
<button id="btn">注册</button>
<script src="../js/tool.js"></script>
<script>
// 注册的接口
// 路径 '../php/reg.php' 404
// 参数
// 请求方式 post
// 参数名字
// username string
// userpwd string
// 后端返回的数据 json
// {status : true , msg : '注册成功'}
// {status : false , msg : '注册失败'}
$('#btn').onclick = () => {
const username = $('#user').value;
const userpwd = $('#pwd').value;
if (username && userpwd) {
ajax({
path: '../php/reg.php',
data: {
username,
userpwd
},
type: 'post',
successCB: data => {
console.log(data);
// 解构
const { msg, status } = data;
if (status) {
alert(msg)
location.href = './login.html'
} else {
alert(msg);
}
}
})
}
}
</script>
列表渲染
php
<?php
header('content-type:text/html;charset=utf-8') ;
// php连数据库
$host = 'localhost:3306';
$user = 'root';
$pwd = 'root';
// 注意:不是连接名,而是数据库的名字
$dbname = 'test';
// 创立数据库连接
$conn = mysqli_connect($host , $user , $pwd , $dbname);
// if($conn) {
// echo 'ok';
// } else {
// echo 'err';
// }
if(!$conn) {
// 结束整个php
die('数据库连接失败');
}
// 返回给前端的结果
$obj = [];
// 定义sql语句
$sql = "select * from user_score";
// 执行sql语句 执行之后会得到一个结果集
$res = mysqli_query($conn , $sql);
$list = [];
while($data = mysqli_fetch_array($res)) {
// 把需要的数据留下来
$temp = [];
$temp['uid'] = $data['uid'];
$temp['username'] = $data['username'];
$temp['html'] = $data['html'];
$temp['css'] = $data['css'];
$temp['js'] = $data['js'];
$temp['php'] = $data['php'];
array_push($list , $temp) ;
}
if($list) {
$obj['status'] = true ;
$obj['data'] = $list ;
} else {
$obj['status'] = false ;
$obj['msg'] = '数据请求失败' ;
}
echo json_encode($obj);
?>
php
<h1>
<span class="login"></span>
<span class="nologin">
<a href="">登录</a>
<a href="">注册</a>
</span>
</h1>
<ul>
<li>
<span>姓名</span>
<span>html</span>
<span>css</span>
<span>js</span>
<span>php</span>
<span>
操作
</span>
</li>
</ul>
<ul class="list">
<li>
<span>yy</span>
<span>100</span>
<span>100</span>
<span>100</span>
<span>100</span>
<span>
<button class="edit">编辑</button>
<button class="del">删除</button>
</span>
</li>
</ul>
<script src="../js/tool.js"></script>
<script>
// 根据localStorage
const uname = localStorage.getItem('login_user');
if (uname) {
$('.login').innerHTML = uname;
ajax({
path: '../php/list.php',
type: 'get',
successCB: data => {
console.log(data);
var { status, msg, data } = data;
if (status) {
console.log(data);
let res = '';
// val 实际上是一个对象 val = {uid , }
// val 进行解构
data.forEach(({ uid, username, html, css, js, php }) => {
res += `
<li>
<span>${username}</span>
<span>${html}</span>
<span>${css}</span>
<span>${js}</span>
<span>${php}</span>
<span data-id="${uid}">
<button class="edit">编辑</button>
<button class="del">删除</button>
</span>
</li>
`
})
console.log(res)
$('.list').innerHTML = res;
} else {
console.log(msg)
}
}
})
} else {
$('.nologin').style.display = 'inline-block';
}
删除
php
<?php
header('content-type:text/html;charset=utf-8') ;
// php连数据库
$host = 'localhost:3306';
$user = 'root';
$pwd = 'root';
// 注意:不是连接名,而是数据库的名字
$dbname = 'test';
// 创立数据库连接
$conn = mysqli_connect($host , $user , $pwd , $dbname);
// if($conn) {
// echo 'ok';
// } else {
// echo 'err';
// }
if(!$conn) {
// 结束整个php
die('数据库连接失败');
}
// echo 'ok';
// 删除的接口
// 1 连数据库
// 2 接受前端传来的id
// 2.1 收到了
// 2.2 收不到 返回结果
// 3 去数据库删数据
// 3.1 受影响的行数
// 接受前端传来的数据
$id = $_POST['userid'] ;
// 返回给前端的结果
$obj = [];
// 定义sql语句
$sql = "delete from user_score where uid = $id ";
// 执行sql语句 执行之后会得到一个结果集
mysqli_query($conn , $sql);
$rows = mysqli_affected_rows($conn) ;
if($rows > 0) {
$obj['flag'] = true ;
$obj['msg'] = '删除成功' ;
} else {
$obj['flag'] = false ;
$obj['msg'] = '删除失败' ;
}
// JSON_UNESCAPED_UNICODE 防止中文编码
echo json_encode($obj , JSON_UNESCAPED_UNICODE);
?>
html
// 绑定事件
$('.list').onclick = e => {
var e = e || event;
var target = e.target;
const userid = target.parentNode.getAttribute('data-id');
// 删除
// 删除的接口
// 路径 '../php/del.php'
// 参数
// 请求方式 post
// 参数名字
// userid
// 后端返回的数据 json
// {flag : true , msg : '删除成功'}
// {flag : false , msg : '删除失败'}
ajax({
type: 'post',
path: '../php/del.php',
data: {
userid,
},
successCB: data => {
console.log(data);
const { flag, msg } = data;
if (flag) {
alert(msg);
target.parentNode.parentNode.remove()
} else {
alert(msg)
}
}
})
}
day26
同源策略
是一种约定,是浏览器最基本也是最核心的安全功能,少了它,浏览器的正常功能会受到影响,可以说web是构建在同源策略的基础之上的,浏览器只是针对同源策略的一种实现。
同源
同源: 域名,端口,协议 必须相同
- 协议:http / https
- 端口 :80/5500
- 域名 :jd.com / taobao.com
有的时候,一个大型网站有很多小的网站共同构建 需要实现数据共享
例如百度
- https://www.baidu.com/
- https://www.news.baidu.com/
- https://www.mp3.baidu.com/
- …
跨域
出于浏览器的同源策略限制。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。
使用vscode的LiveServer起一个服务,此时 ,端口会变,访问不同源的网站
http://127.0.0.1:5500/html/test.html
1.php
<?
echo '888';
?>
html
因为同源策略受限,不同源的网站之间不能数据共享
<script src="../js/ajax.js"></script>
<script>
ajax({
path: 'http://192.168.56.110/day26/php/kuayu.php',
type: 'get',
success: function (data) {
console.log(data);
}
})
</script>
此时会报错
Access to XMLHttpRequest at 'http://192.168.56.110/day26/php/kuayu.php?' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
解决跨域
CROS
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
后端说明一下谁跨域访问我的接口
header('Access-Control-Allow-Origin: http://127.0.0.1:5500'); // 只允许指定的用户访问
header('Access-Control-Allow-Origin: *'); // 允许所有的人访问
修改Php
<?
// 允许 http://127.0.0.1:5500的可以访问
header('Access-Control-Allow-Origin: http://127.0.0.1:5500');
echo '888';
?>
html
<script>
ajax({
path: 'http://192.168.56.110/day26/php/kuayu.php',
type: 'get',
success: function (data) {
console.log(data);
}
})
</script>
jsonp
-
href与src不受同源策略的影响(img,script标签,link标签【使用href】)
-
跨域 :只能以get方式访问接口,一般用于查询。(因为一般删除,修改,之类的接口,后端会使用Post方式,此时使用get方式,后端后接受不到参数)
-
动态创建script标签
-
给标签设置路径:需要访问的接口
-
如果要携带参数,那么就在接口后面拼接问号
-
后端返回的是一个函数调用
-
前端定义一个同名函数来接受后端的数据
<?
// $user = $_GET('user');
$user = 'blink';
echo "fn($user)";
?>
直接访问php结果
fn(blink) //此时blink代表一个变量,错误
正确写法
<?
// $user = $_GET('user');
$user = 'blink';
echo "fn('$user')";
?>
//fn('blink')
前端
<script>
// src不受同源策略的影响
var oScript = document.createElement('script');
oScript.src = 'http://192.168.137.1/day26/php/kuayu.php?user=jennie';
document.body.appendChild(oScript);
// 回调函数
function fn(data) {
console.log(data);
}
</script>
php
<?
$user = $_GET['user'];
echo "fn('$user')";
?>
结果
打印 jennie
对象php
<?
$user = $_GET['user'];
$obj = [];
$obj['status'] = true;
$obj['data'] = $user;
$obj = json_encode($obj);
echo "fn(${obj})";
?>
百度搜索api接口
https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=key
day27
回顾
1.
商品列表跳转到详情页 根据商品的id进行数据的更改
queryString
network
get 前端携带了参数 queryString parameters 不能说明后端确实收到
post 前端携带了参数 FormData 不能说明后端确实收到
前端数据错误-- - 参数没有收到 (1 请求方式不对,2参数确实没有携带)
同源策略 域名,端口号,协议
3 跨域
跨域的解决方案
1 CORS 后端说明一下谁跨域访问我的接口
header('Access-Control-Allow-Origin: http://127.0.0.1:5500'); // 只允许指定的用户访问
header('Access-Control-Allow-Origin: *'); // 允许所有的人访问
2 jsonp 利用了src和href 不受同源策略的影响 get
创建script标签,把接口放在src属性,参数就直接拼接在后面
后端会返回给你一个函数的执行(携带了参数)
前端定义这个同名函数用于接收后端传来的参数
百度接口利用
接口
https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=key
<body>
<input type="text" id="inp">
<div class="list">
<p>
<a href=""></a>
</p>
</div>
<script>
// https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=关键字
inp.oninput = function () {
const wd = this.value;
if (!wd) {
return
}
const oScript = document.createElement('script');
oScript.src = `https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=${wd}`;
oScript.id = 'jsonp';
// 先判断这个标签是否存在
const os = document.querySelector('#jsonp');
if (os) {
os.remove()
}
document.body.appendChild(oScript);
}
// window.baidu.sug({q:"1",p:false,s:["1升等于多少毫升","1kg等于多少斤","1美元等于多少人民币","163 邮箱","192.1681.1","1吨等于多少千克","192.168.0.1 登陆页面","163","163邮箱登录","12306官网"]});
var baidu = {
sug: data => {
console.log(data);
const { s } = data;
let html = '';
s.forEach(val => {
html += `
<p>
<a href="https://www.baidu.com/s?wd=${val}">${val}</a>
</p>
`
})
document.querySelector('.list').innerHTML = html;
}
}
</script>
try
<script>
try {
JSON.parse('hello');
} catch (error) {
console.log(error);
}
console.log(ff);
</script>
地狱回调
异步
- 事件处理函数
- 定时器和延时器
- ajax
// document.onclick = function () { }
// setTimeout(function () { }, 1000)
// xhr.onreadystatechange = function(){}
1.php
<?
echo '湖北';
?>
2.php
<?
echo '武汉';
?>
3.php
<?
echo '新洲';
?>
思考:发现ajax是 异步的,执行顺序靠请求的时间,不固定
// var str = '';
// 请求接口1 str += data
// 请求接口2 str += data
// 请求接口3 str += data
// 执行完所有的主线任务,遇到异步任务就挂在任务队列里面
// 任务队列看谁先请求到数据 就在主线任务上执行
// 实际上想实现异步中的同步
// 先拿到1,确保拿到1之后再拿2,2拿到之后才能拿3
// let str = '';
// ajax({
// type: 'get',
// dataType: '',
// path: '../php/1.php',
// successCB: data => {
// str += data
// }
// })
// ajax({
// type: 'get',
// dataType: '',
// path: '../php/2.php',
// successCB: data => {
// str += data
// }
// })
// ajax({
// type: 'get',
// dataType: '',
// path: '../php/3.php',
// successCB: data => {
// str += data
// console.log(str)
// }
// })
地狱回调
// 恐怖回调 地狱回调
let str = '';
ajax({
type: 'get',
dataType: '',
path: '../php/1.php',
successCB: data => {
// 才能确认拿到数据1
str += data;
ajax({
type: 'get',
dataType: '',
path: '../php/2.php',
successCB: data => {
// 才能确认拿到数据2
str += data
ajax({
type: 'get',
dataType: '',
path: '../php/3.php',
successCB: data => {
str += data
console.log(str)
}
})
}
})
}
})
promise
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。
promise对象特点
- 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为成功fulfilled和从pending变为失败的rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。
promise 构造函数—对象
- resolve 承诺兑现 成功的
- reject 承诺没有兑现 失败的
const p = new Promise((resolve, reject) => {
console.log(1)
// 异步处理程序
setTimeout(() => {
// 产生一个奇数
var num = 37;
if (num % 2) {
resolve(num)
} else {
reject(num)
}
}, 1000)
console.log(2)
})
console.log(p)
// 许下承诺 pending-- - 一段时间去考验承诺是否兑现
// 1s之后 resolve / reject
p.then(function (num) {
console.log('成功的生成了奇数:' + num)
}, function (err) {
console.log('失败的生成了其他数:' + err)
})
</script>
promise实现Ajax
<script>
const p = new Promise((resolve, reject) => {
ajax({
type: 'get',
dataType: '',
path: '../php/1.php',
successCB: data => {
resolve(data)
},
failCB: err => {
reject(err)
}
})
});
p
.then(
data => {
console.log(data)
},
err => {
console.log(err)
}
)
</script>
promise异步图片加载
<script>
const img = `https://img.zcool.cn/community/01c2365ca2cfd7a80121416828984e.jpg@1280w_1l_2o_100sh.jpg`;
const p = new Promise((resolve, reject) => {
let oImg = document.createElement('img');
oImg.src = img;
// 图片的成功加载事件
oImg.onload = function () {
resolve(oImg);
}
// 图片加载失败事件
oImg.onerror = function () {
reject(oImg);
}
});
p.then(img => {
// resolve
document.body.append(img);
},
error => {
// 图片加载失败,使用指定图片
error.src = '../img/fail.jpg';
document.body.append(error);
}
)
封装
function loadImg(url) {
const p = new Promise((resolve, reject) => {
const oImg = document.createElement('img');
oImg.src = url;
oImg.onload = () => {
resolve(oImg)
}
oImg.onerror = () => {
reject(oImg)
}
})
return p
}
loadImg('https://img12.360buyimg.com/babel/s1180x940_jfs/t1/120286/5/18951/151642/60af7d79E1f7a0a06/5aeb32ea87bb8a63.jpg.webp')
.then(
img => {
document.body.appendChild(img)
},
errImg => {
errImg.src = '../images/1.png'
document.body.appendChild(errImg)
}
)
loadImg('https://img12.360buyimg.com/babel/s1180x940_jfs/t1/120286/5/18951/151642/6af7d79E1f7a0a06/5aeb32ea87bb8a63.jpg.webp')
.then(
img => {
document.body.appendChild(img)
},
errImg => {
errImg.src = '../images/1.png'
document.body.appendChild(errImg)
}
)
</script>
let imgArr = [`https://img.zcool.cn/community/01c2365ca2cfd7a80121416828984e.jpg@1280w_1l_2o_100sh.jpg`,
`https://img.zcool.cn/community/01b2db5c9a2849a801214168aa621b.jpg@1280w_1l_0o_100sh.jpg`,
`https://img.zcool.cn/communiy/0116355c9a284fa801214168be79ce.jpg@1280w_1l_2o_100sh.jpg`
];
imgArr.forEach(val => {
let img = document.createElement('img');
img.width = '200';
img.height = '400';
loadImg(val, '../img/fail.jpg', img);
document.body.appendChild(img);
})
//封装图片异步加载
function loadImg(url, failImgUrl, oImg,) {
const p = new Promise((resolve, reject) => {
oImg.src = url;
oImg.onload = function () {
resolve(oImg);
};
oImg.onerror = function () {
reject(oImg);
}
});
p
.then(img => {
console.log('加载成功');
},
error => {
oImg.src = failImgUrl;
// oImg.src = '../img/fail.jpg';
});
}
promise方法封装登录
ajax
// option 对象
function ajax(options) {
const {
type,
path,
data = {},
dataType = 'json',
successCB,
failCB
} = options;
let params = '';
for (let key in data) {
params += `${key}=${data[key]}&`;
}
// 去掉最后面的一个多余的&
params = params.substring(0, params.length - 1);
const xhr = new XMLHttpRequest();
// 判断数据请求的方式
if (type == 'get') {
xhr.open(type, path + '?' + params, true);
xhr.send()
} else {
xhr.open(type, path, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
xhr.send(params);
}
xhr.onreadystatechange = () => {
try {
if (xhr.readyState == 4 && xhr.status == 200) {
let data = xhr.responseText;
if (dataType == 'json') {
data = JSON.parse(data);
}
successCB && successCB(data);
}
}
// 如果ajax报错 ,这个时候程序依然会向下执行,但是这个错误可以被捕捉到
catch (err) {
failCB && failCB(err)
}
}
}
// 查询地址栏的数据参数
function queryString(name) {
var href = location.search.replace('?', '');
var arr = href.split('&');
arr = arr.map(val => {
return {
name: val.split('=')[0],
value: val.split('=')[1]
}
})
var res = arr.filter(val => val.name === name)[0];
if (res) {
return res.value
} else {
return ''
}
}
function $(e) {
return document.querySelector(e)
}
api1
api1.js
function login(data) {
return new Promise((resolve, reject) => {
ajax({
type: 'post',
path: '../php/login.php',
data,
success: val => {
resolve(val);
}
})
});
}
function reg(data) {
return new Promise((resolve, reject) => {
ajax({
type: 'post',
path: '../php/reg.php',
data,
success: val => {
resolve(val);
}
})
})
}
调用
<script src="../js/a1.js"></script>
<script>
let data = {
username: 'jennie',
password: 12
}
login(data)
.then(val => {
console.log(val);
});
reg(data)
.then(val => {
console.log(val);
});
</script>
api2
function request(data, type, path) {
return new Promise((resolve, reject) => {
ajax({
path,
data,
type,
success: val => {
resolve(val);
}
})
})
}
function login(data) {
return request(data, 'post', '../php/login.php');
}
function reg(data) {
return request(data, 'post', '../php/reg.php');
}
function list(data) {
return request(data, 'get', '../php/list.php');
}
使用
let data = {
keyword: '',
orderType: 'html',
orderSort: 'desc'
}
list(data)
.then(
val => {
console.log(val);
}
);
api3
function request(path, data, type) {
return new Promise((resolve, reject) => {
ajax({
path,
data,
type,
success: val => {
resolve(val);
}
})
});
}
// 变为表达式函数
// 因为箭头函数,只能是表达式形式
const login = data => request('../php/login.php', data, 'post');
const reg = data => request('../php/reg.php', data, 'post');
const list = data => request('../php/list.php', data, 'get');
day28
回顾
跨域
后端 cors
jsonp
后端返回的是一个带有参数的函数的执行
前端接受这个函数 写参数,接受后端传来的数据
图片服务器
异步 -- 任务队列 --- 是按时间排列
恐怖回调 想让异步任务想同步一样执行
函数嵌套比较深
耗时比较长 t1+t2+t3
promise 承诺
为了解决异步的一些问题
对象,构造函数--创建对象的工具
容器,存放的是一个结果(未来才会揭晓)
三种状态
创建的时候 pending
揭晓结果之后 fulfilled(成功) / rejected(失败)
new Promise(resolve , reject) 回调函数
.then(function(){} , function(){})
图片 onload onerror
接口的api
链式操作
链式操作能实现的根本原因 就是方法会不断的返回同样类型的数据
var str = 'hello';
var res = str.replace('l', '*').replace('l', '*');
var arr = [1, 2, 3];
var res = arr.concat(4).concat(5, 6);
promise的链式操作
// promise的链式操作
// 以下代码执行报错
// var p = new Promise();
// p
// .then() return Promise
// .then()
// .then()
promise补充
promise链式操作
promise
- then 同时接受成功和失败两个回调
- catch 只能接受失败的回调
- finally 不管成功还是失败都执行,不接受参数
var p = new Promise((resolve, reject) => {
setTimeout(() => {
var num = parseInt(Math.random() * 100);
if (num > 20) {
resolve(num)
} else {
reject(num)
}
}, 400)
})
// p
// .then(
// num => {
// console.log('成功的生成了大于20的数:' + num)
// },
// num => {
// console.log('失败的生成了不大于20的数:' + num)
// },
// )
p
.then(num => console.log('成功的生成了大于20的数:' + num))
.catch(num => console.log('失败的生成了不大于20的数:' + num))
.finally(() => console.log('不管你成功还是失败,我都要执行'))
promise地狱回调
function aa() {
return new Promise((resolve, reject) => {
ajax({
type: 'get',
dataType: '',
path: '../php/1.php',
successCB: data => {
resolve(data)
}
})
})
}
function bb() {
return new Promise((resolve, reject) => {
ajax({
type: 'get',
dataType: '',
path: '../php/2.php',
successCB: data => {
resolve(data)
}
})
})
}
function cc() {
return new Promise((resolve, reject) => {
ajax({
type: 'get',
dataType: '',
path: '../php/3.php',
successCB: data => {
resolve(data)
}
})
})
}
var str = '';
// 链式操作 不断的返回同类型的数据
// 依然是等aa执行完毕,再执行的bb t1+t2+t3
aa()
.then(data => {
str += data;
// bb(); return p
return bb()
})
.then(data => {
str += data;
return cc()
})
.then(data => {
str += data;
console.log(str)
})
promise.All
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。新Promise的状态由多个Promise实例的状态决定,所有的实例全为fulfilled时新Promise的状态才为fulfilled,只要其中有一个状态为rejected,新Promise的状态也会变成rejected。
当新Promise的状态为fulfilled时,可以拿到所有实例的成功结果集合,状态为rejected时拿到的是第一个状态为rejected实列失败的结果
-
promise的结果由多个实例的结果来决定
- 成功 必须每个实例都是成功的
- 失败 第一个失败的实例的结果
- 请求时同时执行的
t1:100ms t2:200ms t3:300ms
链式实现p.then.then.then t1+t2+t3 600ms
p.all 最长300ms ,最短100ms(第一个就失败了)
var p1 = new Promise((resolve, reject) => {
ajax({
type: 'get',
dataType: '',
path: '../php/1.php',
successCB: data => {
resolve(data)
}
})
})
function bb() {
return new Promise((resolve, reject) => {
ajax({
type: 'get',
dataType: '',
path: '../php/2.php',
successCB: data => {
resolve(data)
}
})
})
}
function cc() {
return new Promise((resolve, reject) => {
ajax({
type: 'get',
dataType: '',
path: '../php/3.php',
successCB: data => {
resolve(data)
}
})
})
}
var str = '';
// 多个实例是同时请求的 1s 500ms 600ms
// 结果集的顺序的是由数组的顺序决定
var p = Promise.all([p1, bb(), cc()]);
p
.then(data => console.log(data))
promise.all失败
- 成功的结果建立在多个实例的结果都是成功的
- 失败的结果 谁先失败就catch谁作为失败的结果
// 产生一个大于20的数
function aa() {
var p = new Promise((resolve, reject) => {
setTimeout(() => {
var num = parseInt(Math.random() * 100);
if (num > 20) {
resolve('1:' + num)
} else {
reject('1:' + num)
}
}, 400)
})
return p
}
// 产生一个小于20的数
function bb() {
var p = new Promise((resolve, reject) => {
setTimeout(() => {
var num = parseInt(Math.random() * 100);
if (num < 20) {
resolve('2:' + num)
} else {
reject('2:' + num)
}
}, 300)
})
return p
}
Promise.all([aa(), bb()])
.then(data => console.log(data))
.catch(data => console.log(data))
promise.race
谁先拿到数据,结果就显示谁的,不管成功或者失败
Promise.race([aa(), bb()])
.then(data => console.log(data))
.catch(data => console.log(data))
面向对象
面向对象基本特点
- 对象是单个实物的抽象
- 对象是一个容器,封装了’属性’和’方法’
函数的基本特点
- 可传参
- 可有返回值
- 可调用
所谓构造函数,就是提供一个生成对象的模板,并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构。
构造函数基本特点
- 构造函数的函数名的第一个字母通常大写
- .函数体内使用this关键字,代表所要生成的对象实例
- 生成对象的时候,必须使用new命令来调用构造函数
// 对象 拥有属性和方法
var obj = {
name: 'cc',
age: 18,
say: function () {
console.log('hello world')
面向对象解决问题
// 面向对象
function Dog(type, age, color) {
var obj = {
type,
color,
age,
say: function () {
console.log(`有一只${obj.age}岁的${obj.color}的${obj.type}`)
}
}
// 函数返回了一个对象
return obj
}
var dog1 = Dog('狗子', 3, '黑色');
dog1.say();
var dog2 = Dog('狗子', 2, '白色');
dog2.say();
console.log(dog1 instanceof Dog); // false
console.log(dog1.say == dog2.say) // false
// 出现了问题
// dog1和Dog之间没有血缘关系
// dog1和dog2 在内存当中各自存储自己的属性和方法
对象
var str = 'hello';
console.log(str); //hello
var str2 = new String('hello');
console.log(str2); //String {"hello"}
this指向问题
- 普通 window
- 普通函数 window
- 事件处理函数 绑定事件的对象
- 在对象里面 对象本身
console.log(this);
function fun() {
console.log(this);
}
fun();
btn.onclick = function () {
console.log(this); //<button id="btn">按钮</button>
}
var obj = {
name: 'jennie',
age: 18,
say: function () {
console.log(this); //{name: "jennie", age: 18, say: ƒ}
console.log(this.name); //jennie
}
}
obj.say();
new与this
new做了三件事
- 创建一个对象
- 把this指向改变成这个对象
- 返回了这个对象
function Person(name) {
this.name = name;
console.log(this);
}
var p1 = new Person('jennie');
console.log(p1); //Person {name: "jennie"}
console.log(p1.name); //jennie
var p2 = new Person('lisa');
console.log(p2); //Person {name: "lisa"}
console.log(p2.name); //lisa
构造函数
当一个普通的函数和new一起使用的时候, 这个函数就叫构造函数
- 为了与普通函数做区分 ,一般构造函数首字母大写
- 使用this
function Dog(type, age, color) {
// var obj = {}
// this => obj
// obj.type = type
this.type = type;
this.age = age;
this.color = color;
this.say = function () {
console.log(`有一只${this.age}岁的${this.color}的${this.type}`)
}
console.log(this)
// return this/obj
}
var dog1 = new Dog('狗子', 2, '黑色');
dog1.say()
console.log(dog1 instanceof Dog) // true 血缘关系
var dog2 = new Dog('狗子', 2, '黑色');
console.log(dog1.say == dog2.say) // false 依然各自占内存
原型对象prototype
js提供了一个属性 prototype ,实现实例化对象之间的数据共享
未使用 prototype
// function Dog(type, age, color) {
// this.type = type;
// this.age = age;
// this.color = color;
// this.say = function () {
// console.log(`有一只${this.age}岁的${this.color}的${this.type}`)
// }
// }
使用Prototype
function Dog(age, color) {
this.age = age;
this.color = color;
}
// 通过原型对象prototype来存储共有的属性和方法
Dog.prototype.type = '狗子';
Dog.prototype.say = function () {
console.log(`有一只${this.age}岁的${this.color}的${this.type}`)
}
// 类型和say方法是公用的
var dog1 = new Dog(2, '白色')
var dog2 = new Dog(3, '黑色')
console.log(dog1.say == dog2.say)
console.log(dog1)
扩展数组方法
// 数组求和的方法
Array.prototype.sum = function () {
var sum = 0;
//this代表使用这个prototype方法的实例对象
this.forEach(val => {
sum += val
})
return sum
}
var arr = [1, 2, 3];
console.log(arr.sum())
扩展date
this指代使用此方法的 实例对象
Date.prototype.format = function () {
var year = this.getFullYear();
var month = this.getMonth() + 1;
var day = this.getDate();
var hour = this.getHours();
var min = this.getMinutes();
var sec = this.getSeconds();
return `${year}-${month}-${day} ${hour}:${min}:${sec}`
}
var date = new Date();
console.log(date.format());
面向对象方式 封装选项卡
day29
NodeList循环
<p class="aa">1</p>
<p class="aa">2</p>
<script>
//通过这种方式得到的数组不能循环
var aa1 = document.getElementsByClassName('aa');
console.log(aa1); //HTMLCollection(2) [p.aa, p.aa]
// 是一个伪数组,不能forEach
// aa1.forEach(val => {
// console.log(val);
// });
for (var i = 0; i < aa1.length; i++) {
console.log(aa1[i]);
}
var aa2 = document.querySelectorAll('.aa');
console.log(aa2); //NodeList(2) [p.aa, p.aa]
aa2.forEach(val => {
console.log(val);
});
jquery封装
获取元素封装
<p>1</p>
<p>2</p>
<script>
function GetEle(ele) {
this.eles = document.querySelectorAll(ele);
}
// 为了省略new这个过程
function $(ele) {
// 返回的是一个对象
// 这个对象的eles属性利用querySelectorAll保存着查询的元素数组
return new GetEle(ele);
}
// 使用
console.log($('p')); //GetEle 是一个对象
console.log($('p').eles); //NodeList(2) [p, p]
</script>
显示与隐藏
<p>1</p>
<p>2</p>
<script>
function GetEle(ele) {
this.eles = document.querySelectorAll(ele);
}
function $(ele) {
return new GetEle(ele);
}
// 添加hidden方法
GetEle.prototype.hide = function () {
// 循环遍历所有选择的元素(可能是一个,可能是多个)
this.eles.forEach(val => {
val.style.display = 'none';
})
}
$('p').hide();
//添加show方法
GetEle.prototype.show = function () {
this.eles.forEach(val => {
val.style.display = 'block';
})
}
$('p').show();
获取/设置元素内容
- text()设置或返回所选元素的文本内容
- html()设置或返回所选元素的 内容(包含HTML标记)
- val() 设置或返回表单字段的值
html()
<p><b>black</b></p>
<p><b>pink</b></p>
<script>
function GetEle(ele) {
this.eles = document.querySelectorAll(ele);
}
function $(ele) {
return new GetEle(ele);
}
// 获取或设置html内容
// 当 获取时,选中多个元素时,获取第一个元素的内容
// 设置元素内容时,设置所有元素内容
GetEle.prototype.html = function () {
// 有参数,表示给元素设置html内容
if (arguments.length >= 1) {
// 遍历所有的元素,全部赋值
this.eles.forEach(val => {
val.innerHTML = arguments[0];
})
} else {
// 如果没有参数就返回第一个元素的innerHTML
return this.eles[0].innerHTML;
}
}
// 获取Html内容
console.log($('p').html()); //<b>black</b>
// 设置html内容
$('p').html('<i>jennie</i>');
text()
GetEle.prototype.text = function () {
if (arguments.length >= 1) {
// 设置text
this.eles.forEach(val => {
val.innerText = arguments[0];
})
} else {
return this.eles[0].innerText;
}
}
//使用
console.log($('p').text()); //black
$('p').text('lisa');
val()
GetEle.prototype.val = function () {
// 有参数,设置value
if (arguments.length >= 1) {
this.eles.forEach(val => {
val.value = arguments[0];
})
} else {
// 返回值
return this.eles[0].value;
}
}
console.log($('input').val()); //jennie
//设置
$('input').val('jisoo')
元素属性
attr
// attr 自定义属性和自有属性 无法获取或者设置表单的自有属性
// attr(“src”); 获取元素属性
// attr(“src”,“test.jpg”); //设置一个元素属性
// attr({ src: “test.jpg”, alt: “Test Image” }); //同时设置多个属性
// attr 自定义属性和自有属性 无法获取或者设置表单的自有属性
// attr("src"); 获取元素属性
// attr("src","test.jpg"); //设置一个元素属性
// attr({ src: "test.jpg", alt: "Test Image" }); //同时设置多个属性
GetEle.prototype.attr = function () {
if (arguments.length >= 2) {
// 参数有两个,设置一个属性
this.eles.forEach(val => {
val.setAttribute(arguments[0], arguments[1]);
})
} else if (arguments.length >= 1) {
if (typeof arguments[0] == 'object') {
// 设置多个属性
// 参数为对象形式
// 遍历对象
for (let key in arguments[0]) {
// 给每个元素设置属性
this.eles.forEach(val => {
val.setAttribute(key, arguments[0][key]);
})
}
} else {
// 返回属性值
return this.eles[0].getAttribute(arguments[0]);
}
}
}
<!-- datas 自定义属性 -->
<h1 class="test" datas='haha'>BLACK</h1>
<h1>PINK</h1>
<input type="radio" name="yg" checked>
<input type="radio" name="yg">
console.log($('h1').attr()); //undefined
// console.log($('h1').attr(''));
console.log($('h1').attr('datas')); //haha
$('h1').attr('myattr', 'rosie'); //设置属性
console.log($('input').attr('checked')); //空
prop
一般用于自有属性的获取,如checked,selected或class等
自有属性的获取
<input type="radio" id="one">
<input type="radio" checked id="two">
<h1>black</h1>
<h2 class="a b">pink</h2>
// 获取特殊自有属性时
var radio = document.querySelector('#one');
// 初始未设置checked
console.log(radio.getAttribute('checked')); //null
// 获取checked属性的方法
console.log(radio.checked); //false
var two = document.querySelector('#two');
console.log(two.getAttribute('checked')); //空
console.log(two.checked); //true
var h1 = document.querySelector('h1');
console.log(h1.className); //空
var h2 = document.querySelector('h2');
console.log(h2.className); //a b
// prop 针对自有 属性而言的
GetEle.prototype.prop = function () {
if (arguments.length >= 2) {
// 设置属性
this.eles.forEach(val => {
if (arguments[0] == 'class') {
// 设置class
val.classList.add(arguments[1]);
} else {
// val.id = 'a'
// val.checked = true
val[arguments[0]] = arguments[1];
}
})
} else {
// 返回自有 属性值
if (arguments[0] == 'class') {
return this.eles[0].className;
}
return this.eles[0][arguments[0]];
}
}
console.log($('input').prop('checked')); //false
$('input').prop('checked', true);
console.log($('h1').prop('class'));
$('h1').prop('class', 'orange')
css
- 返回第一个元素的指定样式 .css(‘color’)
- 设置单个样式 .css(‘color’ , ‘red’)
- 设置多个样式 .css({‘color’:‘red’ , ‘fontSize’:‘20px’})
GetEle.prototype.css = function () {
if (arguments.length >= 2) {
// 设置单个样式
this.eles.forEach(val => {
val.style[arguments[0]] = arguments[1];
})
} else if (arguments.length >= 1) {
//只有一个参数,判断 返回样式还是设置多个样式
if (typeof arguments[0] == 'string') {
// 返回样式
if (window.getComputedStyle) {
return getComputedStyle(this.eles[0])[arguments[0]];
}
return this.eles[0].currentStyle[arguments[0]];
} else {
// 是一个对象,设置多个样式
this.eles.forEach(val => {
// 遍历这个对象
for (let key in arguments[0]) {
val.style[key] = arguments[0][key];
}
})
}
}
}
console.log($('h1').css('fontSize')); //32px
$('h1').css('textDecoration', 'underline'); //设置单个样式
$('h1').css({ color: 'pink', background: 'orange' }) //设置多个样式
类名操作
addClass
- addClass() - 向被选元素添加一个或多个类
//添加一个类
$("div").addClass("important");
//添加多个类
$("#div1").addClass("important blue");
// addClass('inport')
//addClass(import blue)
GetEle.prototype.addClass = function () {
// 对参数以进行切割,返回一个类名数组
if (arguments.length >= 1) {
let parms = arguments[0].split(' ');
// 添加类名
this.eles.forEach(val => {
parms.forEach(value => {
val.classList.add(value);
})
})
}
}
//测试
$('h1').addClass('import blue');
$('h1').addClass('blue');
removeClass
// removeClass('import') -移除单个样式
// removeClass('import blue') -移除多个样式
GetEle.prototype.removeClass = function () {
if (arguments.length >= 1) {
// 参数进行切割
let params = arguments[0].split(' ');
this.eles.forEach(val => {
params.forEach(value => {
val.classList.remove(value);
})
})
}
}
// $('h1').removeClass('test');
// $('h1').removeClass('test pink');
toggleClass
该方法对被选元素进行添加/删除类的切换操作:
- 有指定的类,则移除
- 没有指定的类,则添加
// toggleClass 切换 如果原来有on就删除on 如果没有on就添加on
GetEle.prototype.toggleClass = function (a) {
this.eles.forEach(val => {
// 把classList这个伪数组变成数组
var list = [];
val.classList.forEach(v => {
list.push(v)
})
if (list.includes(a)) {
val.classList.remove(a)
} else {
val.classList.add(a)
}
})
}
eq
// eq 找到指定角标的元素
// $("p").eq(1)
// $("p").eq(-2)
GetEle.prototype.eq = function () {
if (arguments.length >= 1) {
// this.eles.forEach((val, i) => {
// if (arguments[0] == i - 1) {
// // forEach没有返回值
// return val;
// }
// })
if (arguments[0] > 0) {
return this.eles[arguments[0] - 1];
} else {
// 传过来负数
let index = Math.abs(arguments[0]);
let arr = [];
this.eles.forEach(val => {
arr.push(val);
})
// 数组反转
arr = arr.reverse();
return arr[index - 1];
}
}
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
console.log($('ul li').eq(3));
console.log($('ul li').eq(-1));
链式操作
不断的返回同类型的数据
GetEle.prototype.eq = function () {
if (arguments.length >= 1) {
if (arguments[0] > 0) {
this.eles = [this.eles[arguments[0] - 1]];
} else {
let index = Math.abs(arguments[0]);
let arr = [];
this.eles.forEach(val => {
arr.push(val);
})
// 数组反转
arr = arr.reverse();
this.eles = [arr[index - 1]];
}
// 链式操作
return this;
}
}
GetEle.prototype.css = function () {
if (arguments.length >= 2) {
// 单个样式值
this.eles.forEach(val => {
val.style[arguments[0]] = arguments[1];
});
} else if (arguments.length >= 1) {
if (typeof arguments[0] == 'string') {
// 获取样式
if (getComputedStyle) {
return getComputedStyle(this.eles[0])[arguments[0]];
}
return this.eles[0].currentStyle[arguments[0]];
} else {
// 设置多个样式
this.eles.forEach(val => {
for (var key in arguments[0]) {
val.style[key] = arguments[0][key];
}
});
}
}
// 链式操作
return this;
}
使用
$('ul li').eq(3).css('color', 'pink').css('background', 'springgreen');
// addClass('pink')
// addClass('blue pink')
GetEle.prototype.addClass = function () {
// 切割参数
if (arguments.length >= 1) {
let parmas = arguments[0].split(' ');
this.eles.forEach(val => {
parmas.forEach(value => {
val.classList.add(value);
})
});
// 链式操作
return this;
}
}
$('li').addClass('blue').addClass('black pink ');
jquery.js
总体封装
// 构造函数
function GetEle(ele) {
this.eles = document.querySelectorAll(ele);
}
// 为了省略new这个过程
function $(eles) {
var p = new GetEle(eles)
return p
}
GetEle.prototype.hide = function () {
this.eles.forEach(val => {
val.style.display = 'none'
})
}
GetEle.prototype.html = function () {
// 参数的长度大于等于1时,表示有参数
if (arguments.length >= 1) {
this.eles.forEach(val => {
val.innerHTML = arguments[0]
})
}
// 如果没有参数就返回第一个元素的值
else {
return this.eles[0].innerHTML
}
}
GetEle.prototype.text = function () {
// 参数的长度大于等于1时,表示有参数
if (arguments.length >= 1) {
this.eles.forEach(val => {
val.innerText = arguments[0]
})
}
// 如果没有参数就返回第一个元素的值
else {
return this.eles[0].innerText
}
}
// eq 找到指定角标的元素 equal
// .eq(2)
GetEle.prototype.eq = function (index) {
for (var i = 0; i < this.eles.length; i++) {
if (i == index) {
// return this.eles[i - 1]
// this.eles = this.eles[i - 1] // 数据类型就会改变
this.eles = [this.eles[i - 1]]
}
}
return this
}
// 返回第一个元素的指定样式 .css('color')
// 设置单个样式 .css('color' , 'red')
// 设置多个样式 .css({'color':'red' , 'fontSize':'20px'})
GetEle.prototype.css = function (a, b) {
// 设置单个样式
if (arguments.length >= 2) {
this.eles.forEach(val => {
// val.style.width = '200px'
val.style[a] = b;
})
} else {
if (typeof a == 'string') {
// 只能返回行内样式
// return this.eles[0].style[a]
if (window.getComputedStyle) {
return getComputedStyle(this.eles[0])[a]
}
return this.eles[0].currentStyle[a]
} else {
// 对象
this.eles.forEach(val => {
// 设置多个样式 .css({'color':'red' , 'fontSize':'20px'})
for (let key in a) {
val.style[key] = a[key]
}
})
}
}
return this
}
// toggleClass 切换 如果原来有on就删除on 如果没有on就添加on
GetEle.prototype.toggleClass = function (a) {
this.eles.forEach(val => {
// 把classList这个伪数组变成数组
var list = [];
val.classList.forEach(v => {
list.push(v)
})
if (list.includes(a)) {
val.classList.remove(a)
} else {
val.classList.add(a)
}
})
return this
}
day30
封装链式操作
function GetEle(ele) {
this.eles = document.querySelectorAll(ele);
}
function $(ele) {
return new GetEle(ele);
}
GetEle.prototype.toggleClass = function () {
this.eles.forEach(val => {
// 变为真的数组
let list = [];
val.classList.forEach(value => {
list.push(value);
});
if (list.includes(arguments[0])) {
// 存在移除
val.classList.remove(arguments[0]);
} else {
// 添加
val.classList.add(arguments[0]);
}
})
return this;
}
GetEle.prototype.eq = function () {
// this.eles = [this.eles[arguments[0]]];
this.eles.forEach((val, i) => {
if (arguments[0] == i) {
this.eles = [this.eles[arguments[0]]];
}
})
return this;
}
sibling封装
// 找到所有的兄弟元素
GetEle.prototype.siblings = function () {
var parent = this.eles[0].parentNode;
var children = parent.children;
var list = [];
// 是一个htmlCollection 不能使用forEach
for (var i = 0; i < children.length; i++) {
if (children[i] != this.eles[0]) {
list.push(children[i]);
}
}
this.eles = list;
return this;
}
$('#two').siblings().css('color', 'pink');
事件绑定
GetEle.prototype.on = function () {
// 一般事件绑定
// $('p').on('click',function(){})
if (arguments.length == 2) {
this.eles.forEach(val => {
val.addEventListener(arguments[0], arguments[1]);
})
}
// $('body').on('click','#black',function(){})
// id为p的事件委托
if (arguments.length > 2) {
// 事件委托
// 第二个参数,选择器 #black
let select = arguments[1];
// 回调函数
let callback = arguments[2];
this.eles.forEach(val => {
val.addEventListener(arguments[0], function (e) {
e = e || event;
var target = e.target || e.srcElement;
if (target.id == select.replace('#', '')) {
callback();
}
})
})
}
}
//一般事件绑定
$('p').on('click', function () {
console.log(888);
})
//事件委托
$('body').on('click', "#black", function () {
console.log('事件委托');
})
this指向
- 一般情况下,this—>window
- 普通函数 this—>window
- 对象里面 this-> 对象本身
- 在事件处理函数中 this-> 绑定事件的元素
- 箭头函数 没有自己的指向 爹指向谁,我就指向谁
console.log(this); //window
function fn() {
console.log(this); //window
}
fn();
var obj = {
name: 'IU',
age: 23,
say: function () {
console.log(this); //{name: "IU", age: 23, say: ƒ},指向对象本身
}
}
obj.say();
document.onclick = function () {
console.log(this); //#document
}
箭头函数
var fn = () => {
console.log(this); //window
}
fn();
var obj = {
name: 'JENNIE',
fn: () => {
console.log(this); //window
}
}
obj.fn();
箭头函数this
箭头函数自己没有指向,指向父元素的指向。父元素的this
var fn = () => {
console.log(this); //window
}
fn(); // window.fn fn的this实际指向window 因此 箭头函数 window
var obj = {
name: 'jennie',
fn1: function () {
console.log(this); // this -> obj
},
fn2: () => {
console.log(this); // window.obj this -> window
}
}
obj.fn1(); //obj
obj.fn2(); //window
var obj = {
name: 'IU',
fn: function () {
console.log(this); //obj
return () => {
console.log(this); // fn()obj
return () => {
console.log(this); //fn()()obj
}
}
}
}
obj.fn();
obj.fn()();
obj.fn()()();
事件绑定的箭头函数
document.onclick = function () {
console.log(this); //#document
}
document.onclick = () => {
console.log(this); //window
}
var obj = {
name: 'cc',
init: function () {
console.log(this); //obj
document.onclick = () => {
console.log(this); //obj
}
}
}
obj.init();
箭头函数使用实例
可以利用箭头函数,保留this,(因为箭头函数自己指向,依赖于父元素的指向)
Banner.prototype.autoPlay = function () {
clearInterval(this.timer);
let that = this;
//定时器的this指向会变,利用箭头函数,保留this指向
//因为箭头函数自己没有指向
this.timer = setInterval(function () {
console.log(this); //window
that.index++;
if (that.index >= that.lis.length) {
that.index = 0;
}
that.activeOne();
}, 2000);
}
//改变后
Banner.prototype.autoPlay = function () {
clearInterval(this.timer);
//定时器的this指向会变,利用箭头函数,保留this指向
this.timer = setInterval(() => {
this.index++;
if (this.index >= this.lis.length) {
this.index = 0;
}
this.activeOne();
}, 2000);
}
this指向小结
-
普通函数 this – window
-
事件处理函数 this – 绑定事件的标签
-
对象里面 this – 对象本身
-
箭头函数 自身没有this指向 父元素指向谁,箭头函数就指向谁
总结:注意箭头函数的使用场景
普通函数和箭头函数的区别
写法不一样
this指向也不一样
使用场景: 如果this有了一个固定的指向之后,不想让这个指向发生改变,就在这个函数内部统一使用箭头函数
this如果发生改变
- 通过变量保存住这个this
- 通过箭头函数,保留住this指向
原型链
-
所有的对象都拥有 proto 属性 原型
-
prototype 属性对象 只有函数拥有
-
原型 proto
-
函数 prototype 和__proto_ _ _
原型链的应用
var arr = [1, 2, 3];
var arr2 = [3, 4, 5];
// 相当于Array.prototype
arr.__proto__.sum = function () {
var res = 0;
this.forEach(val => {
res += val;
})
return res;
}
// Array.prototype.sum = function () {
// var res = 0;
// this.forEach(val => {
// res += val
// })
// return res
// }
console.log(arr.sum());
console.log(arr2.sum());
伪数组
HTMLCollection和NodeList的prototype不同
- NodeList可以forEach而HTMLCollection不能,是因为NodeList的Prototype有forEach方法
<p>1</p>
<p>2</p>
<p>3</p>
<script>
var aa = document.getElementsByTagName('p');
console.log(aa)
// aa.__proto__ HTMLCollection 没有forEach , 但是有length
var bb = document.querySelectorAll('p');
console.log(bb)
// bb.__proto__ 原型上拥有forEach NodeList.prototype.forEach=function()
<body>
<p>1</p>
<p>2</p>
<p>3</p>
<script>
var aa = document.getElementsByTagName('p');
console.log(aa); //HTMLCollection
var bb = document.querySelectorAll('p');
console.log(bb); //NodeList
// bb.forEach(val => {
// console.log(val);
// })
// 在变为真正的数组之前保留length,因为其原型变为Array后没有了length数学
var len = aa.length;
// 将伪数组变为真正的数组(改变其指向)
aa.__proto__ = Array.prototype;
console.log(aa);
aa.length = len;
aa.forEach(val => {
console.log(val);
})
// aa.pop(); //报错 Index property deleter is not supported.不支持
call和apply和bind
-
call,apply,bind 在函数的执行过程中改变this指向
-
call(newObj,arguments,arguments)
-
apply(newObj,[arguments,arguments])
-
bind(newObj,arguments,arguments)() 返回的是一个函数,必须执行一下
箭头函数本身没有指向—它的指向是不会被手动改变的
// 在函数执行过程中改变this指向
// call
// apply
// bind
var name = 'window';
var obj = {
name: 'dog',
age: 18,
say: function (num1, num2) {
console.log(this.name);
console.log(num1);
},
speak: () => {
console.log(this);
}
}
obj.say(5); //dog 5
obj.say.call(window, 6, 6); //window 6
var obj2 = {
name: 'obj2'
}
// call(newObj,argument,argument)
obj.say.call(obj2, 7, 8); //obj2 7
// apply
obj.say.apply(obj2, [4, 5]); //obj2,4
// bind 返回的是一个函数,需要再次执行
console.log(obj.say.bind(obj2, 3, 4));
// ƒ (num1, num2) {
// console.log(this.name);
// console.log(num1);
// }
obj.say.bind(obj2, 3, 4)(); //obj2 3
day31
知识点回顾
1 cookie 设置cookie 获取cookie 删除cookie
2 webStorage
3 区分 cookie / localstorage / sessionStorage
4 同源策略
5 跨域 域名 / 端口号 / 协议
CORS
jsonp
6 获取地址栏的参数 列表跳转到详情页 querySelector
7 promise
为了解决异步 问题
本质上是一个构造函数 函数本身也是一个对象
方法
实例化对象的方法 .then() .catch() .finally()
Promise.all([p1,p2,p3]) Promise.race([p1,p2])
8 面向对象 封装 , 继承,多态
面向过程 面向对象 -- 原型链 追根溯源
构造函数的封装
首字母大写
this
prototype 原型对象
选项卡 轮播图 数组求和 日期对象 jq
this指向 4种指向(普通函数/事件处理函数/对象/箭头函数) call / apply / bind
原型链 __proto__ 原型 prototype 原型对象
每一个对象都有 __proto__ 原型
每一个构造函数都有prototype属性对象
实例化对象原型链
dog1.__proto__ === Dog.prototype
Dog.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
构造函数原型链
Dog.__proto__ === Function.prototype
Function.prototype.__proto === Object.prototype
Object.prototype.__proto__ === null
继承
继承:子类当中拥有父类的属性的方法
// 父类
function Dog() {
this.type = '狗子';
this.say = '汪';
}
// 子类
function KeJi(name, color) {
this.type = '狗子';
this.say = '汪';
// this.name = name;
// this.color = color;
}
构造函数继承
构造函数继承: 主要的实现方法就是改变父类的this指向
缺点:无法继承父类的原型对象(prototype)上的 属性和方法
<script>
function Dog() {
this.type = '狗子';
this.say = '汪';
}
Dog.prototype.speak = function () {
console.log(this.say);
}
// 子类
function Keji(name, color) {
// 执行Dog这个函数,并改变this指向
/* this.type = '狗子';
this.say = '汪'; */
Dog.call(this);
this.name = name;
this.color = color;
}
var kj = new Keji('kuma', '黑色');
console.log(kj);
</script>
原型链继承
原型链式继承: 让子类的prototype指向父类的实例化对象 (子类的原型对的constructor -> 子类)
// 缺点:子类无法向父类传参
<script>
function Dog(age) {
this.type = '狗子';
this.say = '汪';
this.age = age;
}
Dog.prototype.speak = function () {
console.log(this.say);
}
// 子类
function Keji(name, color) {
this.name = name;
this.color = color;
// 实例化对象会优先找自己的构造函数的属性或方法,如果自己没有,就找prototype上的属性和方法
this.say = 'wang';
}
// 无法传参,参数固定了
Keji.prototype = new Dog(5);
// 改变构造器, 加和不加这一句,不影响继承, 只不过原型链会出现错乱
Keji.prototype.constructor = Keji;
var kj = new Keji('kuma', '黑色');
console.log(kj);
</script>
jquery-选项卡
<div class="tab">
<ul>
<li class="on">1</li>
<li>2</li>
<li>3</li>
</ul>
<ol>
<li>111</li>
<li>222</li>
<li>333</li>
</ol>
</div>
<script src="../js/jquery.js"></script>
<script>
$('ol li').eq($('.on').index()).show();
$('ul li').each(function (index, ele) {
console.log(index, ele);
$('ul li').eq(index).on('click', function () {
$(this).addClass('on').siblings().removeClass('on')
.parent().parent().children('ol').children('li').eq($(this).index()).show().siblings().hide();
})
})
</script>
组合继承
组合继承:构造函数继承+原型式继承 — 解决了传参的问题
缺点:原型链上出现了多余的属性和方法
<script>
function Dog(age) {
this.type = '狗子';
this.say = '汪';
this.age = age;
}
Dog.prototype.speak = function () {
console.log(this.say);
}
// 子类
function Keji(age, name, color) {
Dog.call(this, age)
this.name = name;
this.color = color;
this.say = 'wang';
}
Keji.prototype = new Dog();
// 改变构造器
Keji.prototype.constructor = Keji;
var kj = new Keji(3, 'kuma', '黑色');
console.log(kj);
</script>
组合继承优化
组合继承:构造函数继承+原型对象的继承 不推荐使用
缺点:子类的原型对象的改变会影响父类 – 共享了地址prototype
<script>
function Dog(age) {
this.type = '狗子';
this.age = age;
}
Dog.prototype.speak = function () {
console.log(66)
}
function KeJi(name, color, age) {
// 继承构造函数的属性和方法
Dog.call(this, age);
this.name = name;
this.color = color;
}
// 继承原型对象的属性和方法
// 子类和父类指向了同一个原型对象,也就是共享了同一个地址 -- 浅拷贝
KeJi.prototype = Dog.prototype;
KeJi.prototype.constructor = KeJi;
// 子类的原型对象的改变会影响父类
KeJi.prototype.ss = 8;
var kj = new KeJi('柯基', '白色', 2);
console.log(kj)
console.dir(Dog);
console.log(Dog.prototype)
</script>
拷贝继承
// 拷贝继承:构造函数 + 原型对象的深拷贝
<script>
function Dog(age) {
this.type = '狗子';
this.say = '汪';
this.age = age;
}
Dog.prototype.speak = function () {
console.log(this.say);
}
// 子类
function Keji(age, name, color) {
Dog.call(this, age)
this.name = name;
this.color = color;
this.say = 'wang';
}
//拷贝prototype方法
for (var key in Dog.prototype) {
Keji.prototype[key] = Dog.prototype[key];
}
var kj = new Keji(3, 'kuma', '黑色');
console.log(kj);
</script>
day32
回顾
js 继承
1 单一:构造函数的继承 -- 没有继承父类的prototype call / apply / bind
2 单一:原型链式继承 -- 无法向父类传参 Child.prototype = new Parent()
3 组合继承
3.1 构造函数 + 原型链式 原型链上多出Parent的构造函数里面的属性和方法
3.2 构造函数 + prototype
3.2.1 Child.prototype = Parent.prototype 地址相同
3.2.2 拷贝继承 Child.prototype = {...Parent.prototype} 深拷贝
3.2.3 寄生式继承 F(){} F.prototype = Parent.prototype Child.prototype = new F()
注意点:当child的prototype的指向改变了,就一定要加 Child.prototype.constructor = Child
4 Es6继承 -- 语法糖 extends super
juery
parents() parent()
find() children()
siblings() next() prev() nextAll() prevAll()
index() 在同级元素中所排的角标
filter first last not eq()
html / val / text
each 遍历
on
attr-选项卡
- attr 自定义属性 常见的自由属性
- prop 特殊的自有属性 checked
html
<div class="box" id="box1">
<input type="checkbox" class="checkAll"><br>
<input type="checkbox" class="checkOne">
<input type="checkbox" class="checkOne">
<input type="checkbox" class="checkOne">
<input type="checkbox" class="checkOne">
</div>
js
<script src="../js/jquery.js"></script>
<script>
class Check {
constructor(e) {
this.ele = $(e);
this.checkAll = this.ele.children('.checkAll');
this.checkOnes = this.ele.find('.checkOne');
this.init();
}
init() {
var that = this;
this.checkAll.click(function () {
// 全选
that.checkOnes.prop('