1.函数提升问题
1.1用var 声明的变量 都具有变量提升 会提升到当前作用域的顶部变量会提升 但赋值发生在原地
1.2 函数声明会整体提升,函数表达式的提升规则古河变量提升规则
console.log(test2); // undefined
test2();// 报错 赋值发生在原地 此时test2 还不是一个函数 而是undefined
var test2 = function() {
console.log("====test2 ====== ");
}
test2();
1.3 函数提升优先于变量提升
// 解析后的代码如下
// function test3(){
// }
// var test3;
// test3 = 10
此处会报错.......
var test3 = 10;
function test3 () {
console.log("hello world");
}
test3();
2.let const 与var的区别
var 不存在块的概念,可以跨块访问变量,但是不能跨域访问变量
2.1.let 拥有块级作用域 2.不存在变量提升 3.不能重复命名 4.可以先声明后赋值
在初始化之前不能访问 c变量
2.2const 常量
1.拥有块级作用域 2.不存在变量提升 3.不能重复命名 4.必须初始化时有值 (即不能先声明后赋值) 5.不能修改常量 否则报错
3.argument 实际上是一个伪数组 可以使用 length属性 可以像数组一样取值 但不能使用数组的API
把伪数组强制转换伪数组对象 Array.from() from 静态方法 必须是Array 大写的调用 console.log(arguments instanceof Array);// false
function fn4() {
if (arguments.length === 0) return;
// 实际上是一个伪数组 可以使用 length属性 可以像数组一样取值 但不能使用数组的API
// 把伪数组强制转换伪数组对象
// Array.from() from 静态方法 必须是Array 大写的调用
// console.log(arguments instanceof Array);// false
var newArray = Array.from(arguments);
var max = newArray[0]
newArray.forEach(function (v) {
if (v > max) max = v;
})
return max;
}
console.log("max = ", fn4(1, 2, 21, 1, 3, 45, 34, 3));
4.Array.isArray()与instanceof 的意义
1.Array.isArray()判断一个对象是否为数组 返回值为true 或者 false
let array = [1,2,3,4];
let result = Array.isArray(array);//true·······
- instanceof instance 实例(对象)
// 判断是否为一个对象
var a = {};
var b = [1,2];
console.log(a instanceof Object);
console.log(b instanceof Array);
5.数组常见的api
1.
var arr = [9,8,4,2,1];
// includes 包含
// 参数1: 要查找的值
// 参数2: 可选的 开始的索引位置 默认从0开始
// 返回值 true or false
var result = arr.includes(8,1);
console.log("找到了没 == ",result);
2.
var arr = [9,8,4,2,1,8];
// indexOf 查找目标元素的第一次出现的索引
// 找到就返回第一次出现的索引值 找不到则返回-1
var result = arr.indexOf(1,0);
console.log("result = ",result)
3.
// lastIndexOf(8,3) 是倒着数
var arr = [9,8,4,2,1,8];
var result = arr.lastIndexOf(8,3);
console.log("result = ==== ",result)
4.
var arr = [4,5,2,1];
// 1.
// push 推 往数组末尾追加
// 返回的是追加后数组的长度
// 参数:可以同时追加多个
let num = arr.push(789);
// console.log("num = ",num);
console.log("num = ",arr);
5.
// 2.unshift 添加到数组的开头
// 返回的是追加后数组的长度
// 参数:可以同时追加多个
let num1 = arr.unshift(100,200);
console.log("num1 = ",num1);
console.log("num1 = ",arr);
6.
// 3.splice 截取和追加 拼接
// 两个参数时 是截取数组 会改变原数组
// 参数1: 开始的位置 (包含开始位置)
// 参数2: 截取的个数 默认从开始位置到结束
// 返回值: 被截取的内容组成的新数组
var arr = [4,5,2,1];
// let newArr = arr.splice(1,2);
// console.log("原数组: ",arr);
// console.log("新数组: ",newArr);
// 三个参数时 是追加,追加在截取的位置
var newArr = arr.splice(1, 2, 789, 123, 125);
console.log("原数组: ", arr); //[4, 789, 123, 125, 1]
console.log("新数组: ", newArr); // [5, 2]
7.
// 4.pop 弹出 删除数组中最后一个元素
// 参数:无
// 返回值 被删除的元素
var arr = [4,5,2,1];
var x = arr.pop();
console.log("原数组: ",arr);
console.log("x = : ",x);
8.
// 5.slice 切片
// 剪切
// 参数1:start 开始索引 包含
// 参数2: end 结束的索引 不包含
// 返回值 被截取的元素 组成的新数组
// 不改变原数组
var arr = [4,5,2,1];
var newArr = arr.slice(1,2);
console.log("原数组: ",arr);
console.log("新数组: ",newArr);
9.
// 6.shift 删除数组中的第一个
// 参数: 无
// 返回值 别删除的第一个元素的值
// 改变原数组
var arr = [4,5,2,1];
var x = arr.shift();
console.log("原数组: ",arr);
console.log("x : ",x);
10.
// 7.join 连接
// 参数: 可选 字符串 字符串的分隔符
// 返回值 数组元素组成的字符串 默认以,分隔
var arr = [4,5,2,1];
var str = arr.join("xxxx");
console.log("str= ",str);
console.log("原数组: ",arr);
11.
// 8.把数组转换成字符串
var arr = [4,5,2,1];
var str = arr.toString()
console.log("str= ",str);
12.
// 9.forEach方法
// 遍历的方法
var arr = [4,5,2,1];
// foreach 有一个回调函数
// 参数1: 值
// 参数2: 索引
// 参数3: 数组本身
// 记住参数有顺序问题
arr.forEach(function (v,i,a) {
console.log(" === v ==",v, "index = ",i ,"array =",a);
})
//数组高级api
1.
//1. find 方法
方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
var arr = [1, 5, 8, 10];
// find方法的参数 是一个函数 测试函数
// 测试函数 需要返回值的 而测试函数的返回值 为true 或 false
// find方法 也是有返回值的
// 数组依赖于测试函数的返回值 如果测试函数返回true 则得到对应value值 如此题为8
// 如果测试函数 条件都不满足时 返回false 则返回 undefined的
var num = arr.find(function (v, i, arr) {
console.log(" ==== 测试 ====");
if (v > 5) {
return true;
}
else {
return false;
}
});
console.log("num = ", num);//8
2.
// 2. 找索引findIndex
findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
var arr = [1, 5, 8, 10];
// 所有的高级api 其实把函数做为参数进行传递
//测试函数的返回值 决定着 高级函数的返回值
var index = arr.findIndex(function (v, i, array) {
if (v > 5) {
return true;
}
else {
return false;
}
})
console.log("==== index === ", index);//2,数组得到对应值的索引
3.//indexOf()
方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
4.// 3. map 映射
// map返回一个新的数组
// foreach map
// arr.forEach();方法没有返回值 void空的
// map 返回一个新的数组
var arr = [1, 5, 8, 10];
var newArray = arr.map(function (v, i, array) {
return v * 10;
})
console.log("===== new Array ", newArray);
4.
// 4.filter 过滤
// 满足测试函数条件 组成的新数组
var arr = [1, 5, 8, 10];
var newArray = arr.filter(function (v, i, array) {
return v > 5;
});
console.log("===== new Array filter ", newArray);//[8,10]
5.
// 5.some 一些
5.1 // 只要测试函数 有一次返回为true some函数即返回为true
var arr = [1, 5, 8, 10];
var res = arr.some(function (v, i, array) {
return v > 5;
});
console.log("res some === 0", res);//true
5.2
var arr = [1, 2, 3];
var bloon = arr.some(v => {
if (v > 2) {
console.log(111);
return true; //注意用some方法如果不写return true;那bloon会一直为false//其实some的意思也就是只要有一次是返回true的那结果就为true
}
})
console.log(bloon); //true
6.
// 6.every 所有
// 测试函数 都满足条件时 返回为true 有一个返回为false 则 every函数返回为false,那必然如果不写return是不可以的,但对于箭头函数有时候可以省略return
var arr = [1, 5, 8, 10];
var res = arr.every(function (v, i, array) {
return v >= 1;
});
console.log("res 2 every=== ", res);//true
//every函数还可以用来遍历数组,前提是return true;如果哪次遍历的时候返回false则,不执行下面的遍历,可以模拟break和continue;注意在用foreach方法遍历数组的时候里面是不可以写break和continue的会报错
(1) arr.every(function(v) {
if (v == 5) {
// 当为5的时候跳过
} else {
console.log(v);
}
return true;
})//打印结果为1,8,10除了5
(2)
arr.every(function(v) {
if (v == 5) {
return false;
}
console.log(v);
return true;
})//执行结果为1,5和5之后的数字将不执行,对于every遇到一次返回值为false执行也就不在继续了
7.数组中用for in的方法遍历数组是支持break和continue的,但注意遍历时候的参数index索引是字符串不是数字
for (let index in arr){
if(index==2){//此时的==为两个=,不能为三个=
break;
}
console.log(index,arr.[index]);
}
7.
// 7.reduce 聚合 ,根据返回值的特点可以用来求最大小值和求总和
//测试函数的返回值 会做为下次执行时的previousValue
// 1.
var arr = [1, 5, 8, 10];
var num = arr.reduce(function (previousValue, currentValue, currentIndex, arrray) {
console.log("previousValue ", previousValue);
console.log("currentValue ", currentValue);
console.log("currentIndex ", currentIndex);
return previousValue + currentValue;
})
console.log("num = ", num);//24
// 2 使用reduce方法实现 找到最大值
var arr = [101, 53, 108, 123];
// 测试函数的返回值 会做为下次执行时的previousValue
var num = arr.reduce(function (previousValue, currentValue, currentIndex, arrray) {
if(currentValue > previousValue) return currentValue;
else return previousValue;
})
console.log("num = ", num);//123
// 3 reduce 方法拥有两个参数
// params1: 回调函数
// params2: 初始值
var arr = [1, 2, 3, 4];
// 测试函数的返回值 会做为下次执行时的previousValue
var num = arr.reduce(function (previousValue, currentValue, currentIndex, arrray) {
return previousValue + currentValue;
},100)
console.log("num = ", num);//110
8.
// 8.sort 排序
// return a - b 是升序
// return b - a 是降序
var arr = [101, 53, 108, 123];
arr = arr.sort(function (a,b) {
return b-a;
});
console.log("排序后的数组 == ",arr)
6. Date时间对象
var date = new Date();
console.log(date);
1.
// 年
var year = date.getFullYear();
console.log(year);
2.
// 月 0- 11
var month = date.getMonth();
console.log(month);
3.
// 日
var day = date.getDate();
console.log(day);
4.
// 获取 时分秒
var hours = date.getHours();
console.log(hours);
var minutes = date.getMinutes();
console.log(minutes);
var seconds = date.getSeconds();
console.log(seconds);
5.
// 星期
// 周日 0 - 6
var week = date.getDay();
console.log(" === ", week);
switch (week) {
case 0:
week = "周日";
break;
case 1:
week = "周一";
break;
default:
break;
}
6.
// 获得是从 现在距离 1970年1月1日 0 点0分 (世界标准时间 格林威治时间 ) 的 总毫米数
console.log(" ===== ",date.getTime());//实例方法
//
console.log("====== ", Date.now());//静态方法
7.stringAPI
var str = "helloworld";
// var str = 'helloworld';
// 1.获取字符串的长度
console.log("长度 ===", str.length);
// 2.遍历一般用for of 取值,用for in 取到的是索引号
// 没有foreach方法
// str.forEach(element => {
// console.log("element = ",element);
// });
for (const v of str) {
// console.log("====v ==",v);
}
// 3.转换大写 小写字母
var newStr = str.toUpperCase();// 大写
console.log("new str ", newStr);
var newStr2 = newStr.toLowerCase();// 小写
console.log("new str2 ", newStr2);
// 4.以...开始 或结尾
var str = "helloworld";
console.log(str.startsWith("hell", 2));// fasle
console.log(str.endsWith("d"));// true
// 5.查找字符的索引,indexOf()
console.log(str.indexOf("he"));
// 6.includes 是否包含
console.log(str.includes("he"));
// 7.slice 切片,不改变原字符串的值
console.log(str.slice(1, 3)); // 包含开始 不包含结束 string没有splice的api
// 8. split 分隔
var url = "username=admin&password=123";
var array = url.split("&");// ['username=admin', 'password=123'],这可以用来把字符串转化为数组,而数组可以用array.join('')转化为字符串
for (var str of array) {
var arr = str.split("=");
for (var value of arr) {
console.log("截取的值:", arr[1]);
}
}
var url = "username=admin&password=123&gf=3";
// limit 限制切成几段
var array = url.split("&", 2);
console.log(array);
// 9.replace 替换
var str = "hello";
str = str.replace("el","大头鬼");
console.log("replace = ",str);
// 10.concat 连接
var a = "a";
var b = "b";
var c = a.concat(b);
console.log("concat = ",c)
var c = a.concat(...[1,2,3,4,5,67]);// 传递多个
console.log("concat = ",c)
// 11.charAt 根据索引查找字符
var str = "abc";
console.log(" === ",str.charAt(0));
// 12.charCodeAt() 查找对应字母ASCII码值 A65 a97
var str = "abc";
console.log(" === ",str.charCodeAt(0));
// 13. substring 截取字符串
var str = "helloworld";
console.log(str.substring(1,3));//el
console.log(str);//"helloworld"
// 14.trim 去除首尾空格
var str = " a b c ";
str = str.trim()
console.log("除去首尾空格 =",str);
8.构造函数的内部原理
1.在函数体最前面隐式的加上this={};其实也不是空对象里面有this ={
–proto–:Person.prototype;指向原型
}
2.执行this.xxx=xxx;
3.隐式的返回this这个对象;
function Student(name, age, sex) {
//var this={
// name:'';
// age:'';
// }
this.name = name;
this.age = age;
this.sex = sex;
this.grade = 2019;
//return this;
}
var student = new Student('xicuui', 23, 'meale');
7.包装类
var str = new String('acc');
var bol = new Boolean('true');
var num = new Number(123);
num.name = 'xx';
console.log(num);
//var kong = new Null('null');,报错,undefined和null不可以设置
// console.log(kong);
1.原始值是不能有属性和方法的;对象可以有(数组,对象本身,函数)
//对于对象
var arr = [1, 2, 3, 4];
arr.length = 2;
console.log(arr); //[1,2]
//对于原始值 number
var num = 4;
num.len = 3; //系统会自动 new Numder(4).len=3; 然后delete
console.log(num.len); //undefined//因为当你访问的时候,系统会 new Numder(4).len 和上个new Number不是一个,所以并没有赋值,结果为undefined
//对于原始值string有一点·特殊是string可以访问.length属性
varstr = 'abcd';
str.length = 2;// str.length不是可以写的,但是可读
console.log(str); //'abcd'//系统会new String('abcd').length=2; delete
console.log(str.length); //4//系统会new String('abcd').length,然后这个创建的对象字符的长度为4,所以可以出现结果为4
//再来一题
var str='abc';
str+=1;
var test=typeof(str);
if(test.length==6){
test.sign='type地返回值';//系统会new String(test).sign='xxx';
}
console.log(test.sign);//undefined//new String(test).sign因为和双面构造的test对象不是一个对象所以结果为undefined
9.set集合 类似于数组一样的容器 但其值唯一 无序 数组是有序的
var set = new Set([1, 2, 3, 1]);//{1,2,3}
// set 添加add array push
set.add(20);
// 删除
set.delete(10);
// 清除所有的
set.clear();
// 属性的调用 直接对象.属性;
// 对象.方法();
var size = set.size;
// 检验集合中中是否包含某个值
var you = set.has(10);
var set = new Set(["a", "b"]);
// set 也是可以使用foreach方法的
set.forEach(function(value1, value2, set) {
// value1 value2都是指向 集合中的值,没有区别//因为set无序,所以并没有索引号
console.log("value1 =", value1, "value2 =", value2) //执行结果第一次为a a 第二次为b b
})
set.forEach(function(value1, set) {
// 执行的结果为第一次为a,第二次为b
console.log("value1 =", value1)
})
set.forEach(values => {
});//箭头函数时的额写法
// set 可以通过 es6 提出的 for of 进行遍历
for (var a of set) {
console.log("a ==== ", a);
}
console.log(set);
10 浅拷贝与深拷贝
// 3. ... 展开运算符
// 浅拷贝 只拷贝指针 两个变量依然指向同一个对象 修改的是同一块内存地址
var array2 = ["a","b"];
var array3 = array2;
array2[0] = "c";//此时array2和array3都变为['c','b']
console.log(array3 === array2);
// 3.1 赋值数组 把数组的每一项 展开另外一个数组中 此处为深拷贝 ,但array2内部的引用类型,依然是浅拷贝
var array3 = [...array2];
console.log(array2 == array3); //false
console.log(array2 === array3); //false
//虽然两个的值相同数据类型相同但是地址不相同
array2[0] = "c";
console.log(array2); //['c','b']
console.log(array3); //['a','b']
11. … 展开运算符的作用
// 3.1合并数组
var array4 = ["a", "c"];
var array5 = ["e", "g"];
var array6 = [...array4, ...array5];
console.log(array6);
// 3.2 传递多个参数时使用
function test(a, b) {
return a + b;
}
var arr=[1,2];
console.log(test(...arr));//不用写成test(arr[0],arr[1]);
// 3.3 把字符串转换为数组
var str = "hello";
var array7 = [...str];
["h", "e", "l", "l", "o"];
console.log(array7);
11.合并数组的一些方法
//1.合并数组...展开运算符
var array4 = ["a", "c"];
var array5 = ["e", "g"];
var array6 = [...array4, ...array5];
console.log(array6);
//2..cancat( 不改变原数组。concat合并数组之后,返回值才是新数组,并且可以合并两个及其以上的数组)
let temparr2 = temparr.concat([7,8])
console.log(temparr)//[1,2,3,4]
console.log(temparr2)//[ 1, 2, 3, 4, 7, 8 ]
//3.push.apply( 合并数组是把后一个数组的值依次push进前一个数组,使前一个数组发生改变,并且只能两个数组之间发生合并。)
let arr1 = [1,2,3,4];
let arr2 = ['a','b','c','d'];
arr1.push.apply(arr1,arr2)
console.log(arr1) //[ 1, 2, 3, 4, "a", "b", "c", "d" ]
11.数组去重的一些方法
1.第一种方式就是最简单的set去重
var arr = [1,2,2,4,3,4,1,3,2,7,5,6,1]
var newArr = new Set(arr)
2.第二种方式就是遍历数组,创建一个新数组,用indexOf来判断新数组中某个数字是否存在
function fn(arr){
let newArr = []
arr.forEach((val)=>{
if(newArr.indexOf(val) == -1){
newArr.push(val)
}
})
return newArr
}
3.遍历数组,用splice()方式普通去重
for(var i=0;i<arr.length;i++){
for(var j=i+1;j<arr.length;j++){
if(arr[i]==arr[j]){
arr.splice(j,1)
}
}
}
12. for of 是ES6 可以遍历可以迭代即(遍历)的对象 string array set map(字典),对于对象可以用for in遍历,不可以用for of也不可以用foreach
对于字符串用forin的方法得到的是索引号,不可用foreach方法。 而forin遍历数组时,变量为索引值因此遍历数组数尽量使用forof foreach for
// 注意遍历数组时 变量为其值
var index = 0;
for (const value of[1, 23, 34, 6]) {
// 可以把index 做为索引值
index++;
console.log("iterator = ", value)
}
for (const value of "string") {
console.log("iterator = ", value)
}
for (const value of new Set([2, 1, 4, 5])) {
console.log("iterator = ", value)
}
//对象遍历
//for in 变量是对象的key
for(var key in obj){
console.log(obj[key]);
}
//而forin遍历数组时,变量为索引值因此遍历数组数尽量使用forof foreach for
13. 获取对象的属性
// 获取对象的属性
// 1.对象.property
// 2.通过key 获取对象中属性值 一般用于遍历中
// 1.
// var name = obj.name;
// 2.
// 获取对象中的某个属性的第二种写法
// 这样的好处是 [key是可以变化的]
var key = "name";
var name = obj[key];
//获取属性为name的值'张三’
console.log("name = ", name);
// 3.获取所有的key 和所有值
//3.1.1
var keys = Object.keys(obj);//Object.keys() 方法会返回一个由一个给定对象的自身属性组成的数组
for (var i = 0; i < keys.length; i++) {
console.log("key = ", keys[i]);
let key = keys[i];
console.log("value = ", obj[key])//打印出了所有的值
}
//3.1.2
var val = Object.values(obj); //得到的是关于obj的属性值的数组
for (var index in keys) {
console.log(index);
} //0 1 2 3 4 5 6
for (var v of val) {
console.log(v);
} //数组的每一个值
//对于数组forof可以获取里面的值,forin获取数组的索引
//3.1.3
//也可以直接遍历对象
//for in 变量是对象的key即属性名
for (var key in obj) {
console.log(obj[key]);
}
15.对象的扩充
1.assign方法
// assign 分配 // 把可以枚举的属性从一个对象中合并到目标对象中 返回的是目标对象(会覆盖,是浅拷贝引用数据类型的修改相互之间会影响,基本数据类型不会)
var target = {
name: "zs",
age: 20
}
var source = {
gf: 10,
age: 10,
address: "二七区嵩山路街道",
box: {
money: 1000,
card: 5
}
}
var obj = Object.assign(target, source); //注意返回的是改变之后的target
console.log(target); //{name: 'zs', age: 10, gf: 10, address: '二七区嵩山路街道', box: {money:1000,card:5}}
source.age = 5; //对于基本数据类型没有浅拷贝这一说。所以此时target里的age是不变的,还是10
console.log(target); //{name: 'zs', age: 10, gf: 10, address: '二七区嵩山路街道', box: {…}}
source.box.money = 50; //此时target的money值会改变
console.log(target); //{name: 'zs', age: 10, gf: 10, address: '二七区嵩山路街道', box: {money:50,card:5}}
console.log(obj === target);// true
// 合并数组
var array = [1, 2, 4, 5, 6];
// 数组的替换(原因是有覆盖)
console.log(Object.assign(array, [100, 200, 300]));//[ 100, 200,300, 5, 6]
//对象之间的覆盖
var o1 = { x: 1, y: 2, z: 3 };
var o2 = { y: 4, z: 6 };
var o3 = { z: 10 }
var obj = Object.assign({}, o1, o2, o3); //{x:1,y:4,z:10}
2.//entries 返回的是以键值对组成的数组
var obj = {
name:"zhangsna",
age:20
}
var entries = Object.entries(obj);//该静态方法,使obj变为可便利的对象
console.log(entries)//[[ "name", "zhangsna" ], ["age",20 ]]
3. // 判断是否为同一个对象
// Object.is() 方法判断两个值是否为同一个对象(地址也要相等)
console.log(Object.is([1],[1]))//false,因为这是两个[1],地址并不一样
16.创建对象的另一种方式(对象的扩充,密封与冻结)
1. // 1.创建对象 并配置属性的选项
// 参数1:指定的原型对象(指定你创建对象的原型)
// 参数2:属性的配置项
// Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。
var obj = Object.create({}, {
age: {
value: 123, //值
writable: false, // 可修改
configurable: true, // configurable 可配置 删除 默认 false
enumerable: true // 可枚举(是否可以被遍历出来 默认为 fasle)
}
})
console.log(obj); //{age:123};
2.阻止对象的扩充
Object.preventExtensions(obj1);
// 判断一个是否被阻止扩充
var res = Object.isExtensible(obj1);
3.
// 3.对象的密封
// seal
// 1.不能添加
// 2.可以修改 但必须设置 writable true
// 3.不能删除 (配置为configurable true 也不行)
var obj2 = Object.create({}, {
name: {
value: "三哥",
writable: true,
configurable: true
}
})
Object.seal(obj2); // 密封对象
// 查看对象是否被密封 返回值为bool
// Object.isSealed(obj2);
4. // 4.冻结
// 不可以删除 不可以修改 不可增加
var obj3 = Object.create({}, {
name: {
value: "三哥",
writable: true,
configurable: true
}
})
// freeze
Object.freeze(obj3);
console.log(Object.isFrozen(obj3)) //true
13.什么是深浅拷贝
1.浅拷贝:只是拷贝了源对象的地址,所以源对象的任何值发生改变时,拷贝对象的值也会随之而发生变化。
2.深拷贝:则是拷贝了源对象的所有值而不是地址,所以即使源对象的值发生任何变化时,拷贝对象的值也不会改变。
14. 二维数组
var arr = [1,2,3];
// 索引越界 得到的值为undefined 并不会报错
console.log(arr[3]);//undefined
// 二维数组 数组中嵌套数组对象
var array = [[1,2,3],[4,5,6]];
for (const arr of array) {//第一次遍历得到两个数组
for (const value of arr) {
console.log("value = ",value);
}
}
// 从二维数组中取值
var num = array[1][2];
console.log("num = ",num);//6
// 设置
array[0][2] = 10;
console.log(array);
15.JSON 初探
JSON
javascript object notation js对象简谱
轻量级的数据格式 一般服务端返回顶数据格式就是json (常用的)
xml 可扩展标记语言 也是服务端返回的数据格式
// JSON数据的格式中不包含数 方法
// key 必须是字符串
// json对象如下
// let jsonObj = "{"name":"三","age":20,"gfs":[{ "name": "lili", "age": 18, "height": 170 }]}"
// "{"name":"三","age":20,"gfs":[{ "name": "lili", "age": 18, "height": 170 }]}"
let jsobj = {
name: '张胜男',
age: 20,
gfs: [
{ name: "lili", age: 18, height: 170 },
{ name: "shasha", age: 18, height: 170 },
]
}
// 1.
// 把js对象转换为json对象 (字符串)
// stringify 字符串化
let jsonstr = JSON.stringify(jsobj);
console.log(jsonstr);
console.log(typeof jsonstr);
// 2.把json 对象转换为 js对象
// 为什么要进行解析 ? js对象可以通过 打点调用属性 获取其值
// parse 解析
let obj = JSON.parse(jsonstr);
console.log(obj);
console.log(typeof obj);
// 任意进行了一次取值的操作
console.log(obj.gfs[0].name)
16.引用值之间的赋值与改变
····· //1.基本数据类型
var a = 1;
var b = a;
a = 2;
console.log(a, b);//2,1
//2.当obj=obj1时 对象重新赋值对应的obj1的变化
var obj = {
name: 'mm',
sex: 'll'
}
var obj1 = obj;
obj = {
name: 'll'
};
console.log(obj, obj1); //{name: 'll'} {name: 'mm', sex: 'll'}
//当obj=obj1时 对象取里面属性值改变时对应的obj1的变化
var obj = {
name: 'mm',
sex: 'll'
}
var obj1 = obj;
obj.name = 'oo';
console.log(obj, obj1); //{name: 'oo', sex: 'll'} {name: 'oo', sex: 'll'}
//3.当arr=arr1时 数组重新赋值对应的1的变化
var arr = [1, 2, 3];
var arr2 = arr;
arr = [1, 2]; //即重新开创一个空间
console.log(arr, arr2); //[1,2],[1,2,3]
//当arr=arr1时 数组取里面值改变值时对应的arr1的变化
var arr = [1, 2, 3];
var arr2 = arr; //引用值的赋值,也把地址空间赋值进去了,所以后面一个有改变的时候,另一个也改变,除非重新赋值
arr2[0] = 4;
console.log(arr, arr2); //[4,2,3],[4,2,3]
// arr[0]=4;
// console.log(arr, arr2); //[4,2,3],[4,2,3]
//对于原型的改变,赋值时
Person.prototype.name = 'kk';
function Person() {
}
Person.prototype = {
name: 'pp'
}
var person = new Person();
console.log(person.name); //pp
//2
Person.prototype.name = 'kk';
function Person() {
}
var person = new Person();
Person.prototype = {
name: 'pp'
}
console.log(person.name); //kk
16.闭包
闭包: 有权访问另外一个作用域变量的函数 称为闭包(是一个过程,这个过程是 在内部函数访问外部函数变量)
闭包的作用: 1.变量私有化 2.延长局部变量生命周期
缺点: 内存泄漏(常驻内存 丢不了 )
//3.
function test1() {
var a = 100;
return function() {
a++;
console.log("a - == ", a);
}
}
var fn2 = test1(); //大函数只执行一次,下面的是小函数执行,那也就产生了一个a后面小函数都是在这一个a操作
fn2();//101
fn2();//102
// 4.
for (var i = 0; i < btns.length; i++) {
fn(i); //执行了四次大函数,每
}
function fn(a) {
var num = 0; //每执行一次大函数,都会产生一个新的num,并给对应的 btns[a]的点击事件用,点击一次相应的num++一次
btns[a].onclick = function() {
// num++;
// console.log(num);
this.innerText = ++num;
}
}
//5.
var lis = document.querySelectorAll("li")
for (var i = 0; i < 3; i++) {
(function(i) {
lis[i].onclick = function() { //因为点击事件里面用着i所以相应的外部函数的i,不能被释放,与对应的点击事件相对应 如果不写外面的立即执行函数,那么i为全局变量不会产生闭包,最终i为3,所以此时的立即执行函数的作用只是为了传参产生闭包
console.log(" i = = ", i);
}
})(i)
}
15.一般用闭包做什么
1.延长局部变量的生命
2.将数据绑定在指令上运行,让指令不再依赖全局数据。一般出现在事件中,使用闭包和立即执行函数来完成
3.全局变量私有化
4.函数的节流
5.传递参数
16.闭包与立即执行函数的深度理解例题
1.for (var i = 0; i < 5; i++) {//for循环里的i是全局变量
console.log(i);
}
//立即执行 0 1 2 3 4
2.for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000 * i);//注意一点这里1000后面的i是立即执行的
}
//5 5 5 5 5 隔一秒
3.// 闭包实现
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);//这里有i引用着外面的i
}, i * 1000);
})(i);
}
// 0 1 2 3 4 5 隔一秒
4.
// 参数i 没得了
for (var i = 0; i < 5; i++) {
(function() {//形参没有传递i则里面的函数就没有用到立即执行函数里的i,而是用的全局的i,则这里没有产生闭包
setTimeout(function() {
console.log(i);
}, i * 1000);//1000前面的i时全局变量
})(i);//这个地立即执行函数和没有时一样的
}
//每隔1s执行一次 5 5 5 5 5
5.for (var i = 0; i < 5; i++) {
setTimeout((function(i) {
console.log(i);
})(i), i * 1000);//settimeout里的回调函数是立即执行地,且没有返回值则执行之后地结果为undefined,在settimeout里面第一个参数为一个常量不是一个函数地时候,后面设置地时间也无效了
}//此时的写法和上面的1类型差不多
//立即执行 0 1 2 3 4
17.call/apply/bind
//1.call的概念和初步理解
function Person(name, age) {
this.name = name;
this.age = age
} //构造函数如果不new的话,this指向window
var person = new Person('deng', 100)
var obj = {};
Person.call(obj, 'deng', 300); //如果第一个传参为对象,做了一个改变this指向的问题,使Person构造函数的this指向obj,然后后面是传实参值,得到obj为{name: 'deng', age: 300}
console.log(Person.call(obj, 'deng', 300)); //undefined,因为没有返回值
console.log(obj); //{name: 'deng', age: 300}
Person.call(obj);
//函数执行的时候如果第一个参数不是对象,那么,有call和没有是一样的
// 如test()和test.call()一样
//2.call的应用
function Person(name, age, sex) {
this.mingzi = name;
this.nl = age;
this.xb = sex;
}
function Student(name, age, sex, tel, grade) {
Person.call(this, name, age, sex); //相当于写了 this.name=name; this.age=age;this.sex=sex;利用call的作用,此时this指的是new的时候产生的this {}空对象;这样使代码效率提高许多
this.dh = tel;
this.jb = grade;
}
var student = new Student('sunny', 123, 'male', 139, 2019);
console.log(student);
//call与apply的区别
1.改变(函数)this指向
2.后面传的参数形式不一样
call需要把实参按照形参的个数传进去
apply需要传一个arguments(一个实参列表,伪数组)
1.call 立即调用 并传递多个参数
hello.call(btn,12,345);
2. apply 应用 立即执行
传递两个参数 并且第二个参数为数组对象
hello.apply(btn,[1,2]);
3.bind
返回一个新的函数 不是立即执行 必须调用才会执行
var newFn = hello.bind(btn,1,2);
newFn();
18.原型 原型链
//什么是原型
// JavaScript 中,如果把方法全部定义在构造器中 新建对象中都会包含构造函数中方法 浪费内存。所以在创建一个新的函数地时候,就会根据一种特定地规则为该函数创建一个prototype属性,这个属性指向函数地原型对象。可以说原型是一个对象,存放着新建对象的属性和方法
// 原型 本身就是一个对象 目的: 共享方法 节省内存
原型之间的关系
以上三者的关系如下
①构造函数Star通过prototype指向Star的原型对象prototype
②Star原型对象prototype通过自身具有的construtor属性指回构造函数Star
③Star创建了对象实例ldh,ldh通过自身具有的__proto__属性指向其原型对象
//什么是原型链
8.//什么是原型链?
访问对象的某一个属性时 ,首先会在该对象本身内部进行查找, 如果没找到 ,会沿着 _proto_向上一层进行查找 一直找到 null 为止
//原项链 每一个对象都有自己的原型,而原型也是对象,也有自己的原型,依次类推而形成的链式结构就叫做原型链
①拿ldh实例对象来说,//假如ldh需要用到一个方法,ldh会先现在自己的构造函数Star里面找是否有这个方法
②如果Star构造函数里面有这个方法就直接用,没有就根据通过ldh.__proto__找到Star.prototype,看里面有没有这个方法
③有直接用,没有继续往上找到,通过Star.prototype.__proto__找到Object的原型对象prototype,看里面有没有这个方法
④有直接用,没有继续往上找,通过Object.prototype.__proto__找到,发现结果是null,即没有这个方法
1.原型链的终点是什么
由于Object是构造函数,原型链终点是Object.prototype.__proto__,而Object.prototype.__proto__=== null // true,所以,原型链的终点是null
2.constructor是prototype上的属性,所以dog.constructor实际上就是指向Dog.prototype.constructor;constructor属性指向构造函数。constructor是prototype上的属性,这一点很容易被忽略掉。
3.function Person(name) {
this.name = name
}
var p2 = new Person('king');
console.log(p2.__proto__) //Person.prototype
console.log(p2.__proto__.__proto__) //Object.prototype
console.log(p2.__proto__.__proto__.__proto__) // null
console.log(p2.__proto__.__proto__.__proto__.__proto__)//null后面没有了,报错
console.log(p2.prototype)//undefined p2是实例,没有prototype属性
console.log(Person.constructor)//Function 一个空函数
4. dog instanceof Dog//true
19.在构造函数中,new关键字做了什么
1.创建了一个空对象;
2.将这个空对象的隐式原型_proto_\指向构造函数的显示原型prototype;如例,是将空对象的__proto__成员指向了Base函数对象prototype成员对象;
3.将构造函数的this指向空对象,并调用Base函数;
var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
20.继承发展史==>继承的几种模式
1.传统模式-->原型链//过多的继承了没用的属性
Grand.prototype.lastName = 'ji';
function Grand() {
};
Father.prototype = new Grand();
function Father() {
this.name = 'hehe';
};
Son.prototype = new Father();//即构成对象原型的原型,形成链式结构,即为原型链
function Son() {
}
var son = new Son();
2.借用构造函数//1.不能继承和借用构造函数的原型 2.每次构造函数都要多走一个函数(每次都要走两个函数,Person和Student)
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex, grade) {
Person.call(this, name, age, sex);//利用call()方法,来借用其他构造函数所写的
this, grade = grade;
}
var student = new Student();
3.共享原型//不能随便更改自己的原型
Father.prototype.lastName = 'deng';
function Father() {
}
function Son() {
}
function inherit(Target, Origin) {
Target.prototype = Origin.prototype;//直接让一个构造函数的原型,赋值给另一个构造函数的原型
}
inherit(Son, Father);
4. 继承模式---圣杯模式//最终的模式,既能继承父级的原型,又可以更改自己的原型 (使原型链模式和共享模式的结合,且有一个中间变量)
function inherit(Target, Origin) {
function F() {};//利用这个中间变量构造函数
F.prototype = Origin.prototype;
Target.prototype = new F();//利用原型链的方式,使Target继承F的原型
Target.prototype.constuctor = Target;//使 Target.prototype这个原型的构造器归位
}
Father.prototype.lastName = 'deng';
function Father() {
}
function Son() {
}
inherit(Son, Father);
var son = new Son;
var father = new Father;
19.类
// 类
// 类具有相同属性和行为的集合 (抽象)
// 属性 存 数据
// 行为(方法) 操作 数据
1. class Person {
constructor(name, age) { // constructor 构造对象 初始化属性// 主要存放属性
this.name = name;
this.age = age;
}
play() {
console.log("乒乓球 父类的方法")
}
static num = 0;
static haHaHa() {
console.log("父类的静态方法")
}
}
// extends 继承
class Man extends Person {
constructor(name, age, cool) {
// 在访问this之前必须调用super函数
super(name, age); //调用父类的constructor(name, age)
// 特有的属性
this.cool = cool; //如果不调用super()方法,子类就得不到自己的this对象。
}
//super()表示调用父类的构造方法constructor(),只是调用方法,不构造对象。 super.fun();表示调用父类的某方法 在继承的时候这个方法是肯定被继承下来的。//继承的时候而静态方法也可以从super.fn()上调
smoke() {
console.log("抽烟 ---- ");
}
play() {
// super 超级的 指的是父类的对象
// 用super调用的好处是 保留了父类的功能 又叠加了新功能
super.play();
console.log("打篮球 ==== ");
}
}
var man = new Man("张三", 20, "cool");
console.log(Man.num) // 静态方法只能在当前类上调用,不能被该类的实例对象调用。但是父类的静态方法可以被子类继承。(注意要区分子类和实例使不同的)
2.例子
定义一个类Point(点)
数据属性:
x, x坐标
y, y坐标
实例方法:
moveRight:实现x加1
moveLeft:实现x减1
moveUp:实现y减1
moveDown:实现y加1
静态方法//静态方法只能在当前类上调用,不能被该类的实例对象调用。但是父类的静态方法可以被子类继承。(注意要区分子类和实例使不同的)如果静态方法包含this关键字,这个this指的是类,而不是实例。(注:静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。)
计算两点间距离
参数:两个点
返回值:两点间距离距离
class Point{
constructor(x,y){
this.x = x;//this指向new时候的实例
this.y = y;
}
moveRight(){
this.x++;//谁调用了这个方法,this就指向谁
}
moveLeft(){
this.x--;
}
moveUp(){
this.y--;
}
moveDown(){
this.y++;
}
static distance(p1,p2){//属于静态方法,和上面的方法使不同的,不会出现在实例的原型上面,只会在父类上面
// 逻辑省略
return Math.sqrt(Math.pow(Math.abs(p1.x - p2.x),2) + Math.pow(Math.abs(p1.y - p2.y),2));
}
}
var p1 = new Point(10,20);
p1.moveUp();
p1.moveRight();
p1.moveDown();
p1.moveLeft();
var p2 = new Point(300,200);
Point.distance(p1, p2);//调用父类静态方法的方法
20.手写call()函数 bind()函数
1.手写call()
// Symbol (标志 )基本数据类型中的一种 是唯一标识 主要是用于对象让属性名不重复
Function.prototype.mycall = function(obj, ...args) {//不确定传递多少个参数,可以使用展开运算符
// 判断如果传递为空时 让ctx 等于window 或者 等于nodejs种的全局对象 global
if (obj == null || ctx == undefined)
obj = window || global;
// 因为名称的问题
var key = Symbol();//使key是一个独一无二的值,防止和其他变量命名冲突
ctx[key] = this;//使test()函数作为obj对象内部的一个方法添加进去,这时候该test()就指向ctx
var result = obj[key](...args);
delete obj[key];//在原对象中,并没有obj.fn属性,所以我们要将其进行删除。
return result;// 将执行结果保存,并返回
}
var res = test.mycall({
name: 123,
key: "哈哈哈"
}, 20, 40)
console.log(res);
2.
// 手写bind
Function.prototype.mybind = function(ctx, ...abc) {
console.log(this); // m2
var _this = this; //bind()不是立即执行函数,需要返回一个函数,为了防止this发生改变
var key = Symbol("temp")
return function() {
console.log("====", this)
ctx[key] = _this; //改变test()函数的this指向,指向了ctx
return ctx[key](...abc);
// 简写
// return _this.call(ctx,...abc);
}
}
function method2(a, b) {
console.log(this, a, b)
return a + b;
}
var newFn = method2.mybind({}, 1, 2);
21.正则表达式
1. //用来检索或替换符合规则的文本
2.创建方式
// 创建正则的对象的方式
// 1.字面量 (见名知意)
var regExp = /abc/;
// 2.通过new 关键字
var regExp = new RegExp(/abc/);// 参数为正则对象
var regExp = new RegExp("abc");// 参数为正则字符串
var regExp = new RegExp("abc","gi");// 参数为正则字符串
3.用法
3.1
// /abc/ 精确匹配 只要字符串中包含abc 就会匹配到,注意此时的abc是连在一起的如果为abacd也为false
console.log("====== ", regExp.test("abc"));// true
console.log("====== ", regExp.test("abcd"));// true
console.log("====== ", regExp.test("abacd"));// false
3.2
// 2.[] 模糊匹配 匹配 [...] 中的所有字符中的一个就是true,
var regExp = /[abc]/;
console.log("====== ", regExp.test("abc"));// true
console.log("====== ", regExp.test("a234"));// true
3.3
// 3 ^写在[]里面 匹配除了 [...] 中字符的所有字符
var regExp = /[^abc]/;
console.log("====== ", regExp.test("abc")); // false
console.log("====== ", regExp.test("a234")); // true//注意检索是一个一个的检索的,此时只要有一个不是a,b,c中的数就是true
3.4
// 4.
// [A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母。
// \d [0-9] \D [^0-9] \w [0-9A-Za-z_] \W[^0-9a-zA-Z_]
// 匹配所有。\s 是匹配所有空白符,包括换行,\S 非空白符,不包括换行。
// . 匹配除换行符(\n、\r)之外的任何单个字符,相等于 [^\n\r]。
var regExp = /[a-z]/;
console.log("====== ", regExp.test("abc")); // true
console.log("====== ", regExp.test("a234")); // true
3.5
//^ 边界符 开头 // $ 结尾
var regExp = /^[a-z]$/;//注意此时匹配到的是a-z中的任意一个字母,不是多个
console.log("====== ", regExp.test("abc")); // false
console.log("====== ", regExp.test("a")); // false
3.6
// | 或 其中一个
var regExp = /123|456|789/;
console.log(" ====== ", regExp.test(123)) // true
console.log(" ====== ", regExp.test(1234)) // true
console.log(" ====== ", regExp.test(567)) // false
3.7
// 2.分组
var regExp = /(123|456|789)7/;//有()先看()里面的
console.log(" ====== ", regExp.test(1237)) // true
console.log(" ====== ", regExp.test(1234)) // false
3.8 量词
var str = "正则很麻烦但123需要坚持以456后用直接46464646百度1搜索"
// + 匹配一次或多次{1,n}
var regExp = /\d+/g;
console.log("=============", regExp.test(str)); // true
console.log("=============", str.match(regExp)); //
// * {0,n}//0次的含义是如果匹配的不符合,则返回为'';多次的意思是如果可以匹配会一直匹配,直到匹配不到
var regExp = /\d*/g;
console.log("=============", regExp.test(str)); // true
console.log("=============", str.match(regExp)); //
// 一下匹配两次
var regExp = /\d{2}/g;
console.log("=============", regExp.test(str)); // true['12','45','46','46','46','46']
console.log("=============", str.match(regExp)); //
var regExp = /\d?/g; //?匹配0次或1次
var str = "天天下1雨";
console.log("=============", regExp.test(str)); // true
console.log("=============", str.match(regExp)); //[ "", "","", "1","",""]
4.正则的使用
var str = "在哪个激情的夜晚,他和他走在漆黑的路边,吓他妈的我一跳";
var array = ["激情", '他妈的', "傻逼", "卧槽", "TMD"];
var regExp = new RegExp(array.join("|"), "gi");//多余多个敏感词的时候
str = str.replace(regExp, function(value) {//replace方法第二个参数还可以是函数,遍历匹配到的值
// value 匹配到的字符串
// var x = "";
// for (const s of value) {
// x += "*"
// }
// var str = "*".repeat(value.length);
return "*".repeat(value.length);
})
22.模块化
19.值类型与引用类型
// 数据结构 栈和堆
// https://blog.youkuaiyun.com/qq_44799466/article/details/106074212
// 1.值类型 存储在栈的数据结构中的 速度快 效率高
// string number bool null undefined
var a = 10;
var b = "string";
var c = true;
// 2.引用类型 Array Function Object
// obj 称为对象的引用(指针) 引用是存放在栈中的 而对象本身是存放在堆中
// 如果需要修改对象的值 通过指针找到对象的内容地址 进行修改其值
var obj = {
name: "张三",
age: 20
}
var arr = [1, 2, 3];
// 3.值传递 和 引用传递
// 在赋值 或 传参时 把实参赋予形参的过程 称为值传递 引用传递
// 3.1 值传递 把值copy一份赋值新的变量 a改变时 不影响b变量
var a = 10;
var b = a;
a = 11;
console.log(" b = ", b); // 10
function test(num) {
num = 100;
}
test(a);
console.log("a === ", a); // 11
// 3.2 引用传递
// 新老指针指向同一个对象 修改任何一个 两者的都时相同的
// 只拷贝指针 不拷贝对象的值(这里可以说基本类型拷贝值,而引用类型拷贝指针不拷贝对象的值)
var obj = {
name: "lili",
age: 18
}
var obj2 = obj;
obj.name = '莎莎';
console.log("obj2 == ", obj2.name); //莎莎
----------------------------
function test2(obj) {
obj.name = "四";
}
var obj3 = {
name: "三"
};
test2(obj3); //对于形参与实参的引用传递,相当于把实参的指针赋值一份,给形参,此时形参与实参都指向同一个指针
console.log("obj3 .name = ", obj3.name); // 四
------------------------------
function test(person) {
person.age = 18;
person = {//让形参person重新指向一个新的地址
name: "king",
age: 45
}
return person;
}
var p1 = {
name: "小明",
age: 20
}
var p2 = test(p1);
console.log(p1); // 小明 18
console.log(p2); // king 45
20.this指向
// 1. 全局作用域下 this指向window对象
console.log(this);
// 声明的全局变量 默认做为window的属性
var a = 10;
console.log(this.a);
console.log(window.a);
var obj={
point:this//这个this也指向window
}
// 在全局作用域下 尽量避免使用name这个名称
// this window 可以省略
console.log(this.b);// undefined 没有给该属性赋值 为undefined的,注意如果不写this时会报错
function test() {
console.log("测试一下",this);// window
}
window.test();
this.test();
test();//直接这样写,前面没有谁调用这个函数时,函数里的this指向window
// 2.
// 指向当前方法的调用者
var obj = {
name:"张三",
age:20,
play(){
console.log(this.name + "去打篮球");// this -> obj
}
}
obj.play();
// 3.this指向绑定事件
var btn = document.querySelector('button');
btn.onclick = function () {
console.log(this);// this -> btn对象
}
//4.在对象中的this指向
var obj1 = {
name:"1111",
play:function (){
console.log(this);//指向obj1
var test = function() {
console.log("this == ",this);//此时指向window,这个时候是window调用的这个函数
}
test();
}
}
obj1.play();
21.箭头函数与箭头函数中的this指向(注意箭头函数的this指向不看是谁调用这个函数,和普通函数不一样)
它是看写这个箭头函数时,当前作用域的this指向谁
1.
// ES6 箭头函数
// 结构
// (参数列表) => {函数体}//写的时候和函数表达式有点像,但没有function
var test = (a) => {
console.log(123)
}
// 可以简写
// 一个参数 可以省略 括号,没有参数或者多个参数的时候不可以省略括号
// 如果有返回值 并且业务逻辑简单 比如一个表达式 可以省略{} 和 return 关键字
var test4 = a => a + 100;//a+100为箭头函数的返回值
console.log(test4(19));
2.
//重点: 在箭头函数中 this它总是指向其最近的外层函数作用域的 this 所指对象
例子 2.1 var btn = document.querySelector('button');
btn.onclick = () => {
console.log("this ,", this); // window
}
2.2 var obj = {
name: "zhagnsan",
age: 20,
play: function() {
setTimeout(() => {
console.log("第一个this = ", this); // obj
}, 1000);
setTimeout(function() {
console.log("第二个this = ", this); // window
}, 1000);
}
}
obj.play();
2.3
var obj1 = {
name: "zhagnsan",
that: this,
age: 20,
play: () => {
setTimeout(() => {
console.log("第一个this = ", this); // window, 这个时候要看obj1里的this了,obj1里的this指向 window
}, 1000);//注意一点obj里的this指向window,但是obj里的普通函数里的this指向这个对象
setTimeout(function() {
console.log("第二个this = ", this); // window,他是一个独立的函数体,里面的this指向window
}, 1000);
}
}
obj1.play();//谁调用了这个函数,函数里面的this就指向谁
console.log(obj1.that);
22.改变this指向
// 1.箭头函数
setTimeout(() => {
console.log("改变后的 = ", this); // obj
}, 1000);
// 2. 定义临时变量 that
var that = this;
setTimeout(function() {
console.log("改变后的that = ", that); // obj
}, 1000);
// 3. bind call apply
setTimeout(function() {
console.log("bind后 = ", this);
}.bind(this), 1000);
23.事件this的相关指向,对象里·的this
allInput.onchange = function() {
console.log(this);
// 以后使用this之前 一定知道this目前指向的是
liInputs.forEach(function(el) {
console.log("this =", this);
el.checked = this.checked;
});//方法里面传函数为回调函数,这个函数里的this指向window (foreach是一个方法,回调函数的this一般指向window
改变放法
// 1.利用箭头函数改变
liInputs.forEach((el) => {
el.checked = this.checked;//利用箭头函数这里的this指向allInput
})
// 2.点击事件中定义that 利用中间变量that
var that = this;
liInputs.forEach(function(el) {
el.checked = that.checked;
})
// 3.利用bind方法
liInputs.forEach(function(el) {
el.checked = this.checked;
}.bind(this));//bind()里的this指向当前作用域的this
}
// 注意:在对象里面有方法即函数,方法里的this指向这个对象,因为只有这个对象才能调用这个方法
function test(obj) {
console.log(obj.name);
console.log(obj.age);
obj.success(10);
}
test({
name: 123,
age: 20,
success: function(response) {
console.log("this = ", this); //this指向 当前方法传递的对象
} //在对象里面的函数,函数里面的this‘一般指向这个对象
})
24.深浅拷贝
//深浅拷贝 只针对于引用类型
// 1.浅拷贝 对内存的地址的复制
var obj1 = {
name: "张三",
age: 20
}
var obj2 = obj1;//浅拷贝
obj1.name = "李四";
console.log("obj2 .name = ", obj2.name);
console.log(obj1 === obj2);//true
// 2.深拷贝
// 拷贝对象的具体内容,内存地址市自主分配的
// 如何实现深拷贝 ?
var obj = {
name: "张三",
age: 20,
gf: {
name: "小绿"
}
}
// 1.JSON中的方法
// 可以实现多层的深拷贝
var jsonobj = JSON.stringify(obj);
var obj2 = JSON.parse(jsonobj);
console.log(obj === obj2);//false,因为深拷贝是不同的地址了
console.log(obj.gf === obj2.gf);//false
// 2.自定义函数来实现深拷贝 (只拷贝一层),用遍历的方法
function deepObj(obj) {
var nObj = {};
for (const key in obj) {
nObj[key] = obj[key];//这样可以给一个空对象同时添加了属性,与属性值
}
return nObj;
}
var obj1 = {
name: "张三",
age: 20
}
var newObj1 = deepObj(obj1)
console.log("--------------");
console.log(newObj1 === obj1);
// 拷贝数组
function deepArray(arr) {
var nArr = [];
for (const value of arr) {
nArr.push(value);
}
return nArr;
}
var array = [1, 2, 3, 4, 5];
var newArray = deepArray(array);
console.log(newArray === array);//false
//3.用递归的方式实现多层深拷贝
var obj = {
name: "zhangsna",
age: 20,
gfs: [
{ gfname: "lili", hobby: ["篮球", '乒乓球'] },
{ gfname: "shasha", hobby: ["篮球", '乒乓球'] },
]
}
function deepClone(newObj, oldObj) {
// key gfs value = [.....]
// newObj {gfs:[hobby:[]]}
// deepclone([],[.....])
//
for (const key in oldObj) {
// 判断是不是数组
if (Array.isArray(oldObj[key])) {//oldObj[key]是属性值并不是属性名
newObj[key] = [];
deepClone(newObj[key], oldObj[key]);
}
else if (oldObj[key] instanceof Object) {
newObj[key] = {};
deepClone(newObj[key], oldObj[key]);
}
else {
newObj[key] = oldObj[key];//同时给一个空的对象或数组,添加了属性名和属性值,也会给一个空数组添加 了索引对应的值
}
}
return newObj;
}
// 4,实现了深拷贝,lodash中的-clonedeep
var newObj = deepClone({}, obj);
console.log(newObj === obj);// false
25.逻辑 || 与逻辑 &&
26.cookie的理解,如何清除cookie
1.“Cookie是服务端在HTTP响应中附带给浏览器的一个小文本文件,一旦浏览器保存了某个Cookie,在之后的请求和响应中,会将此Cookie来回传递,可以通过Cookie跟踪用户状态,验证用户身份。”
可以手动清除cookie,或者设置cookie数据过期的时间
2.cookie的优点及缺点?
优点:
①.可配置到期时间
②持久性
③不需要任何服务器的资源
缺点:
①.不同浏览器 不能互相访问
②.可能会被用户禁用,删除
③.不够安全
④.储存空间大概 4kB 左右
27.常用的CSS3函数有?
1.var()用于插入自定义的属性值。 第一个参数必需,为自定义属性的名称,名称需以 -- 开头;第二个参数可选,为备用值,在属性不存在的时候使用。
body {
background-color: var(--main-bg-color, blue);
}
2.calc()用于动态计算长度值。
.box{
width: calc(100% - 10px);
}
3.rgba()
4.linear-gradient 创造一个线性渐变的图像
28.如何改变一个函数a的上下文?
1.call
2.apply
3.bind
29.什么是回流和重绘及区别?
①.回流:
页面中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建,这就称为回流
每个页面至少需要一次回流,就是初始化页面
②.重绘
页面中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局
回流必将引起重绘,而重绘不一定会引起回流
30.浅拷贝的方法
1.直接赋值
2. ...展开运算符
3.Object.assign()方法
31.同步任务和异步任务的区别·
1. 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行。
32.return
1.一个函数里面的语句,只要遇到了return,就结束该函数的执行语句,不执行后面的了,无论这个return写到了函数的哪里比如if语句里。也结束了该函数的执行语句
function(valid){
//console.log(vaild);
if(!valid) return;
const {data: res}= await this.$http.post('login',this.loginForm);//对象的结构赋值
console.log(res);
if(res.meta.status!==200) return this.$message.error('登录失败');
this.$message.success('登录成功');
}
33.请描述js中同步执行的函数和异步执行的函数的区别
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行
34.http协议中请求报文分为?响应报文分为?
1.请求行 请求头 请求体 响应行 响应头 响应体
dom操作
1.获取与设置元素内容如innerHTML
// 获取div中的内容
// querySelector() 找到是文档流中第一个元素
var div = document.querySelector("div");
// 1.innerText 内部 获取标签的内容
// 不包含空格和换行
var text = div.innerText;
console.log("text = ",text);
// 2.textContent内容
// 保留内容区域的空格和换行
var text = div.textContent;
console.log("text =",text);
// 3.innerHTML
//保留内容区域的空格和换行
// 在获取元素内容时把标签做为内容进行展示,在设置或者添加元素时可以解析标签
var text = div.innerHTML;
console.log("text =",text);
// 4.outerHTML
// 包含标签的整个文本
var text = div.outerHTML;
console.log("text =",text);
// set
// 1.
div.innerText = "1245556 <h1>哈哈哈哈</h1>";
section.innerHTML = "<input type='text'> <button class='del'> -- </button> <br/>";
2.通过 getElementsByClassName获取元素得到的是伪数组,不能使用foreach等方法,可以先转化为数组,但是利用querySelectorall()得到的伪数组可以用foreach遍历,但不能用数组的其他方法,因为不是一个真数组
var lis = document.getElementsByClassName("li");
console.log("===lis == ", lis);
// getElementsByClassName 得到是 伪数组
// 不能使用forEach
// 强制转换为数组对象
// 此处会报错
// lis.forEach(function (v,i,array) {
// })
var colos = ["red", "blue", "yellow"]
Array.from(lis).forEach(function (el, i) {
console.log(el);
el.style.background = colos[i];
})
3.获取与修改元素自带的属性之className与classList的区别
var divClass = div.className;
console.log("clname =", divClass);
// 通过classname 来修改元素的类名 会覆盖之前的
// 如果要保留之前的 都拼接上去
div.className = "green red blue";
var divClass = div.classList;//classList获得的是类名的一个伪数组(新集和类型的实例),里面有许多方法和属性,比className方便许多
console.log("clname =", divClass);
//classList的一些方法
。
// add
div.classList.add("yellow");
// 删除
div.classList.remove("red");
// 删除多个
div.classList.remove("yellow","green");//删除多个的写法
div.classList.remove(...["yellow", "green"]);
// replace 替换
div.classList.replace("blue", "bigBlue");
toggle()
// 如果列表中已经存在给定的值,删除它,如果列表中没有给定的值,添加它
//div.classList.contains("red");
// 检查class列表中是否拥有某个值
// true or false
var contain = div.classList.contains("red");
console.log(contain)
4.通过api中提供方法 往元素中添加元素(创建一个元素,添加内容·,追加到别的敌方)
var hh = document.createElement("h2");//创建一个元素
hh.innerText = "我是好男人 好男人就你";//写入内容
// appendchild 方法只能方 元素节点
document.querySelector("div").appendChild(hh);//注意appendChild里面写的是元素节点,不是字符串
// append 可以追加多个 并且类型可以字符串 或者 是元素节点
document.querySelector("div").append("123",hh);
// 追加到开头的位置 prepend
document.querySelector("div").prepend(hh);
5.占位符··和$占位符与转义符
模板字符串 `` ${} 占位符
str += `
<div class='box'>
<img src="${obj.imgsrc}">
<p>${obj.title}</p>
</div>
`
//占位符利可以写变量,模块字符串可以放元素,且可以换行,任意嵌套
1.对于字符串遵循外双内单,外单内双
2.对于单引号 和双引号 都不可以换行 只能通过 + 进行拼接
3. \ 转义符 把后边特殊符号做为一个普通字符串 或者 转义成换行 制表符健
\n 换行 \t 4个空格 tab
var str = "我要送你个\"惊喜\"";
// 第一个\ 把第二个\转义成字符串
var path = "c:\\user\\desktop";
6.绑定事件的另一种写法
<!-- 绑定事件的另外一种写法 "方法名()" 一定要记得加() -->
<button onclick="btnclick()">点我</button>
7.利用appendChild()方法,在追加同一个元素时,只能追加一次,解决方法
// 解决办法1 利用函数的性质
var box = document.querySelector(".box");
function insertEl(el,content){
var element = document.createElement(el);
element.innerText = content;
box.appendChild(element);
}
insertEl("h2","我是内容");
insertEl("h2","我是内容");
insertEl("h2","我是内容");
// 解决2
// clone 克隆 node 节点
// 参数为true时 才会拷贝一份新对元素对象
var h3 = document.createElement("h3");
h3.innerText = "我是h3 ===== ";
box.appendChild(h3);
box.appendChild(h3.cloneNode(true));
box.appendChild(h3.cloneNode(true));
8.往父元素中添加子元素 通过innerHTML的方式 会导致子元素的所有绑定的事件被注销
1.
var btn = document.querySelector("button");//子
var div = document.querySelector("div");//父
btn.onclick = function() {
// 往父元素中添加子元素 通过innerHTML的方式 会导致子元素的所有绑定的事件被注销
div.innerHTML += "<input type='text'> <button> -- </button>"//只执行一次,因为当btn被点击的时候,此代码执行一次,然后点击事件被注销,之后再点击则无效
}
2.因此在向父级添加元素的时候,可以先建一个子盒子把元素加到子盒子中,就不会有点击事件被注销这一说法了
btn.onclick = function() {
var section = document.createElement("section");
section.innerHTML += "<input type='text'> <button class='del'> -- </button> <br/>"
div.appendChild(section);
}
9.添加事件监听
// p1:触发事件的类型 不能添加on
// p2:事件处理函数
// p3:true or false 如果第三个参数不传递 则默认为冒泡事件流
// 如果为true 则为捕获事件流
small.addEventListener("click", function () {
console.log(" small ======= ");
});
10.事件委托的本质就是利用事件的冒泡,把原本自己要做的事情交给父元素来处理
案例一
1.当没有利用事件委托的原理
// var lis = document.querySelectorAll("li");
var lis = document.getElementsByTagName("li");
Array.from(lis).forEach(function(li) {
li.onclick = function() {
console.log(this.innerText);
}
});
var ul = document.querySelector("ul");
var four = document.createElement("li");
four.innerText = "4444444444";
ul.appendChild(four);
//此时会出出现,后面增加的第四个li则不会执行点击事件,因为绑定事件的时候,并没有第四个li,想要解决这个问题,可以利用事件委托原理吧,事件添加到父元素,因为父元素有这个点击事件,后面不管添加几个li也都属于父元素ul,则第四个li也会又点击事件
2.当有冒泡事件时
如下: var lis = document.getElementsByTagName("li");
var ul = document.querySelector("ul");
ul.onclick = function(e) {
event.target.style.color = "red"
}
//ul.innerHTML += "<li>444444</li>"; // 会导致事件注销
// three.js 处理3D的
var four = document.createElement("li");
four.innerText = "4444444444";
ul.appendChild(four);//这里的第四个元素是绑定事件之后加地,但由于绑定的事件是冒泡事件,给其父级添加的事件,所以第四个点击的时候里面的元素也会变红
案例二:删除元素
// 3.可以利用冒泡,给里面的每个子元素添加一些操作,这里不存在闭包什么的了,而且此时还可以避免代码执行顺序的弊端,因为是给父级添加地事件,所以此时子元素不存在也没关系
main.onclick = function() {
if (event.target.className === "del") {
// 1
// event.target.parentElement.remove();
// 2.
main.removeChild(event.target.parentElement);
}
}
11.自定义属性
<!-- 在h5标准中 把自定义属性 以data-开头 -->
<p myname="superwings">1235456778</p>
<main data-name="xiaoai" data-my-test="test">hello dom</main>
----------获取元素自定义属性-----------------------
1.
var p = document.querySelector("p");
console.log(" p 的自定义属性 = ", p.myname); // undefined
// Attribute 获取自定义属性
var name = p.getAttribute("myname");//这种p的自定义属性前面没有加data-
console.log("myname = ", name);
// 设置自定义属性
p.setAttribute("myname", "ledi");
2.用dataset方法获取
var main = document.querySelector("main");
// 相对麻烦一些
// console.log(" 自定义属性 = ",main.getAttribute("data-name"));
// main.setAttribute("data-name","xiaoaitongxue");
// 获得自定义属性
console.log(main.dataset.name);
// 设置自定义属性
main.dataset.name = "哈哈哈哈";
//获得自定义属性
console.log(main.dataset.myTest); // 如果使用多个横线分隔 则使用dataset取值时 采用小驼峰的形式
console.log(" 自定义属性 = ", main.getAttribute("data-my-test"));
12. window.onload与DOMContentLoaded的区别
// onload 等待 所有的资源文件 (音视频文件 图片) 和 dom结构加载完毕后执行 onload
window.onload = function () {
var div = document.querySelector("div");
console.log(div.innerText);
}
// 只等待dom结构加载完毕后执行
document.addEventListener("DOMContentLoaded", function () {
var div = document.querySelector("div");
console.log(div.innerText);
})
13.input事件
1.
// 获取焦点
// 同一个页面只有一个焦点
input.onfocus = function () {
console.log("获取焦点");
this.style.background = "red";
}
// blur 模糊
input.onblur = function () {
console.log("失去焦点");
this.style.background = "";
}
// 获取实时输入的内容
input.oninput = function (e) {
// console.log(e.target.value);
}
2.onchange事件,一般在表单事件中常用
2.1
input.onchange = function () {
console.log("内容发生变化 且 失去焦点时执行");//内容发生变化 且 失去焦点时执行
}
2.2 checkbox.onchange = function() {
console.log("checkbox change", this.value); //被绑定的对象选中情况发生变化
}
2.3 select.onchange = function() {
console.log("select === ", this.value); //选中的发生变化
}
14.鼠标事件
// 鼠标按下
// down - up - click
div.onmousedown = function (e) {
// 可以进行区分 左右中健
// 0 左 1 中 2 右
console.log("鼠标按下", e.button);
}
div.onmouseup = function () {
console.log("鼠标松开");
}
// 不会冒泡 enter + leave
div.onmouseenter = function () {
console.log("鼠标进入");
}
div.onmouseleave = function () {
console.log("鼠标离开");
}
div.onmousemove = function () {
console.log("移动----");
}
// over + out 会冒泡
div.onmouseover = function () {
console.log('div over');
}
div.onmouseout = function () {
console.log('div out');
}
15.事件解绑的两种方式
1.置空
btn.onclick = null;
2.removeEventListener
var fn = function () {
console.log("124");
}
注意 // 添加监听器时的回调函数必须写成具名函数 否则无法进行解绑操作
btn.addEventListener("click", fn);
btn.removeEventListener("click", fn);
16.删除所有的子元素
<div>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
</div>
<script>
var div = document.querySelector('div')
//1.动态的 children
var ps = div.children;//因为·获得的children是动态的所以,后面应该i--,不然会漏删,会剩下2和4
for(var i = 0;i < ps.length;i++){
div.removeChild(ps[i]);
i--;
}
//2.getElementsByTagName() 动态的
var ps = div.getElementsByTagName("p");
for (var i = 0; i < ps.length; i++) {
div.removeChild(ps[i]);
i--;
}
// 3 querySelectorAll 静态的
var ps = div.querySelectorAll("p");
for (var i = 0; i < ps.length; i++) {
div.removeChild(ps[i]);
}//不需要i--了
17.元素的尺寸,元素的位置
// 获取元素的宽高 不包含滚动条的 高度为可视区域的高 和 window.innerHeight
// client 客户端
console.log("html 的 宽 ",document.documentElement.clientWidth);
console.log("html 的 高 ",document.documentElement.clientHeight);
console.log("body 的 宽 ",document.body.clientWidth);
console.log("body 的 高 ",document.body.clientHeight);
var div = document.querySelector('div');
// 不包含 border 包含padding 不包含滚动条
// clientWidth = padding + w|h - 滚动条
console.log("div 的 宽 ",div.clientWidth);
console.log("div 的 高 ",div.clientHeight);
// offsetWidth = padding + w|h + border (包含滚动条)
var section = document.querySelector('section');
console.log("section 的 宽 ",section.offsetWidth);
console.log("section 的 高 ",section.offsetHeight);
// 不包含边框 不包含滚动条 包含 内边距
// 宽高都是 包含被隐藏的部分
console.log("section scrollWidth的 宽 ",section.scrollWidth);
console.log("section scrollHeight 高 ",section.scrollHeight);//包含隐藏部分·
// !!!!!!!!!!!!
// 获取元素滚动的距离 元素滚动距离的最大长度=section.scrollHeight(包含隐藏)-盒子的实体高度(可视区域)
section.onscroll = function () {//元素的滚动条在发生滚动时的事件
console.log(" = ",this.scrollTop);
console.log(" = ",this.scrollLeft);
}
// 默认 给根元素对象 document.docmentElement.scrollTop 是有值的 而document.body.scrollTop没有值
// 如果body 和 html 都有滚动条
// 谁的滚动条位置发生变化 谁就有值
window.onscroll = function () {// window即整个页面在滚动的时候发生的事件
console.log("滑动的高度",window.scrollY);
console.log("滑动的高度",document.documentElement.scrollTop);
}
// 一定要记得
// 相对于距离它最近的定位为非static 的父元素 如果都没有则相对于 body
console.log("偏移 量 左 ",section.offsetLeft)//算上滚动的
console.log("偏移 量 上 ",section.offsetTop);
18. 元素的节点
// children 所有的子元素
var els = div.children;
div.children[0] //第一个子元素
console.log(els)
var pEl = els[1];
console.log(pEl.nodeName); // 大写
console.log(pEl.tagName); // 大写
// 常用 父元素
var el = pEl.parentElement;
var el = pEl.parentNode;
console.log(el);
// 获取兄弟节点
// previous 上一个 Sibling 兄弟
//
var previous = pEl.previousElementSibling;
var next = pEl.nextElementSibling;
console.log(previous, next);
// 获取的是文本节点 不常用
var pNode = pEl.previousSibling;
var nNode = pEl.nextSibling;
console.log(pNode, nNode);
// 移除指定的子元素
// 参数必须为元素节点
div.removeChild(pEl);
// 移除方法的调用者
div.remove();
18.event对象的补充
box.onclick = function() {
// 点击点距离屏幕的位置(整个电脑屏幕)
console.log('screenX', event.screenX);
console.log('screenY', event.screenY);
// // 距离页面的可视区域的,不包含滚动的
console.log("c = ", event.clientX);
console.log('clientY)', event.clientY);
// offsetX 距离自身位置的
console.log(event.offsetX);
console.log(event.offsetY);
// pageY = scrollTop + clientY
// 当滚动时 位置结果会不相同
console.log("p = ", event.pageX);
console.log(event.pageY);
19.事件的补充 选中和右键复制
//控制用户是否能选中
box.onselectstart = function() {
return false;
};
//context 上下文
//menu 菜单
//禁止右键
document.documentElement.oncontextmenu = function () {
return false;
}
//禁止复制
document.documentElement.onkeydown = function () {
console.log(event.ctrlKey);
if((event.ctrlKey && event.keyCode === 67)){
event.preventDefault();
// alert("禁止复制");
}
}
20.对于onscroll事件的兼容性
对与window对象几乎每个浏览器都可以使用,所以一般写onscroll事件都写window
document.documentElement对象好多浏览器都不可以使用,不建议用
21.history的用法·页面的跳转
1.History 浏览器的曾经在访问的会话历史记录。
1.1 History.back()
在浏览器历史记录里前往上一页,用户可点击浏览器左上角的返回 (译者注:←) 按钮模拟此方法。等价于 history.go(-1).
1.2 History.forward()
在浏览器历史记录里前往下一页,用户可点击浏览器左上角的前进 (译者注:→) 按钮模拟此方法。等价于 history.go(1).
2. location.href = "url地址" ,执行时使浏览跳转到对应的·页面
3.<a href="/delete?id" >删除 </a> 点击的时候使浏览器跳转到对应的页面
22. location.href = “url地址” ,执行时使浏览跳转到对应的·页面
关于优化浏览器性能的手段
1.防抖和节流
1.防抖 利用settimeout 降低事件的执行频率;简单的说,当一个动作连续触发,则只执行最后一次。
(搜索框搜索输入。只需用户最后一次输入完,再发送请求)
(1) //先清除timer 再创建timer
var timer = null;
input.oninput = function () {
clearTimeout(timer);
// 会影响性能 每次都会执行发送网络请求
// console.log("发起网络请求");
timer = setTimeout(() => {
console.log("发起网络请求");
}, 1000);
}
(2)封装一个函数
var timer = null;
input.oninput = function () {//利用这种方法,让里面的函数执行起来,并可以传参
debounce(function () {
console.log("发起网络请求")
}, 1000);
}
function debounce(cb, wait) {
clearTimeout(timer);//如果点击的特别快,不等到wait的时间,那每次都不会执行setTimeout里面的函数,除非你等到wait时间了还没点击就执行setTimeout里的
timer = setTimeout(() => {
if (typeof cb == "function") {
cb();
}
}, wait);
}
2.函数节流(throttle) 限制一个函数在一定时间内只能执行一次。
(高频点击提交,表单重复提交)
// 定义全局变量 bool
var isStart = true;
input.oninput = function () {
if (isStart) {
isStart = false;
setTimeout(() => {
console.log("发起网络请求");
isStart = true;//两秒之后才可以继续执行setTimeout里的,没有这个限制,持续输入内容的时候,持续有定时器触发,两秒之后会有好多定时器积累一起,持续触发
}, 2000)
}
}
2.表格拖拽
var trs = document.querySelectorAll("tr:not(:first-child)"); //一个选择器,
var tbody = document.querySelector('tbody')
trs.forEach((el, index) => {
el.draggable = true; //要想实现拖拽每个元素都要加上这个属性为true
el.className = "t" + index;
el.ondragstart = function() {
event.dataTransfer.setData("move", this.className); //为后面得到拖拽的元素
}
});
document.documentElement.ondragover = function() {
event.preventDefault(); //让.ondrop事件里的逻辑执行
}
tbody.ondrop = function() {
// 1.获取拖动的元素
var cname = event.dataTransfer.getData("move"); //得到被拖拽元素类的名字
var dragEl = document.querySelector("." + cname); //获取被拖拽的元素
console.log("dragEl = ", dragEl);
// 2.目标元素 (放在谁的身上)
var targetEl = event.target.parentElement.nextElementSibling; //目标元素在发生.ondrop元素的下面
console.log("targetEl = ", targetEl);
if (targetEl == dragEl) {
targetEl = event.target.parentElement;
}
// 把a插入在b的前面
// 如果b元素为空 则把a添加在 tbody的末尾
tbody.insertBefore(dragEl, targetEl);
}
3.懒加载
<div>
图片在下面你慢慢往下翻就实现了懒加载
</div>
<img src="./img/loading.gif" alt="" data-src="./img/1.jpg">
<script>
var img = document.querySelector("img");
var screenHeight = document.documentElement.clientHeight;
var imgTop = img.offsetTop;
window.onscroll = function () {
var sTop = document.documentElement.scrollTop;
if(sTop + screenHeight > imgTop + 500){
img.src = img.dataset.src;
}
}