《一起学前端 之 JS 篇》是记录从 2020.4.29 开始,学习 JS 的收获与感悟。
2020.4.29
数据存储单位
JavaScript介绍
JS是什么
运行在客户端的脚本语言,不是服务器端语言。
JS的组成
JS的作用
浏览器执行JS
JS书写位置
1 行内式JS
2 内嵌JS
3 外部JS
JS常用输入输出语句
注意 prompt 返回的是字符串!
var name = prompt('请输入您的名字');
alert(name);
JS变量命名规范
注意 name 虽然不是关键字,也不是保留字,但是一般在浏览器里面有特殊含义,所以最好不要使用它作为变量名。
弱类型语言 & 动态语言
解释型语言 & 编译型语言
概述
执行过程
数据类型的分类
JS把数据类型分类两类 基本数据类型( 简单数据类型 / 值类型 ) 和 复杂数据类型 (引用类型)。
其中简单数据类型包括:Number、String、Boolean、Undefined、Null。
复杂数据类型包括: object(自定义对象、内置对象、浏览器对象)
基本数据类型 在存储时,变量中存储的是值本身;而 复杂数据类型 在存储时,变量中存储的是地址(引用)。
基本数据类型
还有一个 symbol
基本数据类型 之 Number
数字型进制
数字型范围
数字型三个特殊值
判断是否是NaN
用方法 isNaN() 判断
基本数据类型 之 String
字符串引号嵌套
字符串的转义
字符串的长度
var str="my name is andy";
console.log(str.length);//15
字符串的拼接
var myName='牛牛';
alert('hello' + ' ' + 'world'); //hello world
alert('100' + '100'); //100100
alert('11' + 12); //1112
alert(11 + 11) //22
alert('我的名字叫'+myName);//我的名字叫牛牛
alert('我的名字叫myName');//我的名字叫myName
字符串的不可变性
指的是字符串里面的值不可变,虽然看上去内容可以改变,但其实地址变了,内存中新开辟了一个空间。
如果不断给一个字符串变量赋值,会占用很大的内存。
基本数据类型 之 Boolean
console.log(true+1);//2
console.log(false+1);//1
基本数据类型 之 Undefined 和 Null
一个声明后没有被赋值的变量会有一个默认值 undefined
var variable;
console.log(variable);//undefined
console.log('你好'+variable);//你好undefined
console.log(11+variable);//NAN
一个变量声明后给nulll值,里面存的值为空
var vari =null;
console.log('你好'+vari);//你好null
console.log(11+vari);11
console.log(true+vari);//1
2020.4.30
检测变量的数据类型 typeof
typeof 可以用来检测变量的数据类型
var name = '小蜜';
var age = 18;
console.log(typeof name);//string
console.log(typeof age);//number
数据类型转换
转换为字符串型
转换为数字型
console.log(parseInt('3.14'));//3 取整
console.log(parseInt('3.94'));//3 取整
console.log(parseInt('120px'));// 120
console.log(parseInt('rem120px'));// NaN
console.log(parseFloat('3.14'));//3.14
console.log(parseFloat('120px'));//120
console.log(parseFloat('rem120px'));//NaN
var str='123';
console.log(Number(str));
console.log(Number('12'));
console.log('12'-0);//12
console.log('123'-'120')//3
console.log('123'*1);//123
转换为布尔型
浮点数的精度问题
浮点数的最高精度是17位小数,但在进行算术计算时其精度远远不如整数,因为它会先转为二进制再计算。
console.log(0.1 + 0.2);//0.30000000000000004
console.log(0.07 * 100);//7.000000000000001
此外不要直接判断两个浮点数是否相等,容易出错。
var num = 0.3;
console.log(num == 0.3); //true
num = 0.1 + 0.2; //误差产生
console.log(num == 0.3); //false
递增运算符
注意前置与后置的区别,还有注意 表达式中同时出现一个变量的前置与后置递增 时的情况
var a = 10;
++a;//11
var b = ++a + 2;//++a 先对a自加1,再返回a
console.log(b); //14
var c = 10;
c++;//11
var d = c++ + 2;//c++ 先返回c,再对c进行自加1
console.log(d); //13
var e = 10;
//从左往右。先返回e,为10,然后对e进行自加1,得到11;先对e进行自加1,得到12,返回e;实际上等于 10+12=22
var f = e++ + ++e;
console.log(f); //22
比较运算符
逻辑运算符
短路运算
运算符优先级
分支流程控制语句 switch
注意 表达式num中的内容 需要跟 case 里面的内容全等(‘===’),才会进入该case执行代码。
var num = 1;
switch (num) {
case '1':
console.log(1);
break;
case '2':
console.log(2);
break;
case 1:
console.log('匹配数字1');
break;
default:
console.log('不匹配');
}
switch 和 if else 的区别
2020.5.2
断点调试
1 F12 - sources - 选择文件进行调试
2 单击行号即可为该行设置断点,再次点击可以取消
3 通过右侧工具栏可以在断点之后一步步运行,通过watch可以观察变量变化。
数组
创建数组
1 利用 new 创建数组
var arr = new Array(); //创建一个空数组
var arr1 = new Array(2); //创建一个长度为2的空数组
var arr2 = new Array(2, 3); //等价于数组[2,3]。创建一个数组,它有两个元素,分别为2和3
2 利用数组字面量创建数组
var arr1 = [];
//数组元素的类型不限
var arr2 = ['小白', 1, true, 28.4];
新增数组元素的最基本方法
1 修改length长度
var arr = [1, 2, 3];
arr.length = 5;//把数组长度改为5
console.log(arr[3]);//undefined
console.log(arr[4]);//undefined
2 新增索引号,追加数组元素
var arr = [1, 2, 3];
arr[3] = 4;
console.log(arr.length); //4
console.log(arr); //1,2,3,4
应用:挑选一个数组中大于等于10的元素出来,放入一个新的数组中
//方法1 用一个计数变量
var arr = [2, 0, 6, 1, 77, 0, 52, 10, 25, 7];
var newArr = [];
var count=0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] >= 10) {
newArr[count] = arr[i];
count++;
}
}
console.log(newArr);
//方法2 用 newArr.length 作为索引
var arr = [2, 0, 6, 1, 77, 0, 52, 10, 25, 7];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] >= 10) {
newArr[newArr.length] = arr[i];
}
}
console.log(newArr);
//方法3 push
新增或删除数组元素的其他方法
var arr = [1, 2, 3];
arr.push(4);
console.log(arr); //[1,2,3,4]
arr.push(5, 6, 7);
console.log(arr); //[1,2,3,4,5,6,7]
console.log(arr.pop()); //7
arr.shift();
console.log(arr); //[2,3,4,5,6]
arr.unshift(0);
console.log(arr); //[0,2,3,4,5,6]
翻转数组 reverse
var arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log(arr);//[5,4,3,2,1]
数组排序 sort
普通的 sort 会按位并按大小进行比较。
带有 比较函数 的sort能按照一定规律进行比较。
//普通 sort
var arr = [1, 2, 33, 32, 23];
console.log(arr.sort()); //[1,2,23,32,33]
//带有比较函数的sort
var arr1 = [1, 2, 33, 32, 23];
arr1.sort(function (a, b) {
// return a-b;//升序
return b - a; //降序
})
console.log(arr1); //[33, 32, 23, 2, 1]
检测是否为数组
可以使用 instanceof 或 Array.isArray()。instanceof 与 typeof 的区别是,typeof 最多只能判别变量为对象,但不能判别出具体是什么对象,所以需要用 instanceof 来判别。
var arr = [1, 2];
var num = 10;
console.log(arr instanceof Array); //true
console.log(Array.isArray(arr)); //true
console.log(num instanceof Array); //false
console.log(Array.isArray(num)); //false
数组索引方法
获得数组中元素的索引
var arr = ['blue', 'pink', 'orange', 'white', 'pink'];
// 只返回最先匹配的那一个的位置
console.log(arr.indexOf('pink')); //1
//只返回最先匹配的那一个,但并不会改变索引的顺序
console.log(arr.lastIndexOf('pink')); //4
//如果元素不在该数组中 则返回 -1
console.log(arr.lastIndexOf('green')); //-1
应用案例:数组去重
var arr = [2, 3, 6, 7, 8, 2, 4, 9, 221, 3];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) == -1) {
newArr.push(arr[i]);
}
}
console.log(newArr); // [2, 3, 6, 7, 8, 4, 9, 221]
数组转换为字符串
var arr = ['I', 'Love', 'You'];
console.log(arr.toString()); //I,Love,You
console.log(arr.join('-')); //I-Love-You
console.log(arr.join(' ')); //I Love You
2020.5.4
函数参数匹配问题
function getSum(num1, num2) {
console.log(num1 + num2);
}
//如果实参形参个数匹配,则正常输出结果
getSum(1, 2); //3
//如果实参个数>形参个数,则会对号入座,直到取到形参个数为止
getSum(1, 2, 3); //3
//如果实参个数<形参个数,则多出的形参会定义为undefined,形参可以看做是不用声明的变量
getSum(1); //NaN
函数 return 注意事项
1 函数 return 只能返回一个值。如果 return 多个由逗号分隔的值,则只返回最后一个值。
function fn(num1, num2) {
return num1, num2;
}
console.log(fn(1, 2)); //2
2 如果没有 return 则返回 undefined
function fn(num1, num2) {
}
console.log(fn(1, 2)); //undefined
break continue return 的区别
break 结束当前循环体
continue 跳出本次循环,继续执行当前循环体的下次循环
return 退出循环,并返回值
arguments的使用
arguments 是 伪数组,它具有 length 属性,可按照 索引 的方式进行存储,但它没有真正数组的一些方法,比如 pop()、push()等。
function fn() {
console.log(arguments); //[1,2,3,4,5]
console.log(arguments.length); //5
console.log(arguments[2]); //3
}
fn(1, 2, 3, 4, 5);
应用:
function getMax() {
var max = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
console.log(getMax(1, 2, 3, 4, 6, 7, 8, 9, 111, 110)); //111
2020.5.5
定义函数的三种方式
函数式声明
function fun() {
console.log("函数式声明");
}
函数表达式
var fun2 = function() {
console.log("函数表达式");
}
new Function()
var fn = new Function('a', 'b', 'console.log(a+b)');
fn(1, 2);//3
全局变量 & 局部变量
1 在函数内部没有声明直接赋值的变量,属于全局变量(但是函数必须被调用,该变量才能创建)
2 函数的形参可以看做是局部变量
function fun(aru) {
var num1 = aru;
num2 = 20;
}
fun(29);//调用fun,才有全局变量num2
// console.log(aru); //error
console.log(num2); //20
作用域链
内部函数访问外部变量时,采取链式查找的方式来决定取哪个值,类似于就近原则
var num = 10;
function fun1() {
var num = 20;
function fun2() {
console.log(num);//20
}
fun2();
}
fun1();
JS 预解析
js引擎运行js分为两步:预解析 和 代码执行 。
预解析 分为 变量提升(变量预解析) 和 函数提升(函数预解析)。
变量提升,就是把所有的变量声明提升到当前作用域最前面,不提升赋值操作。
函数提升,就是把所有函数声明提升到当前作用域最前面,不调用函数。
预解析 时,js引擎会把js里面的所有 var 和 function 声明,提到当前作用域的最前面。
而 代码执行 时,按照代码书写顺序从上往下执行。
//案例1
console.log(num); //undefined
var num = 10;
//案例2
fn(); //11
function fn() {
console.log(11);
}
//案例3
fun(); //报错,fun is not a function
var fun = function () {
console.log(22);
}
//案例4
var num = 10;
fun();
function fun() {
console.log(num);
var num = 20;
}
//--------相当于执行了以下代码
var num;
function fun() {
var num;
console.log(num);
num = 20;
}
num = 10;
fun(); //undefined
创建对象的三种方式
用对象字面量创建对象
属性 或 方法 采用 键值对 的形式,即 属性名: 属性值 。
使用对象属性有两种方法:
1 对象名.属性名
2 对象名['属性名']
删除对象属性的方法:
delete 对象名.属性名;
var obj = {
name: 'Json',
age: 18,
sex: 'man',
sayHi: function () {
console.log('hi');
}
}
<script>
const name = 'lily';
const age = 18;
const sex = 'man';
const obj = {
name,
age,
sex,
say() {
console.log('hello');
},
}
console.log(obj);
obj.say();
</script>
用 new Object 创建对象
var obj = new Object();
obj.name = '张三疯';
obj.age = '18';
obj.sex = '男';
obj.sayHi = function () {
console.log('hi~');
}
用 构造函数 创建对象
使用 构造函数 可以减少重复的代码
1 构造函数的首字母要大写
2 调用构造函数必须要用 new
3 构造函数的属性和方法前面必须添加 this
function Star(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sing = function (song) {
console.log(song);
}
}
var Json = new Star('Json', 17, 'man');
console.log(Json.name); //Json
console.log(Json.age); //17
console.log(Json.sex); //man
Json.sing('happy'); //happy
new 关键字
new 在执行时会做四件事情:
1 在内存中创建一个新的空对象
2 让构造函数的this指向这个新对象
3 执行构造函数里面的代码,给这个新对象添加属性和方法
4 返回这个新对象(所以构造函数里面不需要 return )
for in 遍历对象
语法 for(变量 in 对象){}
var obj = {
name: 'JSON',
age: 29,
sex: 'man',
fn: function () {
console.log('hello');
}
}
for (var k in obj) {
console.log(k);//k 输出得到的是属性名
console.log(obj[k]);//obj[K] 输出得到的是属性值
}
总结 :
for in 得到对象的key 或 数组、字符串的索引 ,
而 for of 和 forEach 一样,是直接得到值,但是 for of 不能对象用。
内置对象
内置对象 是 js 自带的一些对象,供开发者使用,提供了一些常用的功能。比如 Math、Date、Array、String 等。
Math 对象
Math 对象不是构造函数,它具有数学常数和函数的属性与方法。
//绝对值方法
console.log(Math.abs(1)); //1
console.log(Math.abs(-1)); //1
console.log(Math.abs('-1')); //隐式转换 1
console.log(Math.abs('Json')); //NaN
//取整方法 floor 向下取整
console.log(Math.floor(1.1)); //1
console.log(Math.floor(1.9)); //1
//取整方法 ceil 向上取整
console.log(Math.ceil(1.1)); //2
console.log(Math.ceil(1.9)); //2
//取整方法 round 四舍五入取整 但是.5特殊,会往“大”的方向取整
console.log(Math.round(1.5)); //2
console.log(Math.round(1.1)); //1
console.log(Math.round(1.9)); //2
console.log(Math.round(-1.1)); //-1
console.log(Math.round(-1.5)); //-1
Math.random() 随机数
该函数返回一个伪随机浮点数,范围在 [ 0 ,1),即大于等于0,小于1(不包括1),我们可以将随机数的范围缩放到所需要的范围。
//返回一个 [ 0,1 ) 之间的随机数
function getRandom() {
return Math.random();
}
console.log(getRandom());
//返回一个 [min,max) 之间的随机数
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
console.log(getRandom(10, 20));
//得到一个两数之间的随机整数,包括两个数在内
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
为什么计算机时间要从1970年1月1日开始算起
最初计算机操作系统是32位,而时间也是用32位表示。
System.out.println(Integer.MAX_VALUE);//2147483647
Integer 在JAVA内用32位表示,因此32位能表示的最大值是2147483647。另外1年365天的总秒数是 31536000,2147483647/31536000 = 68.1,也就是说32位能表示的最长时间是68年,从1970年开始的话,加上68.1,实际最终到2038年01月19日03时14分07秒,便会到 达最大时间,过了这个时间点,所有32位操作系统时间便会变为10000000 00000000 00000000 00000000,算下来也就是1901年12月13日20时45分52秒,这样便会出现时间回归的现象,很多软件便会运行异常了。
到这里,我想问题的答案已经显现出来了,那就是:因为用32位来表示时间的最大间隔是68年,而最早出现的UNIX操作系统考虑到计算机产生的年代和应用的 时限综合取了1970年1月1日作为UNIX TIME的纪元时间(开始时间),至于时间回归的现象相信随着64为操作系统的产生逐渐得到解决,因为用64位操作系统可以表示到 292,277,026,596年12月4日15时30分08秒,相信我们的N代子孙,哪怕地球毁灭那天都不用愁不够用了,因为这个时间已经是千亿年以后了。
Date 对象
Date对象 用来处理日期和时间。它和Math对象不一样,它是一个构造函数,需要实例化后才能使用。
获取当前时间
//Date 获取当前时间
var now = new Date();
console.log(now); //Wed May 06 2020 18:03:17 GMT+0800 (中国标准时间)
获取特定时间
//获取特定时间
var now = new Date('2019-5-6');
console.log(now); //Mon May 06 2019 00:00:00 GMT+0800 (中国标准时间)
获取时间的指定部分
可以通过获取时间的指定部分,拼接成自己想要的日期格式
案例:返回当前时间 格式 xx:xx:xx
function getTimer() {
var date = new Date();
var hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
var min = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
var sec = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
return hour + ':' + min + ':' + sec;
}
console.log(getTimer());
获取日期的总毫秒数 形式
Date对象 是基于1970年1月1日(世界标准时间)开始算的,可以通过 valueOf() 和 getTime() 方法获取距今的总毫秒数,所得到的时间戳是独一无二的。
//通过 valueOf() getTime()
var date = new Date();
console.log(date.valueOf());
console.log(date.getTime());
//简单写法
var date1 = +new Date(); //+new Date() 返回总毫秒数
console.log(date1);
//H5 新增写法
console.log(Date.now());
案例:倒计时写法
因为得到的 date 不能直接进行相加减,所以需要用到时间戳。预设时间 减去 当前时间,得到相差毫秒数,将相差毫秒数除以1000得到相差秒数,然后分别进行除法求余运算得到各个单位的数值。
function countDown(time) {
var nowTime = +new Date(); //获取当前时间
var inputTime = +new Date(time); //获取预设时间
var times = (inputTime - nowTime) / 1000; //换算单位 ms -> s
var d = parseInt(times / 60 / 60 / 24); //得到天数
d = d < 10 ? '0' + d : d;
var h = parseInt(times / 60 / 60 % 24); //得到时
h = h < 10 ? '0' + h : h;
var m = parseInt(times / 60 % 60); //得到分
m = m < 10 ? '0' + m : m;
var s = parseInt(times % 60); //得到秒
s = s < 10 ? '0' + s : s;
return d + '天' + h + '时' + m + '分' + s + '秒';
}
console.log(countDown('2020-5-6 21:19:00')); //00天00时00分51秒
2020.5.7
基本包装类型
基本包装类型 就是把简单数据类型包装为复杂数据类型,这样基本数据类型就有了属性和方法。
为了方便操作基本数据类型,JS提供了三个特殊的引用类型:String、Number 和 Boolean。
字符串对象
字符串所有的方法都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串。
根据字符返回位置
var str = '改革春风吹满地,春天到啦';
console.log(str.indexOf('春')); //2
console.log(str.indexOf('春', 3)); //8
案例:查找字符串中某个字符出现的次数
var str = 'oabcoefoxyozzopp';
var count = 0;
var i = 0;
for (var j = 0; j < str.length; j++) {
if (str.indexOf('o', i) != -1) {
i = str.indexOf('o', i);
console.log(i);//0 4 7 10 13
i++;
count++;
}
}
console.log(count);//5
根据位置返回字符
var str = 'andy';
console.log(str.charAt(0)); //a
console.log(str.charCodeAt(1));//110
console.log(str[2]);//d
案例:统计字符串中出现次数最多的字符及其出现次数
这是一道经典的算法题,2020年3月初面试字节跳动日常实习生时被问到。
var str = 'abjfkfsdfdmfpiptroqmx';
var obj = {};
var max = 0;
var ch = '';
//先统计各个字符出现的次数存于obj中
for (var i = 0; i < str.length; i++) {
var chars = str.charAt(i);
if (obj[chars]) {
obj[chars]++;
} else {
obj[chars] = 1;
}
}
//找出最大的出现次数及相应字符
for (var k in obj) {
if (obj[k] > max) {
max = obj[k];
ch = k;
}
}
console.log(max);//4
console.log(ch);//f
其他字符串操作方法
concat substr slice substring 都会返回一个字符串
var str = 'abc';
str = str.concat('efg');
console.log(str); //abcefg
str = str.substr(0, 3);
console.log(str); //abc
str = 'abcdefg';
str = str.slice(1, 3);
console.log(str); //bc
replace 替换字符
语法 replace('被替换的字符','替换为的字符') ,且它只会替换 第一个字符 , 该方法会返回一个字符串。
案例:替换一个字符串 'ahbjfhldiiiddsalomkkkal' 中所有的 'a' 为 '*' 。
var str = 'ahbjfhldiiiddsalomkkkal';
while (str.indexOf('a') != -1) {
str = str.replace('a', '*');
}
console.log(str);//*hbjfhldiiidds*lomkkk*l
split 将字符串分割为数组
语法 split(' 分隔符 ') ,它会返回一个数组
var str = 'a&b&c&d';
var arr = str.split('&');
console.log(arr); //["a", "b", "c", "d"]
API 与 Web API
通俗讲,其实 API 就是接口。
2020.5.8
DOM
DOM简介
DOM树
获取页面元素节点 之 利用DOM提供的方法获取
获取页面元素,进而可以操作元素。主要有以下几种方式:
getElementById 根据ID获取页面元素
1 因为文档页面从上往下加载,先要有标签,故script写在标签后面。
2 返回的是一个元素对象。
3 需要 console.dir 才能输出详细信息。
<div id="time">2020-5-8</div>
<script>
var time = document.getElementById('time');
console.log(time); //打印Element标签
console.dir(time); //该Element对象的详细信息
</script>
getElementsByTagName 根据标签名获取
1 返回带有指定标签名的对象的集合。
2 不需要 console.dir 也能输出详细信息。
<ul>
<li>知否知否,应是绿肥红瘦</li>
<li>知否知否,应是绿肥红瘦</li>
<li>知否知否,应是绿肥红瘦</li>
<li>知否知否,应是绿肥红瘦</li>
<li>知否知否,应是绿肥红瘦</li>
</ul>
<ul id="nav">
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
</ul>
<script>
var list = document.getElementsByTagName('li');
console.log(list); //页面中的全部li标签对象
console.dir(list);
var nav = document.getElementById('nav');
var navList = nav.getElementsByTagName('li');
console.log(navList); //#nav中的li标签对象
</script>
通过 H5 新增方法获取
这一些方法会存在兼容问题。
document.getElementsByClassName('类名') 根据类名返回元素对象集合
document.querySelector('选择器') 根据指定选择器返回第一个元素对象
document.querySelectorAll('选择器') 根据指定选择器返回所有符合的元素对象
<ul class="title">
<li>知否知否,应是绿肥红瘦</li>
<li>知否知否,应是绿肥红瘦</li>
<li>知否知否,应是绿肥红瘦</li>
<li>知否知否,应是绿肥红瘦</li>
<li>知否知否,应是绿肥红瘦</li>
</ul>
<ul id="nav">
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
</ul>
<script>
var title = document.getElementsByClassName('title');
console.log(title);
var firstBox = document.querySelector('li');
console.log(firstBox);
var li = document.querySelectorAll('li');
console.log(li);
</script>
document.body 获取body元素
当然,通过其他方法也可以获取。
var body = document.body;
console.log(body);
doucument.documentElement 获取html元素
当然,通过其他方法也可以获取。
var box = document.documentElement;
console.log(box);
获取页面元素节点 之 利用节点层级关系获取
节点概述
获取父级节点
获取子节点伪数组
获取子节点数组有两种方法,一种是用childNode属性,另一种是用children属性。
获取某个子节点
firstchild 和 lastchild 会返回一个子节点,但不一定是元素节点。
firstElementChild 会返回第一个元素子节点,lastElementChild 会返回最后一个一个元素子节点,但是它们会有兼容性问题。
方便且兼顾兼容性的 获取第一个元素子节点 和 最后一个元素子节点 的方法
获取兄弟节点
获取兄弟元素节点
方便且兼顾兼容性的 获取下一个元素兄弟节点 的方法
事件基础
事件类型
补充
禁止鼠标右键菜单 contextmenu 、禁止鼠标选中 selectstart
mouseenter 和 mouseleave 事件
注册事件概述(绑定事件)
给元素添加事件,称为 注册事件 或者 绑定事件。
注册事件有两种方式:传统方式 和 方法监听注册方式
addEventListener 事件监听方式
attachEvent 事件监听方式(了解)
这种方式是用于兼容ie8及更低版本的,并且该方法是ie专有的,不推荐使用。
删除事件
<button id="btn">按钮</button>
<script>
var button = document.getElementById('btn');
//1 传统注册方式
button.onclick = function () {
alert('Hello');
button.onclick = null;
};
//2 方法监听注册方式 ,如果需要删除注册,则不能用匿名函数
button.addEventListener('click', fn);
function fn() {
alert('Hello');
button.removeEventListener('click', fn);
}
</script>
执行事件的步骤
给元素添加事件:1 获取事件源 2 绑定事件 3 添加事件处理程序
<button id="btn">按钮</button>
<script>
var button = document.getElementById('btn');
//1 传统注册方式
button.onclick = function () {
alert('Hello');
};
//2 方法监听注册方式 addEventListener
button.addEventListener('click', function () {
alert('Hello');
});
</script>
页面元素 之 常用元素的属性操作
js 可以通过DOM操作改变网页内容、结构和样式等。
element.innerText 和 element.innerHTML
区别:innerText 不能识别html标签,非W3C标准,会去除空格和换行。
案例:
<button id="btn">按钮</button>
<p id="text">我是文字</p>
<script>
var text = document.getElementById('text');
var btn = document.getElementById('btn');
text.onclick = function () {
text.innerText = '使用<strong>innerText</strong>进行修改';
}
btn.onclick = function () {
text.innerHTML = '使用<strong>innerHTML</strong>进行修改';
}
</script>
效果:
其他常见元素属性
通过修改这些元素属性可以动态修改页面,src、href 、id、alt 和 title。
操作页面元素 之 表单元素的属性操作
通过修改这些表单元素属性可以动态修改页面,type、value 、checked、selected 和 disabled。
案例:
<button id="btn">按钮</button>
<input type="text" id="text" value="我是文字"></input>
<script>
var input = document.getElementById('text');
var btn = document.getElementById('btn');
btn.onclick = function () {
input.value = '我被点击了';
this.disabled = true;
}
</script>
效果图:
操作页面元素 之 样式属性操作
JS 修改页面样式时,如果修改少量可以通过设置style的方式,否则可以通过设置class的方式。
案例:
<!-- 使用style修改样式 -->
<style>
#box {
width: 100px;
height: 100px;
background-color: blue;
}
</style>
<body>
<div id="box"></div>
<script>
var box = document.getElementById('box');
box.onclick = function () {
this.style.backgroundColor = 'red';
}
</script>
</body>
<!-- 使用className修改样式 -->
<style>
.box {
width: 100px;
height: 100px;
background-color: blue;
}
.change {
width: 200px;
height: 200px;
background-color: red;
}
</style>
<body>
<div id="box" class="box"></div>
<script>
var box = document.getElementById('box');
box.onclick = function () {
this.className = 'change';
}
</script>
</body>
<!-- 使用className修改样式,且保留原class -->
<style>
.box {
width: 100px;
height: 100px;
background-color: blue;
}
.change {
border-radius: 50px;
}
</style>
<body>
<div id="box" class="box"></div>
<script>
var box = document.getElementById('box');
box.onclick = function () {
// 或者 this.className += ' change'; ← 这种不好,因为频繁触发字符串会一直加下去
this.className = 'change box';
console.log(this.className);
}
</script>
2020.5.15
自定义属性
H5设置自定义属性
H5获取自定义属性
操作页面元素 之 获取属性
获取属性有两种方法:
1 element.属性
2 element.getAttribute('属性');
区别:
element.属性 用于获取内置属性值,即元素本身自带的属性
element.getAttribute('属性'); 主要获得自定义的属性,即程序员自定义的属性
2020.6.1
创建元素节点
document.write 、element.innerHTML 、document.createElement
添加节点
添加节点有两种形式:
第一种是 node.appendChild 添加到父节点的子节点列表末尾;
第二种是 node.insertBefore 添加到父节点的指定子元素的前面
删除节点
复制节点
复制节点可分为 深拷贝 和 浅拷贝
2020.6.2
DOM事件流
捕获阶段 & 冒泡阶段
事件对象概念
事件对象的兼容性方案
事件对象常用属性和方法
e.target 指向的是触发的对象,比如点击对象;而,回调函数内this指向的是绑定事件的对象,两者不一定相同。
阻止事件冒泡用 e.stopPropagation()
事件委托(代理、委派)
鼠标事件对象
坐标属性
常用键盘事件
键盘事件对象
BOM
BOM概述
BOM的构成
一般我们是不能直接访问 global 对象的。故在浏览器上,它提供的 window 对象扮演了 global 的角色。
window对象的常见事件
窗口加载事件 load & DOMContentLoaded
一种是window的 load 事件,另一种是window的 DOMContentLoaded 事件。
窗口调整大小事件 resize
定时器1 setTimeout
var time1 =setTimeout(fn1,3000);
var time2 =setTimeout(fn2,5000);
定时器2 setInterval
清除定时器1 clearTimeout
清除定时器 2 clearInterval
2020.6.3
this 指向
1、全局作用域或者普通函数中 this 指向全局对象window (注意定时器里面的this指向window)
<script>
var num = 2;
function fn() {
var num = 33;
console.log(this.num);
}
fn();//2
</script>
2、方法调用中谁调用 this 指向谁
3、构造函数中 this 指向构造函数的实例
JS 执行队列(执行机制)
同步和异步
JS 语言的一大特点就是单线程。
同步和异步的本质区别:这条流水线上各个流程执行顺序不同。
事件循环
URL
location 对象
应用:点击按钮跳转页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
</style>
</head>
<body>
<button>点击</button>
<div></div>
<script>
var btn = document.querySelector('button');
var div = document.querySelector('div');
var num = 5;
btn.addEventListener('click', function () {
div.innerHTML = num + 's后将跳转页面';
setInterval(function () {
if (num == 0) {
location.href = 'http://www.baidu.com';
} else {
num--;
div.innerHTML = num + 's后将跳转页面';
}
}, 1000);
})
</script>
</body>
</html>
应用:页面之间传递参数
//页面a.html
<input type="text">
<button>登陆</button>
<script>
var btn = document.querySelector('button');
var ipt = document.querySelector('input');
btn.addEventListener('click', function () {
window.location.href = 'b.html?username=' + ipt.value;
})
</script>
// 页面b.html
<div></div>
<script>
var div = document.querySelector('div');
var string = window.location.search.substr(1);
var arr = string.split('=');
console.log(arr);
div.innerHTML = '欢迎您,' + arr[1];
</script>
navigator 对象
history 对象
元素偏移量 offset 系列
offset 与 style 的区别
在 js 里面获取元素的宽高等属性,只能通过 element.style.width 和 element.offsetWidth ,而不能用 element.width。前者只能获取行内样式的属性,后者可以获取内嵌样式等表的属性。
应用:获取鼠标在盒子内的坐标
e.pageX 和 e.pageY 是鼠标相对于DOM窗口的坐标,而 offsetTop 和 offsetLeft 是元素距离页面窗口的距离。两者相减,就能得到鼠标在元素内的坐标。
效果:
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
margin: 100px 0 0 100px;
background-color: palegoldenrod;
}
</style>
</head>
<body>
<div></div>
<script>
var div = document.querySelector('div');
document.addEventListener('mousemove', function (e) {
div.innerHTML = 'x坐标为' + (e.pageX - div.offsetLeft) + 'y坐标为' + (e.pageY - div.offsetTop);
})
</script>
</body>
</html>
2020.6.5
元素可视区 client 系列
立即执行函数
可以加函数名
(function fn() {})()
(function fn() {}())
元素 scroll 系列
页面被卷去的头部部分
元素被卷去的头部 element.scrollTop , 页面被卷去的头部 window.pageYOffset。
DTD 指的是 <!DOCTYPE html>
offset client scroll 三大系列属性总结比较
JavaScript 动画
动画实现原理
通过定时器 setInterval 不断使元素发生变化
缓动动画实现原理
案例:浏览器右边弹出选框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.sliderbar {
position: fixed;
right: 0;
bottom: 100px;
width: 40px;
height: 40px;
text-align: center;
line-height: 40px;
cursor: pointer;
color: #fff;
}
.con {
position: absolute;
left: 0;
top: 0;
width: 200px;
height: 40px;
background-color: purple;
z-index: -1;
}
</style>
</head>
<body>
<div class="sliderbar">
<span>←</span>
<div class="con">问题反馈</div>
</div>
<script>
// 1. 获取元素
var sliderbar = document.querySelector('.sliderbar');
var con = document.querySelector('.con');
// 当我们鼠标经过 sliderbar 就会让 con这个盒子滑动到左侧
// 当我们鼠标离开 sliderbar 就会让 con这个盒子滑动到右侧
sliderbar.addEventListener('mouseenter', function () {
// animate(obj, target, callback);
animate(con, -160, function () {
// 当我们动画执行完毕,就把 ← 改为 →
sliderbar.children[0].innerHTML = '→';
});
})
sliderbar.addEventListener('mouseleave', function () {
// animate(obj, target, callback);
animate(con, 0, function () {
sliderbar.children[0].innerHTML = '←';
});
})
function animate(obj, target, callback) {
// console.log(callback); callback = function() {} 调用的时候 callback()
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function () {
// 步长值写到定时器的里面
// 把我们步长值改为整数 不要出现小数的问题
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
callback && callback();
}
// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
</script>
</body>
</html>
轮播图思路
节流阀
滚动窗口至文档中的特定位置
使用 window.scroll( x , y ) 即可。
2020.6.6
touch 移动端触摸事件
TouchEvent 触摸事件对象
移动端拖动元素
移动端 click 延时解决方案
有 三种 解决方案
移动端常用开发插件
Swiper 轮播图插件
其他特效插件
superslide 、iscroll 、 zy.media.js
插件使用小结
框架与插件的区别
本地存储
sessionStorage
特点:单页面内使用(共享),关掉页面就没了
使用方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" name="" id="">
<button>存储数据</button>
<button>获取数据</button>
<button>删除数据</button>
<button>删除所有数据</button>
<script>
var ipt = document.querySelector('input');
var btns = document.querySelectorAll('button');
btns[0].addEventListener('click', function () {
sessionStorage.setItem('uname', ipt.value);
})
btns[1].addEventListener('click', function () {
console.log(sessionStorage.getItem('uname'));
})
btns[2].addEventListener('click', function () {
sessionStorage.removeItem('uname');
})
btns[3].addEventListener('click', function () {
sessionStorage.clear();
})
</script>
</body>
</html>
查看方式
localStorage
特点: 同一浏览器内通源页面共享,不删除则永久存在
使用方式
与 sessionStorage 类似
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="ipt">
<input type="checkbox" id="remember">
<label for="remember">记住用户名</label>
<script>
var ipt = document.querySelector('#ipt');
var remember = document.querySelector('#remember');
if (localStorage.getItem('username')) {
ipt.value = localStorage.getItem('username');
remember.checked = true;
}
ipt.addEventListener('change',function(){
if (remember.checked) {
localStorage.setItem('username', ipt.value);
}
})
remember.addEventListener('change', function () {
if (remember.checked) {
localStorage.setItem('username', ipt.value);
} else {
localStorage.removeItem('username');
}
})
</script>
</body>
</html>
查看方式
与 sessionStorage 类似
2020.6.7
JavaScript 库
jQuery 概念
jQuery的入口函数
jQuery的顶级对象 $
jQuery对象 和 DOM对象
注意 jQuery对象只能使用jQuery对象属性方法;DOM对象只能使用原生的JS属性和方法
jQuery对象 和 DOM对象 的相互转换
补充: DOM对象 转换为 jQuery对象还有一种方式:
var myDiv = document.querySelector('div');
$(myDiv).hide();
jQuery 基础选择器
jQuery 隐式迭代
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="jquery-3.4.1.js"></script>
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div></div>
<div></div>
<div></div>
<div></div>
<script>
$(function () {
$('div').css('background-color', 'yellow');
});
</script>
</body>
</html>
jQuery 筛选选择器
jQuery 筛选方法
方便书写链式编程。
如果需要返回所有父级元素,可用 .parents() 或 .parents(selector) 。
取返回的伪数组中的某个元素,用 eq(index) 得到 jq对象;若用 [index] 得到 dom对象。
2020.6.8
jQuery 操作css
// 2
$('.wrapper').css('height', 240); //√
$('.wrapper').css('height', '240px'); //√
$('.wrapper').css('height', 240 px); //×
// 3
$('.wrapper').css({
'color': 'red',
'font-size': '20px',
'height': 280
}); //√
$('.wrapper').css({
color: 'red',
fontSize: '20px',
height: 280
}); //√
jQuery 设置类样式方法
jQuery 链式编程
jQuery 元素动画
显示隐藏效果
滑动效果
淡出淡入效果
自定义动画 animate
animate 是元素动画,不能给document对象设置动画。
$('button').eq(3).click(function () {
$('div').animate({
width: 500,
height: 400
}, 100);
});
jQuery 动画队列及停止排队的方法
jQuery 属性操作
prop() 设置或获取固有属性值
attr() 设置或获取元素自定义属性值
data() 数据缓存
读取自定义属性值时,不需要加"data-"前缀。
jQuery 内容文本值操作
文本框的值用 val()
保留指定小数位 toFixed()
把 Number 四舍五入为指定小数位数的数字
//规定小数的位数,是 0 ~ 20 之间的值,包括 0 和 20,有些实现可以支持更大的数值范围。如果省略了该参数,将用 0 代替。
NumberObject.toFixed(num)
jQuery 元素操作
遍历元素 each
创建元素
添加元素 之 在内部添加
注意 : prepend 可以追加在内部的 前面 。
添加元素 之 在外部添加
删除元素
jQuery 获取元素尺寸
jQuery 位置操作
offset 设置或获取元素偏移
position 获取元素偏移
只能获取,不能设置
scrollTop / scrollLeft 设置或获取元素被卷去的头部和左侧
2020.6.9
jQuery 事件注册
单个事件的注册
单个或多个事件的注册 on
优势3的本质是事件委托,把事件绑定到动态生成的元素的父元素上。如此一来无论新建多少元素,这些元素都能触发相应事件。
一次性事件的注册 one
绑定方式与 on 相似。
jQuery 解绑事件 off( )
jQuery 自动触发事件 trigger( )
只是不需要特定的触发方式便能调用,如果要实现“自动”,仍然需要定时器。
jQuery 阻止默认行为 及 阻止事件冒泡
jQuery 对象拷贝
语法解释:把 object 复制(合并)到 target 里去。
如果是浅拷贝,对于复杂数据类型,相当于是复制了引用,并且会复制完成后会覆盖冲突的数据。
如果是深拷贝,对于复杂数据类型,相当于是复制了相同数据,而复制完成后对于冲突数据,会合并而非覆盖。
jQuery 多库共存
方法1:使用 jQuery 代替 $
方法2:释放 jQuery 对 $ 的控制权,让用户自定义
jQuery 插件
jQuery 插件 之 懒加载
jQuery 获取元素索引号
index() 方法返回指定元素相对于其他指定元素的 index 位置,如果未找到元素,index() 将返回 -1。
$("li").click(function(){
alert($(this).index());
});
2020.6.10
面向对象 与 面向过程 对比
ES6中的类和对象
class 创建类
类名一般大写,且类名后面不加小括号。
constructor 类构造函数
class Star {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
var ldh = new Star('liudehua', 18);
var zxy = new Star('zhangxueyou', 20);
类添加方法
多个方法之间不用写逗号。
使用类的注意事项
1 ES6的类中没有变量提升,必须先要定义类,才能实例化类
2 类里面的共有属性和方法一定要加 this 才能使用
3 constructor 里面的 this 指向实例对象,方法里面的 this 一般指向这个方法的调用者
ES6 类的继承
就近原则:实例化子类调用方法时,会先查找子类中有无该方法,如果没有再去父类中查找。
super 关键字
super (parameter) 调用父类构造函数
super.xxx (parameter) 调用父类普通函数
注意 构造函数内使用super,必须把super放到最前面。
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
super(x, y)
}
}
var son = new Son(1, 2);//3,因为子类对象里面没有x,y只能去父类对象中取
son.sum();
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
super(x, y);
this.x = 2;
this.y = 5;
}
}
var son = new Son(1, 2);//7
son.sum();
//情形 1 输出 hello!
class Father {
say() {
console.log('hello!');
}
}
class Son extends Father {
}
var son = new Son();
son.say();
//情形2 输出 hi~
class Father {
say() {
console.log('hello!');
}
}
class Son extends Father {
say() {
console.log('hi~');
}
}
var son = new Son();
son.say();
//情形3 输出 hello! hi~
class Father {
say() {
console.log('hello!');
}
}
class Son extends Father {
say() {
super.say();
console.log('hi~');
}
}
var son = new Son();
son.say();
ES6继承小结
ES5是通过原型来模拟继承的操作的,然后ES6大概就是将ES5的这些操作封装起来。
ES6 子类 extends 父类 时,如果子类有 constructor(构造函数) ,则一定要在子类 constructor 内使用 super 调用父类的 constructor,否则会报错。
成功创建子类对象实例之后,调用某一个方法的查找顺序是这样的,先看子类对象中有无该方法,若有则执行,若无则再查找父类,以此类推。属性的查找同理。
ES5 构造函数
静态成员 与 实例成员
//实例成员就是构造函数内部通过this添加的成员,只能通过实例化的对象来访问
function Star(name) {
this.name = name;
}
var star = new Star('lili');
console.log(star.name); //lili
//静态成员就是在构造函数本身添加的成员,只能通过构造函数来访问。
Star.sex = 'man';
console.log(Star.sex); //man
构造函数存在的问题
浪费内存。因为对于相同的方法,实例之间是各自拥有,而不是共享。
构造函数原型对象 prototype
function Star(name) {
this.name = name;
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var star1 = new Star('lili');
var star2 = new Star('dudu');
console.log(star1.sing === star2.sing); //true
对象原型 __proto__
构造函数 constructor
如果覆盖了构造函数的原型对象,需要让原型对象内的 constuctor 重新指向原来的构造函数。
function Star(name) {
this.name = name;
}
Star.prototype = {
constructor: Star,
sing: function () {
console.log('sing');
},
movie: function () {
console.log('moveie');
}
}
var star1 = new Star('lili');
var star2 = new Star('dudu');
构造函数 、实例对象 和 原型对象 三者的关系
原型链
JS的成员查找机制
原型对象中 this 的指向问题
1 构造函数里面的 this 指向的是实例化所得到的实例对象
2 原型对象 prototype 函数里面的 this 指向的是调用者,即实例对象
扩展内置对象
ES5 继承
ES5并没有提供ES6的 extends 继承,所以我们通过 构造函数 + 原型对象 来模拟实现继承,称为组合继承。
call ( ) 调用函数
如果没有参数,则直接像普通函数一样运行。
var name = 'lolo';
var star = {
name: 'Jane'
}
function fn() {
var name = 'sony';
console.log(this.name);
}
fn(); //lolo
fn.call(star); //Jane
借用构造函数继承父类型属性
公有方法写在父类构造函数中即可
function People(name, age) {
this.name = name;
this.age = age;
}
function Newpeople(name, age, sex) {
//在父类属性基础上,增加一个sex
this.sex = sex;
People.call(this, name, age);
}
var newpeople = new Newpeople('sony', 18, 'man');
console.log(newpeople); //{age: 18,name: "sony",sex: "man"}
借用原型对象继承父类方法
不直接通过修改 Son.prototype.__proto__属性 进行继承,可能是因为__proto__属性是非标准属性,不能随意赋值。
function Father() {
}
//父类原型对象中的共有方法
Father.prototype.money = function () {
console.log(10000);
}
function Son() {
}
Son.prototype = new Father();
Son.prototype.constructor = Son;
var son = new Son();
son.money();
ES5新增遍历数组方法
array.forEach 循环数组
函数内遇到 return true 不会终止迭代。
var arr = ['a', 'b', 'c', 'd'];
arr.forEach(function (value, index, arr) {
console.log(value, index); //a 0 、 b 1 、 c 2 、 d 4
})
array.filter 过滤数据
函数内遇到 return true 不会终止迭代。主要用于过滤数据,当然也可以遍历。
var arr = [12, 34, 56, 78, 90];
var newArr = arr.filter(function (value, index, arr) {
return value >= 40;
})
console.log(newArr); //56,78,90
array.some 检查符合某条件的元素是否存在 / 查找唯一元素
函数内遇到 return true 会终止迭代,也可用于遍历。
var arr = [12, 34, 56, 78, 90];
var flag = arr.some(function (value, index, arr) {
return value >= 40;
})
console.log(flag); //true
ES5 新增字符串方法
trim 去除字符串两侧空格
ES5 新增对象方法
object.keys 获取对象属性名
var obj = {
name: 'liya',
age: 18,
sex: 'man'
};
var arr = Object.keys(obj);
console.log(arr); //["name", "age", "sex"]
object.defineProperty 定义或修改属性
函数内的this指向
改变函数内的 this 指向
call 方法
var name = 'sandy';
var o = {
name: 'andy'
};
function fn() {
console.log(this.name);
}
fn.call(o); //andy
fn(); //sandy
apply 方法
传参方式特别,apply 常搭配 Math 方法使用。
//因为Math对象的一些方法只能接收一个个的参数,而不是接收一整个的数组。
//而apply方法接收一个参数数组,并可以将数组元素作为参数一个个传给调用的方法。
//apply与Math配合起来,就能将一个个数字传给Math方法,从而可以达到数组也能使用Math方法的效果
function fn2(num1, num2, num3) {
console.log(num1 + num2 + num3);
}
fn2.apply(window, [2, 6, 3]);
bind 方法
不调用函数,但会改变函数 this 的指向。
var o = {
name: 'soso'
}
function fn(a, b) {
console.log(this);
console.log(a + b);
}
var f = fn.bind(o, 1, 3);
f(); //{name: "soso"} & 4
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
</body>
<script>
//给三个按钮添加点击事件,点击后失效,2s后复原
//这里如果用apply或call修改this,则会使定时器内函数立刻执行
var btns = document.querySelectorAll('button');
btns.forEach(function (val, index) {
val.onclick = function () {
this.disabled = true;
setTimeout(function () {
this.disabled = false;
}.bind(this), 2000);
}
})
</script>
三种方法的比较
2020.6.12
严格模式概念
开启严格模式
严格模式导致的变化
高阶函数
闭包
1 延伸了变量的作用范围。
2 利用闭包可以解决一些同步异步产生的问题,详情看案例。
//闭包指向fn
function fn() {
var num = 10;
return function () {
console.log(num);
}
}
var f = fn();
f();
案例1:用闭包的形式完成点击 li 输出当前 li 的索引号
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>第一个</li>
<li>第二个</li>
<li>第三个</li>
<li>第四个</li>
</ul>
</body>
<script>
//利用闭包方式得到当前li的索引号
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].onclick = function () {
console.log(i);
}
})(i);
}
</script>
</html>
案例2:利用闭包方式,通过定时器在3s后打印所以li的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>第一个</li>
<li>第二个</li>
<li>第三个</li>
<li>第四个</li>
</ul>
</body>
<script>
//利用闭包方式,通过定时器在3s后打印所以li的内容
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
(function (i) {
setTimeout(function () {
console.log(lis[i].innerHTML);
}, 3000)
})(i)
}
</script>
</html>
思考题:
//本题不存在闭包
var name = 'the windows';
var object = {
name: 'my object',
getNameFunc: function () {
var name = 'inner';
return function () {
return this.name;
};
}
};
console.log(object.getNameFunc()()); //the windows
//本题存在闭包
var name = 'the windows';
var object = {
name: 'my object',
getNameFunc: function () {
var that = this;
return function () {
return that.name;
};
}
};
console.log(object.getNameFunc()()); //my object
递归
var n = 1;
function fn() {
console.log('第' + n + '次打印');
if (n === 6) {
return false;
}
n++;
fn();
}
fn();
递归数学题一 之 求n的阶乘
//利用递归函数求 n 的阶乘
function fn(n) {
if (n == 1) {
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
递归数学题二 之 求斐波那契数列的第n项
//利用递归函数求斐波那契数列第 n 项
// 1 1 2 3 5 8 13 21....
//我们只需要知道用户输入的n的前两项(n-1) 和 (n-2) 即可算出第n项
function fn(n) {
if (n === 1 || n === 2) {
return 1;
}
return fn(n - 1) + fn(n - 2);
}
console.log(fn(8));//21
递归遍历数据 之 查找某 id 的数据
//data如下,从大类到细类,各个类都有对应的id
//查找某id对应的类别
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
gname: '冰箱'
}, {
id: 12,
gname: '洗衣机'
}]
}, {
id: 2,
name: '服饰'
}];
function getId(data, id) {
data.forEach(function (item) {
if (item.id == id) {
console.log(item);
} else if (item.goods && item.goods.length > 0) {
getId(item.goods, id)
}
})
}
getId(data, 12); //{id: 12, gname: "洗衣机"}
JS 对象拷贝
ES6以前 实现浅拷贝
利用 for in ( for in 得到的是属性名)。但是对于复杂数据,比如 own,复制的是地址,所以 o 修改了 own ,obj 中的 own 也会变化。
var obj = {
id: 1,
name: 'lily',
own: {
money: 100
}
};
var o = {};
for (var k in obj) {
//k是属性名
o[k] = obj[k];
}
ES6 实现浅拷贝的语法糖
不需要再用 for in。
var obj = {
id: 1,
name: 'lily',
own: {
money: 100
}
};
var o = {};
Object.assign(o, obj);
ES6 实现浅拷贝的扩展运算符
let a = {
age: 1
}
let b = {
...a
}
a.age = 2
console.log(b.age) // 1
JS 实现深拷贝的两种方法
用 for in 递归遍历,注意 array 是复杂数据类型并且也是对象类型 。
var obj = {
id: 1,
name: 'lily',
own: {
money: 100
}
};
var o = {};
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
//需要判断属性属于哪种数据类型
//先把属性赋值给item
var item = oldobj[k];
//如果item是数组类型(数组也是复杂数据类型),需要遍历复制
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item);
//如果item是数组外的对象类型,需要遍历复制
} else if (item instanceof Object) {
newobj[k] = {};
deepCopy(newobj[k], item);
//其他基本类型直接赋值
} else {
newobj[k] = item;
}
}
}
deepCopy(o, obj);
另外,还可以借用 JSON.parse 和 JSON.stringify 来实现深拷贝。
2020.6.13
正则表达式
创建正则表达式
测试正则表达式
正则表达式特殊字符 之 ^ $
var reg2 = /^123$/;
console.log(reg2.test(123)); //true
console.log(reg2.test(123123)); //false
正则表达式特殊字符 之 [ ] 、 ^[ ]$ 、 ^[^ ]$
// [] 表示有一系列字符可供选择,只要匹配其中一个即可
var reg = /[abc]/; //内容只要包含abc其中一个即可
console.log(reg.test('andy')); //true
console.log(reg.test('red')); //false
var reg = /^[abc]$/; //三选一,内容只能是a 或 b 或 c
var reg = /^[a-z]$/; //内容只能是26个字母中的一个
var reg = /^[a-zA-Z0-9-_]$/; //内容只能是其中一个
var reg = /^[^a-zA-Z0-9-_]$/; //一个内容,但不能包含其中的任何一个字符/数字
正则表达式量词符
设定前面模式的出现次数
var reg1 = /^a*$/;
console.log(reg1.test('aaaa')); //true
var reg2 = /^a+$/;
console.log(reg2.test('aaaa')); //true
var reg3 = /^a?$/;
console.log(reg3.test('aaaa')); //false
var reg4 = /^a{2}$/;
console.log(reg4.test('aaaa')); //false
var reg5 = /^a{2,}$/;
console.log(reg5.test('aaaa')); //true
var reg6 = /^ab{2,4}$/;
console.log(reg6.test('abbb')); //true
var reg7 = /^(ab){2,4}$/;
console.log(reg7.test('abababab')); //true
var reg8 = /^[a-zA-Z0-9-_]{2,8}$/;
console.log(reg8.test('abb-_bb')); //true
预定义类
正则表达式参数
//把所有的开心或快乐替换成兴奋
var newString = oldString.replace(/开心|快乐/g, '兴奋');
ES6 关键字 let
if (true) {
let a = 10;
var b = 20;
}
console.log(b); //20
console.log(a); //报错
特点
1 let 不存在变量提升,需要先声明,才能使用。
2 let 会产生暂时性死区。
//let 会产生暂时性死区。
//在块级作用域内部,只要有let声明某个变量,这个变量就不同于外面的变量,是属于块级作用域的,与块级作用域绑定了。
//所以在这个if里面,有一个块级作用域的num。
//然后这里先使用num再声明num,而let没有变量提升,故报错。
var num = 10;
if (true) {
console.log(num);//报错
let num = 20;
}
相关面试题一
这里因为是使用 var 来声明 i,所以 for 产生的两个作用域都属于全局作用域。因此当函数要打印 i 的时候,所用到的是全局作用域的 i,且 i=2。
相关面试题二
这里因为是使用 let 来声明 i,所以for产生的两个作用域都属于块级作用域。第一个块级作用域里面的 i=0,第二个 i=1。当函数打印 i 时,会去自己对应的上级作用域寻找 i,所以就输出了相应的块级作用域里面的 i 。
ES6 关键字 const
对于复杂数据类型,可以修改其属性或者内部的某个值,但是依然不可更改其地址。
var 、let 、const 的区别
一般定义不会更改的函数以及常数可以使用 const ,这样JS解析不需要实时监控变化,效率更高。
用 const 的时候,对于复杂数据类型,可以修改其属性或者内部的某个值,但是依然不可更改其地址。
ES6 解构赋值
数组解构
方便从数组中取值。平时我们从数组取值,可能需要多个声明,一个个地进行赋值,现在一句话就可以搞定。
var arr = [1, 2, 3, 4];
//这里相当于 a b c d e 都是用var声明的
var [a, b, c, d, e] = arr;
console.log(a); //1
console.log(b); //2
console.log(c); //3
console.log(d); //4
console.log(e); //undefined
对象解构
解构时的变量名,一定要与对象中的属性名相匹配,才可以解构成功。
var obj = {
name: 'lily',
age: 18,
sex: 'man'
};
//这里相当于花括号内的属性都是用var声明的
var {name, age, sex, money} = obj;
console.log(name); //lily
console.log(age); //18
console.log(sex); //man
console.log(money); //undefined
//如果用 xx:xx 的形式,则左边的是所匹配的属性,右边的是变量名(属性别名)
var {name:myname, age:myage, sex:mysex, money:mymoney} = obj;
console.log(myname); //lily
console.log(myage); //18
console.log(mysex); //man
console.log(mymoney); //undefined
箭头函数
注:
1 => 右边只有一句代码即可省略 {} ,而非一定需是返回值。(maybe)
2 箭头函数内是用不了 arguments 的,只能用 剩余参数 的方法获取全部形参。
//上面图片的代码输出的是两个obj对象
//下面这段代码输出的是两个window对象
//因为箭头函数本身不绑定this,所以它所在的区域this指向的是谁,它里面的this就指向谁
//图片案例里面,this指向obj;下面代码中,this指向window
function fn() {
console.log(this);
return () => {
console.log(this);
}
}
const obj = {
name: 'zhangsan'
};
const resFn = fn();
resFn();
箭头函数面试题
//因为箭头函数没有自己的this,所以要看它所处的区域的this指向谁。
//它所处的区域为obj,obj是对象,不能自己构建一个作用域,obj本身也属于全局作用域
//所以say方法中的this指向window
var age = 100;
var obj = {
age: 20,
say: () => {
alert(this.age);
}
}
obj.say(); //100
剩余参数
const sum = (...args) => {
let total = 0;
args.forEach(item => total += item);
return total;
}
console.log(sum(10, 20, 50));//80
剩余参数和解构配合使用
let students = ['张三', '王五', '李四'];
let [s1, ...s2] = students;
console.log(s1); //'张三'
console.log(s2); //['王五','李四']
ES6 Array的扩展方法(△)
let arr = [1, 2, 3, 4];
console.log(...arr); // 1 2 3 4 ,等价于下面的写法。
console.log(1, 2, 3, 4);
扩展运算符用于合并数组
var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
console.log([...arr1, ...arr2]);
arr1.push(...arr2);
console.log(arr1);
扩展运算符用于将伪数组转换为数组
<body>
<div>
a
</div>
<div>
b
</div>
</body>
<script>
var divs = document.querySelectorAll('div');
var divarr = [...divs];
console.log(divs); //NodeList(2) [div, div]
console.log(divarr); //(2) [div, div]
</script>
ES6 Array扩展方法
将伪数组转换为数组 Array.from
除了用扩展运算符的方法(上面代码),还可以使用 Array 构造函数 中的 from 方法。
<body>
<div>
a
</div>
<div>
b
</div>
</body>
<script>
var divs = document.querySelectorAll('div');
var divarr = Array.from(divs);
console.log(divs); //NodeList(2) [div, div]
console.log(divarr); //(2) [div, div]
</script>
let arrayLike = {
'0': 1,
'1': 2,
'length': 2
}
let newArr = Array.from(arrayLike, item => item * 2);
console.log(newArr);
找出第一个符合条件的数组成员 find
找出第一个符合条件的数组成员的位置 findIndex
判断数组是否包含某个值 includes
2020.6.14
ES6 String扩展方法
模板字符串
有了模板字符串(反引号字符串),在拼接字符串的时候就不需要再用 + + 了。
//变量没有要求必须为反引号字符串,当然反引号字符串也可以
var str = 'lily';
//反引号字符串才能解析变量,普通字符串内部不会解析${}
var sayHello = `hello,my name is ${str}`;
console.log(sayHello); //hello,my name is lily
判断字符串开头结尾 StartsWidth & endsWidth
重复字符串 repeat
数据结构Set
应用:巧用set完成数组去重
const s = new Set(['a', 'a', 'b', 'b']);
console.log(s.size); //2
const arr = [...s];
console.log(arr); //(2) ["a", "b"]
实例方法
遍历
2020.7.7
判断空对象
object.keys(obj).length === 0