【JS】js学习总结(上)-数据类型,正则,DOM,BOM

目录

一.基础

二.数据类型

1.js数据类型

js数字的最大值和最小值

关于js浮点数计算

NaN是什么?

Null和undefined的区别?

2.数据类型检测

3.强制类型转换

1)将其它的数据类型转换为String

2)将其它数据类型转换为Number

3)js表示其它进制的数字 

4)将其它数据类型转换为Boolean

4.数据类型的运算

1)四则运算

2)一元运算符 –/+

3)与运算

4)或运算 

5)比较运算符

6)==、!=、===、!==运算符

7)三目运算符

8)转义字符

四.对象

1.对象的分类

2.创建对象

3.对象属性的增删改

五.内建对象-数组

1.创建数组

2.数组方法

六.Date对象

七.Math对象

八.包装类

九.字符串

1.字符串方法

十.正则表达式

1.正则的创建

2.字符串和正则相关的方法

3.正则对象原型上的方法 

4.常用元字符

1)量词元字符

2)特殊元字符

3)普通元字符 

5.正则捕获的懒惰性 

6.正则的分组

7.正则捕获的贪婪性 

8.正则表达式的断言

9.正则表达式中的.*,.*?,.+?

10.常用的正则表达式

十一.DOM文档对象模型 

1. 获取页面dom元素

2.操作dom元素属性

3.节点操作

4.js操作样式

5.事件

6.事件对象

7.常用事件

8.事件流 

1)事件的冒泡

 2)事件的委派

9.事件绑定

十二.BOM对象

1.Window

1)setInterval()

2)setTimeout()

3)clearInterval()

4)clearTimeout()

2.Navigator

3.Location

4.History

5.Screen


 

一.前置知识

0.关于js

按照相关的js语法,去操作页面中的元素,有时还要操作浏览器里面的一些功能;

需要学习的主要包括三方面的内容:

1)ECMAScript3/5/6...js的语法规范(变量,数据类型,操作语句等等)

2)DOM--文档对象模型,提供一些js的属性和方法,用来操作页面中的DOM元素;

3)BOM--浏览器对象模型,提供一些js的属性和方法,用来操作浏览器。

1.字面量和变量

字面量是一些不可改变的值,字面量可以直接使用

变量可以用来保存字面量,变量的值是可以改变的,更加方便使用,js中用var声明一个变量

var a = 12;
a = 13;
console.log(a);  // 13

2.标识符

js中可以自主命名的都是标识符

遵循如下规则:  

1.可以含有字母,数字,_,$

2.不能以数字开头

3.不能是js中的关键字和保留字(break,var;const)

4.标志符一般采用驼峰命名法,首字母小写每个单词开头字母大写,其余字母小写

3.常用的浏览器

  • webkit内核(v8引擎)  -->  谷歌Chrom,Safari,Opera >= V14,国产浏览器,手机浏览器......
  • Gecko  -->  火狐FireFox
  • Presto  -->  Opera < 14
  • Trident  --> IE(74I01E的EDGE开始采用双内核,其中包括chrom迷你)

 浏览器控制台的功能(以谷歌为例)

1)Elements:查看样式结构,可以修i该这些内容;

2)Console:查看输出结果和报错信息,用于调试js

3)Sources:查看项目源码;

4)Network:查看当前网站所有资源的请求信息(包括服务器传输的http报文信息)、加载时间等(根据加载时间进行项目优化)

5)Application:查看当前网站的数据存储和资源文件(图片)

二.数据类型

1.js数据类型

数据类型就是字面量的类型

js中一共有六种:

String—字符串  Number—数值  Boolean—布尔值

Null—空值  Undefined—未定义  Object—对象

前五个是基本数据类型,对象是引用数据类型;JS中所有数值都是number类型,包括整数和浮点数;

1)js数字的最大值和最小值

JS中可以表示数字的最大值,Number.MAX_VALUE = 1.7976931348623157e+308,如果使用number表示的数字超过了最大值,则会返回一个Infinity(正无穷),使用typeof检查Inifity也会返回number,-Infinty会返回负无穷。Number.MIN_VALUE大于0的最小值 = 5e-324;

2)关于js浮点数计算

用js进行浮点计算可能会得到一个不精确的结果,所以不要用js进行精确度比较较高的计算,这是由于这些浮点是计算机会将其转换成二进制进行计算的,而二进制无法精确表示一个浮点数;

3)NaN是什么?

NaN是一个特殊的数字表示not a number,使typeof检查也是number,比如“ac”*”bd”的结果就是NaN,可以用isNaN()函数检查一个数是不是NaN,返回值为true或false;

在使用isNaN检测的时候,先会验证检测的值是否为数字,如果不是,先基于Number()方法把值转换为数字再检测。 

var b = NaN;
console.log(isNaN(b));  //true

4)Null和undefined的区别?

Null类型的值只有一个,就是null,Null值专门表示一个为空的对象,使用typeof检查null值时会返回object,(一般都是开始不知道值,先手动设置值为null,后期再进行赋值操作);

undefined类型的值只有一个,就是undefined,当声明一个变量但不给它赋值时,它的就是undefined未定义,使用typeof检查它时会返回undefined

null == undefined //true

let num = 0 和 let num = null 的区别在于,赋值0在栈内存中是有存储空间的,赋值null是不占用栈内存的存储空间的

对于getElementById无法获取到指定元素,返回的结果就是null;

函数执行无返回值,默认返回undefined;

变量定义未赋值,输出结果为undefined;

2.数据类型检测

可以用typeof检查一个变量的类型:返回的是数据类型的字符串;

语法:typeof 变量  

它有如下几种返回值: "number","string","undefined","boolean","object","function" 

typeof 返回值的类型都是字符串  

var b = 123;
console.log(typeof b);  // number
console.log(typeof NaN); // number
console.log(typeof {}) // object
console.log(typeof []) // object
console.log(typeof /^$/) // object
console.log(typeof null) // object 
console.log(typeof function(){}) // function

上述案例可以看出typeof不能检测出所有的类型,检测数据类型完整的方案可见下方链接第九点:

(84条消息) (自查用)浏览器工作原理、缓存,前端安全,性能优化,http原理等_玖伴_的博客-CSDN博客

3.强制类型转换

将一个数据类型转换成其它的数据类型,转换为String,Number,Boolean

1)将其它的数据类型转换为String

方式一:调用被转换数据类型的toString()方法

该方法不会影响原变量,他会将转换的结果返回

注意:null和undefined没有toString方法调用会报错;普通对象.toString()的结果是"[objeect Object]",是用来检查数据类型的;

var a = 123;
var a = a.toString();
console.log(typeof a);

方式二:调用String()函数

将转换的数据作为参数传递给函数

使用它做强制类型转化时:

对于Number和Boolean实际上就是调用toString()方法

但是对于null和undefined,不调用toString()方法,会将null转化为“null”,将undefined转换为“undefined”

var a = 123;
a = String(a);
console.log(typeof a);

2)将其它数据类型转换为Number

方式一:使用Number()函数

字符串转换为数值时如果是纯数字的字符串,直接转换

如果字符串中有非数字的内容,则转换为NaN

如果是空串或全是空格的字符串,转换为0

布尔值转换为数字true转换为1,false转换为0

Null转换为数字0

undefined转换为数字是NaN

引用数据类型先把它基于toString方法转换为字符串,再转换为数字

该方法走的是浏览器V8引擎的底层机制,就是以上的几条转换规则。 

console.log(Number([12])); // 12
console.log(Number([])); // 0
console.log(Number([12,23])); // NaN

var a = "124";
a = Number(a);
console.log(typeof a);  // 'Number'

方式二:parseInt()

把一个字符串从左到右依次查找有效数字字符,直到遇到非有效数字字符,将有效的内容取出来然后转换为一个Number;

var a = "123px";
a = parseInt(a);
console.log(a); //123

方式三: parseFloat()

作用和前者类似,不同的是可以转换为有效的小数;

var a = "124";
a = Number(a);
console.log(typeof a);

对于非string使用以上两个方法会先转换为字符串然后再操作 ;

3)js表示其它进制的数字 

需要表示16进制的数字需要以0x开头

需要表示8进制的数字需要以0开头

表示二进制的数字以0b开头,不是所有浏览器都支持(ie不支持);

a = 0xff;
console.log(a); //255
a = 070;
console.log(a); //56

像”070”这种字符串有些浏览器会当作十进制解析,有些会当作八进制解析

我们可以在parseInt()中传递第二个参数,来指定数字的进制;

var a = parseInt("070",8);
var b = parseInt("070",10);
console.log("a=" + a); //56
console.log("b=" + b); //70

4)将其它数据类型转换为Boolean

方式一:使用Boolean()函数

数字转换为布尔:除了0和NaN其它都是true

字符串转换为布尔:除了空串,其余都是true

null和undefined都会转换为false

对象也会转换为true

为任意数据类型做两次非运算,即可将其转换为布尔值

var a = "hello",
a = !!a;
console.log(a);  //true

4.数据类型的运算

1)四则运算

规则:

1)当对非number类型的进行运算时,都会转换为number再运算;

2)任何值与NaN做运算,结果都是NaN;

3)如果对两个字符串做加法运算会拼串;

4)任何的值和字符串做加法运算,都会将其转换为字符串,再拼串

       可以利用这一特点将任意数据类型转换为字符串,实际上也是调用String()函数;

result = 123 + "1";
console.log(result); //1231

5)任何值做-*/运算时都会自动转换为Number;可以利用这一特点隐式转化,可以通过-0 *1 /1转换为Number,原理和Number()一样 

6)null和数字做加法运算会转换为0;布尔值和数字做加法运算true转换成1,false转换为0;数组需要先转换为字符串,再转换为数字,例如[] -> '',[12] -> '12' -> 12

let a = 10 + null + true + [] + undefined + '你好' + null + [] + 10 + false;
console.log(a);  //  11undefined你好null0false


解析:
10 + null => 10 + 0 => 10
10 + true => 10 + 1 => 11
11 + [] => 11 + '' => '11'
'11' + undefined => '11undefined'
...... 

2)一元运算符 –/+

对于非number的值,先转换为number再运算;可以对一个其它数据类型使用+,将其转换为number,原理和Number()函数一样;

result = 1 + +"2" + 3;
console.log(result); //6

a++的值是原变量的值(自增前的值);++a的值是自增后的值; 

3)与运算

 两个值都是true返回后面的

var result = 5 && 6;  //6

两个值中有false,则返回靠前的false 

var result = NaN && 0;  //NaN

4)或运算 

 如果第一个值为true则直接返回第一个值;如果第一个值是false则直接返回第二个值。

console.log(10 <= "hello"); //false

5)比较运算符

比较规则:

1)对于非数值进行比较时会转换成数值进行比较

 2)任何值对NaN做任何比较结果都为false

 3)如果符号两边的值都是字符串,不会将其转换为数字进行比较,而是将其转换为Unicode编码;比较字符编码的时候一位一位比较,可能会得到不可预期的结果,再比较两个字符串型的数字时一定要转型。

在字符串中使用转义字符输入Unicode编码:\u四位编码 十六进制

console.log("\u2620");

708597bcf7a0441aa172c8b640c85c39.png         

如何在网页中输出Unicode编码?

&#编码,这里的编码需要的是十进制

使用windows自带的计算器进行转换,选择程序员模式,十六转十进制

7a03a5b6424a4441b630739236b79dd7.png

6)==、!=、===、!==运算符

使用 == ,!=时会自动进行类型转换,先转换为相同的类型,然后再比较;

underfined衍生自null所以这两个数值进行判断时会返回true;

console.log(undefined == null);  //true

 NaN不和任意值相等包括它本身;

console.log(NaN == NaN); //false

===用来判断两个值是否全等,和相等类似但如果两个值得类型不同则直接返回false;

console.log(undefined === null);  //false

!==判断两个值是否不全等

7)三目运算符

获取a和b中的最大值

var max = a > b ? a : b;

8)转义字符

在字符串中可以使用\作为转义字符,当表示一些特殊符号的时候,可以使用转义字符进行转义。

\’表示’     \”表示“     \n表示换行    \t表示    \制表符(tab键)

三.对象

1.对象的分类

1)内建对象 ES标准中定义的对象 Math String Number Boolean

2)宿主对象 由JS运行环境提供的对象 DOM BOM

3)用户自定义对象

2.创建对象

对象就像一个塑料袋,存放不同的属性。

1)使用new关键字调用的函数是构造函数constructor,构造函数是专门用来创建对象的函数。

2)对象字面量来创建对象

var obj={属性名:属性值,属性名:属性值,......};

任何一个对象都是由0或多个键值对组成的(属性名:属性值),并且属性名不能重复,在创建对象时,直接指定对象中的属性;属性名可以加引号也可以不加。

3.对象属性的增删改

  • 1)在对象中保存的值称为属性,设置对象的属性值

    对象.属性名=属性值

var obj= new object();
obj.name=”xxx”;
  • 2)读取对象中的属性

    对象.属性名

如果读取的是对象中没有的属性会返回undefined

修改对象的属性值和添加的语法是一样的

  • 3)删除对象的属性

    真删除:delete 对象名.属性名  把属性彻底删除掉

    假删除:对象名.属性名 = null  属性还在值为空


注意:

  • 1.如果使用特殊的属性名需要使用 对象名[“属性名”]=属性值 的方式,而不是使用.的方式 ,属性名是数字或者字符串格式; 
obj["123"]=789;
console.log(obj["123"]);

使用[]的方式操作属性会更加的灵活,在[ ]中可以直接在传递一个变量,这个变量是多少就会读取这个属性

obj["123"]=789;
Var n = "123";
console.log(obj[n]);   //789

2.JS中对象的属性值是任意数据类型,可以是对象;

  • 3.in运算符。通过它可以检查一个对象中是否有指定的属性 true false,"属性名" in 对象;
  • 4.属性名是数字,不能使用.方式访问属性; 

四.内建对象-数组

数组也是一个对象。不同的是普通对象是通过字符串作为属性名操作对象,数组是使用数字作为索引操作元素的,在中括号中直接设置属性值,属性名是默认生成的数字,从零开始递增,而且这个数字代表每一项的位置,称为索引

数组默认有一个属性length,存储数组的长度。

1.创建数组

var arr=new Array();  //使用构造函数也可以在参数中添加元素
arr[arr.length]=xxx  //向数组最后添加元素

使用字面量来创建数组

var arr=[1,2,3,4];
arr1=[10]; //数组中只有一个元素
arr2= new Array(10); //创建一个长度为10的数组

数组的第一项索引是0,最后一项索引是arr.length - 1;

let arr = [1,2,3,4,5];
console,log(arr[0]);  // 1
console.log(arr[arr.length - 1]); // 5

向数组末尾追加内容;

let arr = [1,2,3,4,5];
arr[arr.length] = 90;

数组中的元素可以是任意数据类型,可以是对象或函数,也可以放数组

arr2=[function(){alert(1)},function(){alert(0)}];
arr[0]();  //调用

2.数组方法

1)push() 

向末尾添加一个或多个,返回值为新数组长度

2)pop()

删除数组最后一个元素并返回删除的元素

3)unshift()

向数组头部插入元素,返回新的长度

4)  shift()

数组前面删除一个元素并返回,返回删除的元素

var arr=[1,2,3,4];
arr.push(5,6);   //1,2,3,4,5,6
arr.pop(); //1,2,3,4,5
arr.unshift(0); //0,1,2,3,4,5
arr.shift(); //1,2,3,4,5

5)forEach()

用来遍历数组,IE8以及一下的浏览器不支持。需要以函数作为参数

var arr=[per,per1,per2,per3];
arr.forEach(function(){      //有我们创建不由我们调用的函数称为回调函数
    console.log("hello");   //hello hello hello hello 
});                

数组中有几个元素就会执行几次,浏览器会将遍历到的元素以实参的形式传递进来,我们可以定义形参来读取内容;

浏览器会在回调函数中传递三个参数。第一个参数就是当前遍历的元素;第二个参数是当前正在遍历元素的索引;第三个参数就是我们正在遍历的数组对象。

6)slice()

可以从数组中提取指定的元素,该方法不会改变原数组;

参数:截取开始的位置索引,截取结束的位置索引,如果传递一个负值则从后往前计数;

var arr=[1,2,3,4];
arr.slice(0,2) //1,2;  包含开始不包含结束
arr.slice(1); //2,3,4,5 

// 数组克隆(属于浅克隆)
let cloneArr = arr.slice(0);

7)splice()

删除数组中指定元素,使用会影响到原数组,并将被删除的元素作为返回值返回(返回数组);

参数:开始的位置的索引,删除的数量,第三及以后可以传递一些新元素,插入到开始位置索引前

var arr=[1,2,3,4];
arr.splice(1,2); //2,3

还可以插入替换元素,添加元素

var arr=[1,2,3,4];
arr.splice(1,1,9); // 2  [1,9,3,4]  元素替换

arr.splice(1,0,12)  // []  [1,12,9,3,4] 元素插入

8)concat()

链接两个多个数组,并返回新数组,不会对原数组产生影响,不仅可以传数组,还可以传元素

9)join()

将数组转换为字符串,该方法不会对原数组产生影响,可以在括号中指定一个字符串,作为元素的链接符,如果不指定则用逗号链接

10)reverse()

该方法用来反转数组,该方法会直接修改原数组

var arr=[1,2,3,4];
arr.reverse();//5,4,3,2,1

11)sort()

可以用来对数组的元素进行排序,也会影响原数组,按Unicode编码排序,可能会对数字排序时产生错误影响;

我们可以在sort中添加一个回调函数,自己指定排序的规则;

回调函数中我们需要定义两个形参,浏览器会分别使用数组中的元素作为实参去调用,浏览器会根据回调函数的返回值来决定元素的顺序,返回一个大于0的值,则元素会交换位置;如果返回一个小于0的值则元素位置不变;返回一个0,则认为两个元素相等不交换位置

arr=[4,5,2,1,3,6,8,7];
arr.sort(function(a,b){
    if(a>b){
        return 1;
    }else if(a<b){
        return -1
    }else{
        return 0;
    }
});    //1,2,3,4,5,6,7

arr.sort(function(a,b){
	return a-b;
}  //升序排列a-b,降序排列b-a

12)map()

会返回一个数组,该数组会和原数组存在一定的映射关系;

var arr = [1,2,3,4];
var arr1 = arr.map(function(item,index,arr){return item * 2});
console.log(arr1);  // [2, 4, 6, 8]

该方法还可以存在第二个参数,该参数就是这个方法内部的this项;

arr = [1,2,3,4];
var arr1 = [1,2].map(function(item,index,arr){
    return this[item]
},arr);
console.log(arr2); [2,3]
// this代表arr这个数组,它是第二个参数,返回的就是以[1,2]这个数组为索引下标相对应的arr数组元素
// 即arr[1]和arr[2]

使用map将数组转数组

var num = 12345;
var covertArray = (num) => {
    return num.toString().split('').map((item) => {return parseInt(item)}
)};
console.log(covertArray(num));  // [1, 2, 3, 4, 5]

13)find()

返回第一个符合条件的成员

var arr = [1,2,3,4,5,6,7];
console.log(arr.find(function(n){
    return n > 4
})); // 5

14)findIndex()

返回第一个符合条件成员的索引

var arr = [1,2,3,4,5,6,7];
console.log(arr.findIndex(function(n){
    return n > 4
})); // 4

15)includes()

判断当前数组是否包含某个元素,返回布尔值;

var arr = [1,2,3,4,5,6,7];
console.log(arr.includes(4))

16)indexOf() 和 lastIndexOf()

indexOf是从左到右开始查找,找到返回该数组元素在数组中的索引位置,若未找到返回-1;

lastIndexOf是从右往左查找,其余和indexOf一致;

var arr = [1,2,3,4,5,6,7];
console.log(arr.indexOf(1)); // 0
console.log(arr.indexOf(11)); // -1

var arr1 = [1,2,5,6,5];
console.log(arr.lastIndexOf(5));  // 4

17)filter() 

filter是对数组元素进行过滤操作,需要给参数回调函数一个返回值,该值为布尔值,有这个布尔值决定当前的这个数组元素是否存在,是否需要过滤掉

var arr = [1,2,3,undefined,4,null,5,-1,0,false,NaN];
var arr1 = arr.filter(function(item){return item != undefined});
console.log(arr1); // [1, 2, 3, 4, 5, -1, 0, false, NaN]

18)some()/every()

这两个方法都会返回布尔值,参数回调函数需要返回一个布尔值作为判断条件,依次遍历数组的每一个元素。对于some方法,只要数组中有一个元素满足判断条件,some方法就会返回true;对于every方法,必须数组中的所有元素满足判断条件,every方法才会返回true。

var arr = [1,2,3,4,5];
var arr1 = arr.some(function(item){return item % 2 === 0});
var arr2 = arr.every(function(item){return item % 2 === 0});
console.log(arr1);  // true
console.log(arr2);  // false

19)reduce()

 reduce方法作为一个归纳函数,是一个累加器,能够通过累加的方式返回一个具体的值;

该函数可以接收两个参数,第一个是一个回调函数,第二个是累加的初始值,对于第一个回调函数,可以指定四个形式参数,第一个可以获取当前循环累加的最新结果,第二个参数是当前遍历的数组元素,第三个参数是当前遍历元素的索引,第四个是当前遍历的数组。

var initalValue = 0;
var arr = [{x:1},{x:2},{x:3}];
var sum = arr.reduce(function(accumulator,currentValue,index,array){
    return accumulator + currentValue.x
},initalValue);
console.log(sum); // 6

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546W5Ly0Xw==,size_20,color_FFFFFF,t_70,g_se,x_16

将二维数组转化为一维数组

var flattened = [[0,1],[1,2],[2,3]].reduce(function(a,b){
    return a.concat(b)
},[]);
onsole.log(flattened);  // [0, 1, 1, 2, 2, 3]

五.Date对象

在JS中用Date对象来表示一个时间

如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间;

var d =new Date(); // Tue Feb 22 2022 09:45:27 GMT+0800 (中国标准时间)

日期的格式:月份/日/年 时:分:秒

getDate(); 获取当前对象是几号

getDay();获取当前对象是周几 0-6

getMonth():获取当前时间对象的月份 0-11

getFullYear():获取当前对象的年份

getHours():获取当前小时,返回值是0-23 之间的整数

getTime():获取当前对象的时间戳,时间戳指的是格林威治时间的1970.1.1,0时0分0秒到当前时间所花费的毫秒数(1秒=1000毫秒),计算机底层保存时间时都时使用的是时间戳

Date.now():获取当前的时间戳

可以利用时间戳来测试代码执行的性能: 

var start=Date.now();
for(var i=0;i<100;i++){
	console.log();}
var end=Date.now();
console.log(end-start);

六.Math对象

Math和其它对象不同,它不是一个构造函数,它属于一个工具类(简单说就是对象)不用创建对象,里面封装了数学运算相关的属性和方法;

console.log(Math.PI); // 表示圆周率
console.log(Math.ceil(1.1));// 2 对数进行向上取整 round:四舍五入
console.log(Math.floor(1.1));  // 1  向下取整
console.log(Math.round(-12.5)); //  -13 四舍五入,没有大小之分 
Math.round(Math.random() * x)// 生成一个[0-x)之间的随机数
Math.round(Math.random() * (y - x) + x) // 生成一个x-y之间的随机数,小数点后17,18...位
Math.abs([number value]); // 获取一个数的绝对值  传递的不是数字类型的值,先基于Number转换为数字在处理

Math.max/min([val1],[val2],...):获取一堆数中的最大值或最小值

Math.max(3,80,7,-6,56,42,100,79); // 100

 Math.sqrt/pow([val1]):给一个数开平方/计算一个数的多少次幂

负数不能开平方,结果为NaN

Math.pow(2,10); // 1024

七.包装类

JS中为我们提供了三个包装类,通过三个包装类可以将基本数据类型的数据转换为对象

String():可以将基本数据类型字符串转换为String对象

Number();可以将基本数据类型的数字转换为Number对象

Boolean();可以将基本数据类型的布尔值转换为Boolean对象

var num=new Number(3);
console.log(typeof num);  //object

但实际运用中不会使用基本数据类型的对象,在做一些比较时会带来一些不可预测的结果

方法和属性只能添加给对象,不能传递给基本数据类型;当我们对一些基本数据类型的值去调用属性和方法时,浏览器会使用包装类将其转换成对象,在调用对象的属性和方法,调用完后再转换回来。

var s = '123';
// 浏览器隐式调用了new String(s).length
console.log(s.length);

s.length = 12;
// new String(s).length = 12
// delete  new String(s).length
console.log(s.length);  // 3
// new String(s).length

八.字符串

在底层字符串是以字符数组的形式保存;

1.字符串方法

1)charAt()

可以返回字符串指定位置的字符,和[]用途一样

2)charCodeAt()

获取指定位置字符的Unicode编码

3)concat()

连接两个或多个字符串

var str="123";
var result=str.concat("你好");  // 123你好

4) indexOf(),lastIndexOf()

检索一个字符串是否有指定内容,如果字符串中有该内容会返回第一次出现的位置索引,否则返回-1;可以指定第二个参数,规定查找的位置

var str="123";
var result=str.indexOf("1");  //0

5)slice()

可以从字符串中截取指定的位置 ,不会影响原字符串;

参数:开始位置索引,结束位置索引(不包括)

6)substring()

可以用来截取一个字符串与上面类似,不同的是不能接受负值,负值相当于0,而且会自动调整参数的位置,如果第二个参数小于第一个会自动交换;

7)substr()

用来截取字符串

参数:截取开始位置的索引,截取的长度

8)split()

可以将一个字符串拆分为一个数组,需要一个字符串作为参数,按这个字符串拆分这个数组,如果传递一个空串则将每个字符拆分为一个数组元素

var str="abc,def,ghi";
var result=str.spilt(","); //一个有三个元素的数组[abc,def,ghi]

9)toUpperCase(),toLowerCase()

toUpperCase将一个字符串转换大写并返回

var str="abcdefg";
var result=str.toUpperCase(): //ABCDEFG

toLowerCase将一个字符串转换小写并返回

九.正则表达式

用于定义一些字符串的规则,计算机根据正则表达式判断字符串是否符合规则,创建正则表达式对象;

1.正则的创建

1)构造函数创建

语法: var 变量=new RegExp(“正则表达式”,”匹配模式”);

在构造函数中可以传递一个匹配模式作为第二个参数;

常用修饰符可以是:i忽略大小写;g全局匹配模式;m可以进行多行匹配

 var reg=new RegExp("a","i");  // /a/

2)通过字面量创建正则表达式

语法:var 变量=/正则表达式/匹配模式

var reg=/a/i;

两种方式创建的区别

1.构造函数创建的是字符串,\需要写成两个斜杠

let reg = new RegExp('\\d+','g'),
    reg = /\d+/g;

 2.正则表达式中的部分内容是变量存储的值,此时只能使用构造函数法创建正则

let type = 'boolean';
let reg = new RegExp('^@' + type + '@$','g');
console.log(reg.test('@boolean@'));  // true

使用|表示或者

var reg=/a|b|c/;  //检查一个正则表达式中是否有a或b

直接使用x|y会存在很乱的优先级问题,一般我们写的时候都伴随着小括号进行分组,因为小括号改变处理的优先级,例如:

let reg = /^18|29$/;
console.log(reg.test("18"));  // true
console.log(reg.test("29"));  // true
console.log(reg.test("189")); // true
console.log(reg.test("829")); // true

let reg = /^(18|29)$/;
console.log(reg.test("18")); // true
console.log(reg.test("29")); // true
console.log(reg.test("189")); // false
console.log(reg.test("829")); // false

 ^表示开头

       reg=/^a/; //检查一个字符串是否以a开头

$表示结尾

       reg=/a$/;//是否以a结尾

如果在一个正则表达式中以^开头以$结尾则要求字符串要完全符合正则表达式 

2.字符串和正则相关的方法

1)split()

可以将一个字符串差分为一个数组,可以在方法中传递一个正则表达式作为参数;

var str="1a2b3c4d5e";
var result=str.split(/[A-z]/);

2)secrch()

可以搜索字符串中是否有指定内容;如果搜索到指定内容,则返回第一次出现的索引,即使是全局匹配也没用,否则返回-1;

var str="abc aec adc";
var result=str.search(/a[bed]c/);  // 0

3)match()

可以根据正则表达式,从一个字符串中将符合条件的内容提取出来,默认情况下只会找第一个符合要求的内容;会将匹配到的内容封装在一个数组中返回;

var str="1A2b3c4d5e";
var result=str.match(/[a-z]/gi); // 全局匹配,忽略大小写 A,b,c,d,e
console.log(result); // ['A', 'b', 'c', 'd', 'e']

多次匹配的时候,match只能将大正则的内容获取到,小分组的信息无法获取到;而exec可以获取小分组但是,只能获取第一次匹配的结果。如果需要同时考虑两者,需要自己实现:

let str = "{1}-{2}-{3}";
let reg4 = /\{(\d+)\}/g;
let arrBig = [],
    arrSmall = [],
    res = reg4.exec(str);  //  ['{1}', '1', index: 0, input: '{1}-{2}-{3}', groups: undefined]

while(res) {
    let [big,small] = res;
    arrBig.push(big);
    arrSmall.push(small);
    res = reg4.exec(str);
}

console.log(arrBig,arrSmall); //  ['{1}', '{2}', '{3}']  ['1', '2', '3']

4)replace()

将字符串中的内容替换成新的内容,默认只替换第一个,不会修改原串

参数:被替换的内容,新的内容;新的内容为空串则表示删除原内容

result=str.replace(/[a-z]/gi,"@_@");

示例2:修改日期格式,将xxxx-xx-xx 修改为 xxxx年xx月xx日

let time = '2022-5-5';
let reg9 = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;

time = time.replace(reg9,"$1年$2月$3日");
console.log(time);  // 2022年5月5日

$1代表第一个分组信息,以此类推;

还可以这样处理:[str].replace([reg],[function])

1.首先拿reg和time进行匹配捕获,能匹配到几次就把传递的函数执行几次,匹配一次就执行一次(这里就匹配了一次)

2.不仅把方法执行,而且replace还给方法传递了实参信息,和exec捕获的内容一致的信息:大正则匹配的内容,小分组匹配的信息......

3.在函数中返回的是啥就把当前大正则匹配的内容替换成啥。

time = time.replace(reg9,(...arg) => {
    let [,$1,$2,$3] = arg;
    $2.length < 2 ? $2 = '0' + $2 : null;
    $3.length < 2 ? $3 = '0' + $3 : null;
    return $1 + '年' + $2 + '月' + $3 + '日';
})

console.log(time);  // 2022年05月05日

示例3:将一句话的每一个单词首字母大写 

let str3 = 'good good study,day day up';
let reg10 = /\b([a-zA-Z])[a-zA-Z]*\b/g;
str3 = str3.replace(reg10,(...arg) => {
    let [content,$1] = arg;
    $1 = $1.toUpperCase();
    content = content.substring(1);
    return $1 + content;
});

console.log(str3); // Good Good Study,Day Day Up

3.正则对象原型上的方法 

1)test() 

使用这个方法测试一个字符串是否符合一个正则表达式的规则,返回布尔值

var reg=new RegExp("a","i");
var str="Ac";
reg.test(str); //true

进行正则捕获

let str2 = "{1}-{2}-{3}";
let reg8 = /\{(\d+)\}/g;

console.log(reg8.test(str2)); // true
console.log(RegExp.$1);  // 1
console.log(reg8.test(str2)); // true
console.log(RegExp.$1); // 2
console.log(reg8.test(str2)); // true
console.log(RegExp.$1);  // 3
console.log(reg8.test(str2)); // false
console.log(RegExp.$1);  // 3

RegExp.$1 - RegExp.$9:获取当前本次正则匹配后,第一个到第九个分组信息,全局只能存在一个,不常使用。

2)exec() 

捕获得到的结果是null或者一个数组;

第一项:本次捕获到的内容;

其余项:对应小分组对应捕获到的内容;

index:当前捕获内容在字符串中的起始索引;

input:原始字符串

每次执行exec,只能捕获到一个符合正则规则的,但是默认情况下我们无论执行几遍,获取的结果永远都是第一个匹配到的,其余的捕获不到;

例如:

let reg1 = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
console.log('reg1',reg1.exec('310103199805255018'));

里面有六个分组,使用exec会捕获到这六个分组的信息,可以使用?:不捕获某一个分组的信息

f0c505e3848347128a6613013b7e00ca.png

正则捕获的懒惰性:永远捕获第一个 

4.常用元字符

1)量词元字符

可以通过量词设置一个内容出现的次数,只对它前面一个内容起作用

{n}:正好出现n次;{m,n}:出现m-n次;{m,}:出现m次以上;

 

+:至少一个,相当于{1,};

*:0个或多个,相当于{0,};

?:0个或一个,相当于{0,1}

2)特殊元字符

在正则表达式中使用\作为转义字符,\.表示.  \\d 表示字符串 "\d"

在使用构造函数时,由于它的参数是一个字符串,而\时字符串中的转义字符,所以要用\\来代替

\w:代表任意字母数字下划线 [A-z0-9_]

\W:除了字母数字下划线 [^A-z,0-9_]

\d:任意的数字 [0-9]

\D:除了数字 [^0-9]

\s:表示空格

\S:除了空格

\b:单词边界

\B:除了单词边界

var reg=/\bchild\b/; 查找单词child

 取出字符串中的空格

var str= "   he llo   ";
str=str.replace(/\s/g,""); // hello  
str=str.replace(/^\s*|\s*$/g,""); // hello  匹配开头或结尾的空格并删除

. 字符:表示除了\n以外的所有字符 例如:.+ 表示1-n个除了换行的任意字符

[]里的内容也是或关系 [ab]=a|b  a或者b中的一个字符

[a-z]:任意小写字母;

[A-Z]:任意大写字母;

[A-z]:任意字母;

[0-9]:任意数字;

[^]:除了,表示取反

():分组符号

(?:):只匹配不捕获

(?=):正向预查

(?!):负向预查

[]中出现的字符一般都代表其本身含义,例如[@+],代表@和+,这里的+不代表一次或多次;

[]中不存在多位数,例如[10-29]代表匹配1,0-2,9

?的五大作用:

  • 问号左边是非量词元字符,本身代表量词元字符,出现零到一次
  • 问好左边是量词元字符,取消捕获时的贪婪性
  • (?:) 只匹配不捕获
  • (?=) 正向预查
  • (?!) 负向预查

3)普通元字符 

代表本身含义的元字符

/shanghai/:此正则匹配的就是"shanghai"

5.正则捕获的懒惰性 

正则捕获的懒惰性:永远捕获第一个

在正则实例中存在lastIndex的属性,它记录着当前正则下一次匹配的其实索引位置

let reg2 = /(18|29)/;
console.log(reg2.lastIndex); // 0
console.log(reg2.exec('abc18kkhjk29hkk18u2918'));
console.log(reg2.lastIndex); // 0

懒惰性捕获的原因:默认情况下lastIndex的值不会被修改,每一次都是从字符串开始的位置查找,所以找到的永远只有第一个;

解决方法:设置全局修饰符g,第一次匹配完,lastIndex的值自动修改,当全部捕获再次捕获的结果为null,但是lastIndex的值又回归初始值0,再次捕获又从第一个开始了......

let reg2 = /(18|29)/g;
console.log(reg2.lastIndex); // 0
console.log(reg2.exec('abc18kkhjk29hkku2918')); // Array(2) 0: "18" 1: "18"
console.log(reg2.lastIndex); // 5
console.log(reg2.exec('abc18kkhjk29hkku2918')); // Array(2) 0: "29" 1: "29"
console.log(reg2.lastIndex); // 12
console.log(reg2.exec('abc18kkhjk29hkku2918')); // Array(2) 0: "29" 1: "29"
console.log(reg2.lastIndex); // 18
console.log(reg2.exec('abc18kkhjk29hkku2918')); // Array(2) 0: "18" 1: "18"
console.log(reg2.lastIndex); // 20
console.log(reg2.exec('abc18kkhjk29hkku2918')); // null
console.log(reg2.lastIndex); // 0
console.log(reg2.exec('abc18kkhjk29hkku2918')); // Array(2) 0: "18" 1: "18"
console.log(reg2.lastIndex); // 5

需求:编写一个方法execAll,执行一次可以把所有匹配的结果捕获到,前提正则一定要修饰全局修饰符

;(function () {
    // str:需要匹配的字符串
    // this:当前RegExp实例
    function execAll (str='') {
        let arr = []; // 存放结果的数
        // 验证正则是否添加全局匹配模式,不然始终匹配的都是第一个,会导致死循环
        if(!this.global)  return this.exec(str);
        
        let res = this.exec(str); 
        console.log(res[0])

        while(res) {  // 只要捕获的结果不为null,则继续捕获下去
            arr.push(res[0]);  // 每一次将res[0]添加到结果数组中
            res = this.exec(str);
        }

        return arr.length === 0 ? null : arr;
    }

    RegExp.prototype.execAll = execAll;
})()

let reg3 = /(18|29)/g;
console.log(reg3.execAll('abc18kkhjk29hkku2918'));

当然,我们可以使用字符串提供的match方法,在执行一次的情况下捕获到所有匹配的数据,前提是,正则必须设置g; 

6.正则的分组

作用:

1)改变默认优先级,和()运算符的作用一致;

2)分组捕获,一般与exec方法结合使用;

3)分组引用:通过"\数字"让其代表和对应分组一样的内容;

实例:要求匹配一个四个字母的单词,其第二位和第三位字母相同;

let reg5 = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/;
console.log(reg5.test('foot')); // true
console.log(reg5.test('feet')); // true
console.log(reg5.test('wait')); // false

7.正则捕获的贪婪性 

默认情况下正则捕获的时候,默认按照当前捕获的最长结果来获取

取消贪婪性,在量词元字符后面设置?,取消捕获时的贪婪性(按照正则匹配规则的最短结果来获取)

let str1 = '你好2022,再见2021';
let reg6 = /\d+/g;
console.log(str1.match(reg6));  // 0: "2022" 1: "2021"
let reg7 = /\d+?/g;
console.log(str1.match(reg7)); // 0: "2" 1: "0" 2: "2" 3: "2" 4: "2" 5: "0" 6: "2" 7: "1"

8.正则表达式的断言

作用:给指定的位置添加一个限制条件,用来规定此位置之前或者之后的字符必须满足限定条件才能使用正则中的表达式匹配成功;

/ab(?=[A-Z])/:匹配后面跟的是任意大写字母的字符串‘ab’

/ab(?!(A-Z))/:匹配后面不跟任意一个大写字母的字符串‘ab’

密码验证

密码6-12位,必须同时包括数组字母

/^[0-9A-Za-z]{6,12}$/:代表6-12位的密码,由数字大小写字母组成,但可以包含纯数字纯字母的情况,所以要排除这两类,需要使用断言^(?![0-9]+$)(?![A-Za-z]+$)除去纯数字和纯字母

let regPwd = /^(?![0-9]+$)(?![A-Za-z]+$)[0-9A-Za-z]{6,12}$/;

千分符

例子:将123456789.12 转换成 1,234,567,899.12

首先匹配1 4 7 :   /\d(?=(\d{3})+($|.))/

​    +代表多组数字,不加只能匹配到一组,即7

随后将1 4 7 替换为 1, 4, 7,

let str = 123456789.12;
// $1,$2上就是按顺序对应小括号里面的小正则 捕获到的内容  这里 $1 匹配的是(\d)
str.replace(/(\d)(?=(\d{3})+($|.))/,"$1,")

9.正则表达式中的.*,.*?,.+?

1) .*

`.` 表示匹配除换行符 \n 之外的任何单字符,`*`表示零次或多次。所以`.*`在一起就表示任意字符出现零次或多次。没有`?`表示贪婪模式。比如`a.*b`,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索`aabab`的话,它会匹配整个字符串`aabab`。这被称为贪婪匹配

2).*?

`?`跟在*或者+后边用时,表示懒惰模式。也称非贪婪模式。就是匹配尽可能少的字符。就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。
`a.*?b`匹配最短的,以a开始,以b结束的字符串。如果把它应用于`aabab`的话,它会匹配`aab`(第一到第三个字符)和`ab`(第四到第五个字符)。

3).+?

`?`跟在*或者+后边用时,表示懒惰模式。也称非贪婪模式。就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。
`a.+?b`匹配最短的,以a开始,以b结束的字符串,但a和b中间至少要有一个字符。如果把它应用于`ababccaab`的话,它会匹配`abab`(第一到第四个字符)和`aab`(第七到第九个字符)。注意此时匹配结果不是`ab`,`ab`和`aab`。因为a和b中间至少要有一个字符。

10.常用的正则表达式

1)验证是否为有效数字

规则:

1.可能出现 + - 号,可能不出现; [+-]?

2.一位0-9,多位首位不能为0; (\d|([1-9]\d+))

3.小数部分可以有可以没有,一旦有后面必须有小数点 + 数字  (\.\d+)?

let isValidNum = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;

2)验证密码

包含数字字母下划线

let isVaildPwd = /^\w{6,16}$/;

3)验证真实姓名

规则:

是汉字  /^[\u4E00-\u9FA5]$/

长度2-10位 

可能有译名 · 汉字 

let isVaildChinese = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10})?$/;

4)验证邮箱 

规则:

1.开头是数字字母下划线,1到多位

2.还可以是- 数字字母下划线 或者 .数字字母下划线 整体零到多次 例如 shang-hai@xx.com

\w+((-\w+)|(\.\w+))*

3.@后面紧跟数字,字母1到多位  @[a-zA-Z0-9]+

4.对@后名字的补充 可能是多域名(@qq.com.cn) 或者企业域名(@xxxx-xxx-xx.com)

((-|\.)[0-9a-zA-Z]+)*

5.匹配的是最后的域名(.com/.cn/.org/.edu/.net...) \.[0-9a-zA-Z]+

let isValidEmail = /^\w+((-\w+)|(\.\w+))*@[a-zA-Z0-9]+((-|\.)[0-9a-zA-Z]+)*\.[0-9a-zA-Z]+$/;

5)身份证号码 

规则

1.一共18位

2.最后一位可能是大写的X(代表数字10)

前六位 - 省市县

中间八位 - 年月日

最后四位 - 最后一位是X或者数字;倒数第二位偶数女奇数男;其余是经过算法算出来的

使用小括号分组进行正则捕获,将每个省份证对应的以上信息使用exec方法捕获出来

let reg1 = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;

 通过以上的分组,可以快速获取一个身份证持有者的生日年龄等信息

6)使用正则统计字符串中出现最多的字符

方法1:

let str3 = 'shdmahsddmbdaadagsansagsna';
str3 = str3.split('').sort((a,b) => a.localeCompare(b)).join('');  // 按照字符升序排列 aaaaaaabdddddgghhmmnnsssss
let reg11 = /([a-zA-Z])\1+/g;
let arr = str3.match(reg11).sort((a,b) => b.length - a.length);  
// 0: "aaaaaaa" 1: "ddddd" 2: "sssss" 3: "gg" 4: "hh" 5: "mm" 6: "nn"

let max = arr[0].length,
    res1 = [arr[0].substr(0,1)];
for (let i = 1; i < arr.length; i++) {
    let item = arr[i];
    if(item.length < max) {
        break;
    }
    res1.push(item.substr(0,1));
}
console.log(`出现最多的字符${res1},出现了${max}次`);

方法2:

let str3 = 'shdmahsddmbdaadagsansagsna',
    max = 0,
    res1 = [],
    flag = false;
str3 = str3.split('').sort((a,b) => a.localeCompare(b)).join('');  // 按照字符升序排列 aaaaaaabdddddgghhmmnnsssss

for (let i = str3.length; i > 0; i--){
    let reg = new RegExp('([a-zA-Z])\\1{' + (i - 1) + '}','g');
    str3.replace(reg,(content,$1) => {
        res1.push($1);
        max = i;
        flag = true;
    })
    
    if(flag) break;
    
}
console.log(`出现最多的字符${res1},出现了${max}次`);

7)时间日期格式化

获取来自服务器的日期2022-5-6 17:17:19 或 2022/5/6 17:17:19,转化为2022年5月6日 17时17分19秒等自定义格式;

使用正则的方式可以大幅减少代码量,首先使用match方法匹配出所有的日期数字,再使用字符串的replace方法将数字替换到指定的模板指定位置中

;(function () {
    function formatTime (template = '{0}年{1}月{2}日 {3}时{4}分{5}秒') {
        // 获取时间字符串中年月日等信息
        let timeArr = this.match(/\d+/g); // ['2022', '5', '6', '16', '42', '23']
        return template.replace(/\{(\d+)\}/g,(content,$1) => {
            // {0} 0 {1} 1 {2} 2 {3} 3 {4} 4 {5} 5
            let time = timeArr[$1] || "00";
            return time.length < 2 ? '0' + time : time;
        });

    }   

    // 扩展到内置类String.prototype上,此用法适用于需要扩展多个方法的场合
    ["formatTime"].forEach(item => {
        String.prototype[item] = eval(item);  // eval将formatTime字符串转换为表达式
    })
})()

let time = '2022-5-6 16:42:23';
console.log(time.formatTime()); // 2022年05月06日 16时42分23秒

8) 获取url的参数

输入一个url可以获取该url的查询字符串参数和hash;

query参数的key和value不包括?=&#,书写正则时将其排除,使用replace获取指定分组的值,添加该结果对象中

;(function () {
    
    // 获取url地址后面的参数信息
    function queryURLParams() {
        let obj = {};
        this.replace(/([^?=&#]+)=([^?=&#]+)/g,(...[,$1,$2]) => obj[$1] = $2);
        this.replace(/#([^#?&=]+)/g,(...[,$1]) => obj['HASH'] = $1);
        return obj;
    }

    // 扩展到内置类String.prototype上,此用法适用于需要扩展多个方法的场合
    ["queryURLParams"].forEach(item => {
        String.prototype[item] = eval(item);  // eval将formatTime字符串转换为表达式
    })
})()


let url = 'https://www.bilibili.com/video/BV1o64y1h7po?spm_id_from=333.1073.sub_channel.dynamic_video.click#video';
console.log(url.queryURLParams());  // HASH: "video" spm_id_from: "333.1073.sub_channel.dynamic_video.click

9)千分符

 使用非正则的方式

let num = '123456789';
num.split('').reverse().join('');
for (let i = 2; i < num.length - 1; i+=4) {
    let prev = num.substring(0,i + 1);
    let next = num.substring(i + 1);
    num = prev + ',' + next;
}
num.split('').reverse().join('');
console.log(num); // 123,456,789

 使用正则正向预查 ?=

;(function () {

    // 实现大数字的千分符处理
    function millimeter () {
        return this.replace(/\d{1,3}(?=(\d{3})+$)/g,content => content + ',');
    }

    // 扩展到内置类String.prototype上,此用法适用于需要扩展多个方法的场合
    ["millimeter"].forEach(item => {
        String.prototype[item] = eval(item);  // eval将formatTime字符串转换为表达式
    })
})()

let num1 = '123456789';
console.log(num1.millimeter());  // 123,456,789

十.js中的操作语句

操作语句包括判断、循环

1.判断 if

常用的判断语句:

- if/else if/else

- 三元运算符

- switch case

1)if/else

语法规则:下列语法是可以拆分组合的(只写if语句,或只写if-else,或只写if-else if)

if(条件) {

        条件成立执行

} else if (条件2) {

        条件2成立执行

......

else {

        以上条件都不成立执行

}

if语句条件可以多样性:大于、小于、等于的比较/一个值/取反,最后都是要计算出TRUE还是FALSE;

2) 三元运算符

简单if-else的特殊处理

条件 ? 条件成立处理的事情 :不成立处理的事情

let a = 10;
if(a >= 10) {
    console.log('aa');
} else {
    console.log('bb');
}

// 可以使用三元运算符代替上述写法
a >= 10 ? console.log('aa') : console.log('bb')

注意:

1.如果处理的事情比较多,我们用括号括起来,每一件事情用逗号分隔;

2.如果不需要处理事情,可以使用null/undefined占位;

a > 0 && a < 20 ? (a++,console.log(a)) : null;

3)switch...case 

用于一个变量在不同情况下的不同操作

示例:

let a = 5;
switch(a) {
    case 1:
        console.log('aa');
        break;

    case 2:
        console.log('bb');
        break;

    default:
        console.log('cc');  // cc
}

注意:

1.每一种CASE情况结束后最好都加上BREAK,不加BREAK,当前条件成立执行完成后,后面条件无论成立与否都会执行,知道遇到BREAK为止;

2.当一个变量在几种值的情况下要做同一件事情,可以不加BREAK;

let a = 5;
// 当a的值为1或5的时候,都要执行加2操作,可以这样写
switch(a) {
    case 1:
    case 5:
        a += 2;
        break;
    default:
        a--;
}

3.default等价于else,所有条件都不成立干的事情;

4.每一种CASE情况的比较都是用 === (绝对相等)

十.DOM文档对象模型 

作用:可以通过JS操作网页

DOM全程document object model,浏览器已经为我们提供了文档节点,这个对象是window对象的属性,可以在页面中直接调用,文档节点代表的是整个网页,document就是文档节点;

1. 获取页面dom元素

1)获取id选择器

document.getElementById("id名"),其上下文只能是document。

<botton id="btn">我是一个按钮</botton>
......

var btn=document.getElementById("btn"); //获取id为btn的元素:【object HTMLButtonElenent】
//修改按钮的文字
btn.innerHTML="I’m Button";

innerHTML和innerText 的区别:前者识别html标签,保留空格和换行;后者不识别,不保留。这两者是可读写的。 

2)document.querySelector()

需要一个选择的字符串作为参数,可以根据以一个CSS选择器查询一个元素节点对象;

使用该方法只会返回唯一的元素,若有多个则返回第一个(不兼容IE6-8)

<div class=”box1”>
		<div>我是box选择器里的div</div>
</div>

......

var div=document.querySelector(".box1 div");
console.log(div.innerHTML);  //我是box选择器里的div

3)document.querySelectorAll() 

和前者类似,但它会将符合条件的元素封装在一个类数组中返回,只要是选择器都可以传;(不兼容IE6-8)

4)获取元素标签选择器 

[context].getElementsByTagName(),在指定的上下文容器中,通过标签名获取一组元素的集合,上下文可以是document也可以是指定容器,返回的是一个类数组,保存所有符合条件的元素

获取body标签

var body=document.getElementsByTagName("body")[0];

document.body,在document中有一个属性body保存的是body的引用;

5)获取元素类选择器 

[context].getElementsByClassName(),在指定的上下文中,通过样式类名获取一组元素集合(不兼容IE6-8)

6)获取元素name属性

document.getElementByName(),在整个文档中,通过标签的name属性获取一组节点集合,在IE中只有表单的NAME才能识别,多以我们一般只应用于表单元素的获取。

7)获取html根标签<html>,head标签,body标签

document.documentElement;
document.head;
document.body;

8)获取页面上的所有元素

document.all
document.getElementById("*");

497f80e9971441429cfa6570b130910c.png


2.操作dom元素属性

 1)获取属性值的两种方式

1.element.属性 一般用于获取内置元素属性

2.element.getAttribute('属性') ,一般用于获取自定义属性

 2)设置属性值

1.element.属性 = 值

2.element.setAttribute(‘属性’,值)  一般用于设置自定义属性;

setAttribute设置的自定义属性只能用getAttribute获取,另一种方式也是如此。

3)移除属性

1.element.removeAttribute(“属性”);

4)H5新增的自定义属性

data-开头作为自定义属性名并赋值,使用:

element.setAttribute(“属性名”,属性值);

element.dataset.属性名 (属性名不需要data开头,驼峰命名法);ie11以上才兼容!!

element.dataset[“属性名”]

dataset是一个集合里面存放了所有data开头的自定义属性; 


3.节点操作

利用节点层次关系获取元素时候更加简单;

节点具有nodeType(节点类型),nodeName(节点名称),nodeValue(节点值)三个基本属性;

节点类型:nodeType属性有三个值

1 --元素节点 

2 –属性节点

3 – 文本节点(文字,空格,换行等)

节点名称节点类型nodeType节点名称nodeName节点值nodeValue
元素节点 1大写的标签名null
文本节点3'#text'文本内容
注释节点8'#common'注释内容
文档节点9‘#document’null

1)获取父节点

element.parentNode:返回的是距离最近的父节点,没有父节点返回null

2)获取子节点

element.childNodes:得到了所有的子节点包括元素节点文本节点;

element.children:如果只要获得元素节点可以使用,获取所有的子元素节点,或者通过子节点的nodeType属性判断节点的类型是否为1;

以上两者在ie6-8中会把注释节点当作元素节点获取到;

element.firstChild:第一个子节点,文本或元素;

element.lastChild:最后一个子节点,文本或元素;

element.firstElementChild:第一个子元素节点   --- ie9以上支持;

element.lastElementChild:最后一个子元素节点;

或者使用children[0]或者children[xx.children.length - 1]获取;

3)获取兄弟节点(sibling  n. <正式>兄弟姐妹

element.nextSibling:下一个兄弟节点,包含元素节点,文本节点;

element.previousSibling:上一个兄弟节点,包含元素节点,文本节点;

element.nextElementSibling:获取下一个兄弟元素节点; // IE9起兼容 

element.previousElementSibling:获取上一个兄弟元素节点  // IE9起兼容

4)页面添加节点

页面上添加元素:创建节点  -- >  添加节点

4.1 创建节点(元素节点)

var node = document.createElement("tagName");

document.createTextNode([text content]);

4.2 添加节点

后面追加元素:node.appendChild(child);

前面追加元素:node.insertBefore([element],[newElement]);

<ul>
	<li></li>
</ul>

......

ul.insertBefore(li,ul.children[0]);  // 给ul内添加li

5)删除节点

node.removeChild(child);

6)复制节点

node.cloneNode();

node就是要克隆的节点;括号为空,或传值false,为浅拷贝;传值true,为深拷贝;


注意点1:document.write和innerHTML和createElement创建元素的区别?

document.write直接将内容写入页面的内容,页面文档流加载完毕后再触发会导致页面重绘,其他元素全部消失,重新创建一个只有这个新建元素的页面;

innerHTML采用拼接字符串的方式后者创建多个元素的时候效率高于前者;innerHTML采用数组的方式拼接效率要高于createElement;

createElement创建的元素默认不在页面中显示,需要根据实际的需求,添加到页面中的某个位置上。

数组方式:

var arr = [];
for(i=0;i<1000;i++){
	array.push("<a href='#'></a>");
}
document.body.innerHTML = array.join('');

4.js操作样式

1)通过js操作内联样式

  • 修改语法:元素.style.样式名 = 样式值

        如果css样式名中包含-,这种命名在js中是不合法的,需要将这种命名改为驼峰命名法,如:

                 background-color  --> backgroundColor

        通过style设置的样式内联样式,它具有较高的优先级,所以js修改的样式往往立即生效;

  • 读取语法:元素.style.样式名

        通过style设置和读取的样式都是内联样式,无法读取样式表中的样式

2)读取元素当前样式

语法:元素.currentstyle.样式名

如果当前样式没有设置样式则获取它的默认值(transparent auto...);这个属性只有IE6,7,8支持;

在其它浏览器中使用 getComputedStyle(),可以获取当前元素所有经过浏览器计算过的样式。

需要两个参数:

       第一个:要获取样式的元素

       第二个:可以传递一个伪类(:before,:after),一般都传null

该方法会返回一个对象CSSStyleDeclaration,对象中封装了当前元素对应的样式,该方法不支持IE8以及以下的浏览器

alert(getComputedStyle(box1,null).width);

通过currentStyle和getComputedStyle()读取到的样式都是只读的,不能修改,要修改必须通过style属性。


读取样式兼容方法如下:

function getstyle(obj,name){
    //加了window则getComputedStyle变成了对象的属性,没找到该属性返回undefined
    if(window.getComputedStyle){
        return getComputedStyle(obj,null)[name]; //普通浏览器
    }
    else{
        return obj.currentstyle[name];  //IE8
    }
}

3)js盒子模型属性(共13个)

1.clientWidth/clientHeight

获取元素的可视区域宽度和高度,包括元素的内容区和内边距,不包括边框,返回值里没有px,获取的结果为整数(四舍五入),属性是只读的不能修改,内容溢出对其无影响。

获取当前一屏幕的宽高:

let fullScreenWidth = document.documentElement.clientWidth || document.body.clientWidth;
let fullScreenHeight = document.documentElement.clientHeight || document.body.clientHeight;

2.clientLeft/clientTop

获取盒子左边框和上边框的大小

3.offsetWidth/offsetHeight

获取元素的内容区内边距边框的和 ,不包含margin,即盒子本身的宽高;

4.offsetParent

获取当前元素的定位父元素,会获取到当前元素最近的开启定位的祖先元素,都没开启返回body

parentNode:返回的是最近的父元素

5.offsetLeft/offsetTop

当前元素相对于父参照物的左侧偏移量/上侧偏移量,即当前元素的外边框到父参照物的里边框;父参照物和父元素没有必然的联系,在同一个平面上,最外层的元素是所有后代元素的父参照物,而基于position:relative,absolute,fixed可以让一个元素脱离文档流(一个新的平面),从而改变元素的参照物。

需要注意的标准IE8下这两个值获取的是当前元素的外边框到父参照物的外边框,即包括了父参照物的边框。

注意:

document.body.offsetParent === null

6.scrollWidth/Height

可以获取元素整个滚动区域的宽度和高度;在内有内容溢出的情况下,获取的结果和client是一样的;在有内容溢出的情况下获取的结果约等于真实内容的宽高(上/左padding + 真实内容的高度/宽度);

不同的浏览器的默认样式不同;设置overflow属性对于对于最后的结果也会产生一定的影响。

获取整个页面真实的高度:

document.documentElement.scrollHeight || document.body.scrollHeight

7.scrollLeft/scrollTop

可以获取水平滚动条滚动的距离/垂直滚动条滚动的距离;

在以上13个属性中,只有scrollTop和scrollLeft是可读写属性,其余的都是只读属性;

当满足scrollHeight-scrollTop==clientHeight时候说明垂直滚动条已经到底了

4)修改样式类

修改语法:[element].className = xxx


示例1:图片延迟加载

思路:

1.最开始不显示图片,对img的src属性不设置任何图片地址,把图片的真实地址设置给自定义属性DATA-SRC/TRUE-IMG

2.结构中,使用一个盒子包裹着图片,在图片不显示的时候占据这个位置,并且设置默认的背景图或背景颜色

3.当浏览器窗口完全显示到图片的位置时,再去加载真实的图片,让其显示出来。


图片完全显示出来的条件:盒子的高度 + 盒子距离body的上偏移 <= 一屏幕的高度 + 卷去的高度

让图片显示:获取图片TRUE-IMG属性的值,赋值给src属性,当图片能正常加载出来后,让图片显示即可

<head>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        img {
            border: none;
        }

        img[src=""] {
            display:none;
        }
        .imgBox {
            width: 300px;
            height: 245px;
            margin:800px auto;
            background: #ccc;
        }
        .imgBox img {
            width:100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <div class="imgBox">
        <img src="" alt="" trueImg="./img/伞兵.png">
    </div>

    <script>

        // 获取一个元素距离body的左/上偏移(无论父参照物是谁)
        function offset (currentElement) {
            let parent = currentElement.offsetParent;
            let left = currentElement.offsetLeft;
            let top = currentElement.offsetTop;

            // 存在父参照物,且好没有遍历到BODY元素
            while (parent && parent.tagName !== 'body') {
                // 在原有基础上加上父参照物的边框和父参照物的偏移
                // IE8中偏移包含边框需要分来考虑
                if (!/MSIE 8\.0/.test(navigator.userAgent)) {
                    left += parent.clientLeft;
                    top += parent.clientTop;
                }

                left += parent.offsetLeft;
                top += parent.offsetTop;

                // 获取上级参照物
                parent = parent.offsetParent;
            }

            return {
                top,
                left
            }
        }

        // 显示图片
        function lazyImg (currentImage) {
            // 给src赋值真实的图片地址
            let trueImg = currentImage.getAttribute('trueImg');
            currentImage.src = trueImg;
            // 校验图片能真实显示出来
            currentImage.onload = function () {
                currentImage.style.display = 'block';
                currentImage.isLoad = true;
            };

            currentImage.onError = function () {

            }
        }

        // 判断图片是否完全显示出
        let imgBox = document.querySelector('.imgBox');
        let _img = imgBox.querySelector('img');

        // 页面滚动就触发事件
        window.onscroll = function () {
            // 如果加载过了就不需重复加载
            if(_img.isLoad) return;

            let html = document.documentElement;
            let B = html.clientHeight + html.scrollTop;
            let A = imgBox.offsetHeight + offset(imgBox).top;

            if(A <= B) {
                lazyImg(_img);
            }
        }
    </script>
</body>

5.事件

就是用户和浏览器之间的交互行为,元素天生自带的操作行为,比如:点击按钮触发点击事件;

我们可以在事件对应的属性中添加一下js代码,这样当事件触发时,这些代码会执行,但结构和行为耦合不推荐使用;

可以为按钮的对应事件指定处理函数的形式响应事件,这样当事件被触发时,对应的函数会调用;

6.事件对象

当事件的响应的回调函数被触发时,浏览器都会将一个事件对象(event)作为实参传递进响应函数;在事件对象中封装了当前事件相关的一切信息,比如鼠标的坐标,键盘的哪个按键被按下,鼠标滚轮滚动的方向。

areaDiv.onmousemove=function(event) {
    console.log(event.target);
}

事件对象常用属性和方法:

1)event.target返回的是触发事件的对象,由a触发的就是a,由li触发的就是li, ie6,7,8使用e.srcElement ;可以通过target中的tagName属性获取触发事件元素的标签名,例如点击是div标签则tagName属性为大写的DIV;在不兼容此属性的浏览器中使用srcElement获取事件源。

2)event.currentTarget绑定事件的对象this,在事件的委派中,该属性获取的始终是绑定的那个元素,而不会是点击的子元素;

3)event.type:返回事件的类型(相当于click,mouseover);

4)event.preventDefault():阻止默认行为(让链接不跳转,让提交按钮不提交),return false 也能阻止默认行为,无兼容问题,但是其后面的代码不执行,不能用于addEventListener。在不兼容此属性的浏览器中使用event.returnValue = false

  • 处理浏览器兼容:

在IE8中,响应函数被触发时,浏览器不会传递事件对象,在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的;

解决兼容性问题

event = event || window.event;

7.常用事件

1)onload事件

浏览器在加载页面时是自上向下的,读取到一行就运行一行,如果将js代码写到页面的上面在代码执行时,页面还没有加载;将js代码写到页面的下部就是为了在页面加载完毕后再执行js代码;

window.onload()事件会在整个页面加载完成后再触发,该事件对应的响应函数会在页面加载完成后执行,这样可以确保代码执行时DOM对象已经加载完毕了,一般放在head标签时使用;

window.onload=function(){
    var btn =document.getElementById("Btn");  
	btn.onclick=function(){
        alert("你还点我");
    };
};

onunload事件:资源卸载

beforeunload:当前页面关闭之前(点击关闭浏览器弹出窗口)


2)DOMContentLoaded事件

当DOM结构加载完毕触发,不包括图片,css等就可以执行,速度比前者快; DOM0事件绑定不支持。


3)鼠标事件

onclick:鼠标单击事件

dbclick:鼠标双击事件

onmousedown:鼠标按下

onmouseup:鼠标抬起

onmouseover:鼠标在元素中划过

onmouseout:鼠标滑出

onmouseenter:鼠标进入

onmouseleave:鼠标离开

区别mouseover和mouseenter:

dc1c304e8703486d93ce0ebd11f875de.png

onmousemove:该事件会在鼠标在元素中移动时候触发

鼠标的事件对象:

1.e.clientX,e.clientY

返回鼠标在页面的可视区域的X和Y坐标(和页面滚到哪里无关的)

2.e.pageX, e.pageY

相对与文档页面的X坐标和Y坐标;(垂直滚动条水平滚动条向左向右滚动值会越大)IE9+

3.e.screenX,e.screenY

距离电脑屏幕的坐标

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546W5Ly0Xw==,size_20,color_FFFFFF,t_70,g_se,x_16

触发鼠标事件浏览器的处理原理:

1.当鼠标在页面上触发鼠标事件时,浏览器会捕获到当前的操作行为,浏览器会把本次操作的信息存到mouseEvent的实例中去,得到事件对象;

2.通知所有绑定的方法开始执行,并且将event事件对象作为实参传递给每个方法,所以在每个方法中得到的事件对象实际上是同一个;

3.后面再重新触发这个事件行为会重新获取本次操作信息,用新的信息替换老的信息,然后重复之前的步骤。

示例:拖拽案例

使用mousedown mousemove mouseup 事件实现鼠标拖拽盒子的功能;

需要满足的公式:盒子当前值 = 鼠标当前值 - 鼠标起始值 + 盒子起始值

给盒子添加自定义属性保存鼠标起始值和盒子起始值

需要解决的问题:解决鼠标移动过快失去焦点

IE:使用setCapture(),但此方法不兼容谷歌浏览器

通用方法:将mousemove和mouseup事件绑定给document

<head>
    <style>
        html,body {
            height: 100vh;
            width: 100vw;
            overflow: hidden;
        }

        .box {
            position: absolute;
            top: 0;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: red;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        let box = document.querySelector('.box');
        let ua = navigator.userAgent;

        box.onmousedown = down;

        function down(event) {
            this.initLeft = this.offsetLeft;
            this.initTop = this.offsetTop;
            this.mouseInitX = event.pageX;
            this.mouseInitY = event.pageY;
            // 谷歌中解决鼠标焦点丢失的问题,将事件绑定给document
            document.onmousemove = move.bind(this);
            document.onmouseup = up.bind(this);

            // 解决鼠标焦点丢失问题,将鼠标和盒子捆绑再一起(仅仅兼容IE)
            if (/msie/i.test(ua)) {
                this.setCapture();
            } 
        }

        function move(event) {
            let target = event.target;
            console.log(this,target)
            let currentX = event.pageX - this.mouseInitX + this.initLeft;
            let currentY = event.pageY - this.mouseInitY + this.initTop;

            // 边界值判定
            let maxLeft = document.documentElement.clientWidth - this.offsetWidth;
            let maxTop = document.documentElement.clientHeight - this.offsetHeight;
            let minLeft = 0,
                minTop = 0;

            currentX = currentX < minLeft ? minLeft : (currentX > maxLeft ? maxLeft : currentX);
            currentY = currentY < minTop ? minTop : (currentY > maxTop ? maxTop : currentY);

            this.style.left = currentX + 'px';
            this.style.top = currentY + 'px';
        }

        function up() {
            document.onmousemove = null;
            document.onmouseup = null;
            // 把鼠标和盒子解绑
            if (/msie/i.test(ua)) {
                box.releaseCapture();
            } 
        }
    </script>
</body>

4)键盘事件

1.onkeyup 当键盘弹起的时候触发;

2.onkeydown 按键被按下,对于它来说如果按下某一个键不松开事件会连续触发,连续触发时,第一次和第二次之间会有一段时间间隔,以防止误操作;

可以通过code和key存储按键;

可以通过event.keyCode或which(前者不兼容时用)来获取按键的编码,通过它来判断哪个键被按下,例如:

上下方向键 37 38 39 40 => 左 上 右 下

空格 SPACE => 32

回车 ENTER => 13

回退 BACK => 8

删除 DEL => 46

SHIFT => 16,CTRL => 17, ALT => 18

keyCode不区分字母的大小写

event.altKey,ctrlKey,shiftKey用来判断alt,ctrl,shift键是否被按下,如果按下返回true,否则返回false

3.onkeypress 当键盘按下的时候触发,一般用于某键连续按住时(不能识别功能键—如ctrl-enter shift fn CapsLock <- -> keyCode区分字母的大小写);

执行顺序:2-3-1

var input=document.getElementsByTagName("input")[0];
input.onkeydown=function(event){
    // 数字的keyCode是48-57,文本框中不能输入数字
    if (event.keyCode>=48 && event.keyCode<=57) {
        return false;   
    }
    // 在文本框中输入内容属于onkeydown的默认行为
}

keydown和keypress在文本框中的特点:他们事件触发的时候,文字还没有落入文本框中。需要使用keyup事件!


5)鼠标滚轮事件

onmousewheel是鼠标滚轮滚动事件,会在滚轮滚动时触发,火狐不支持,在火狐中需要使用DOMMouseScroll来绑定事件,该事件要通过addEventListener()函数来绑定;

event.wheelDelta可以获取滚轮滚动的方向,向上滚是120,向下滚是-120,不看大小只看正负,这个值火狐不支持,在火狐中使用event.detail来获取,向上滚是-3,向下滚是3;

<style>
        #box1{
            width: 100px;  
            height: 100px;
            background-color: brown;
        }
    </style>
    <script>
        window.onload=function(){
            var box1= document.getElementById("box1");
            box1.onmousewheel()=function(event){
                event=event||winodw.event;
                if (event.wheelDelta>0 || event.dateil<0) {
                    box1.style.height=box1.clientHeight-10+"px";
                }else{
                    box1.style.height=box1.clientHeight+10+"px";
                }
                // 当滚轮滚动时,如果浏览器有滚动条,会随之滚动,可以取消此默认行为
                // addEventListener()方法来绑定的响应函数,取消默认行为时不能使用return false,需要使用event.preventDefault(),IE8不支持
                event.preventDefault && event.preventDefault();
                return false;
            }
            bind(box1,"DOMMouseScroll",box1.onmousewheel);
        }
        function bind(obj,eventStr,callback){
                if (obj.addEventListener) {
                    obj.addEventListener(eventStr,callback,false);
                }else{
                    obj.attachEvent("on"+eventStr,function(){
                        //在匿名函数中调用回调函数.call方法可以改变this的对象
                        callback.call(obj);
                    });
                }
            }

    </script>
</head>
<body>
    <div id="box1"></div>
</body>

6)移动端手指事件 

单手指事件模型 -- Touch

touchstart:手指按下

touchmove:手指移动

touchend:手指离开

touchcancel:操作取消,一般应用于非正常状态下的操作结束,如:点击时手机突然关机

当出发上述事件,事件回调会得到事件对象即TouchEvent的实例,该实例中可以获取两个属性toucheschangedTouches;

区别touches和changedTouches

它们都存储每根手指的信息(它是一个集合,对于TOUCH单手指事件来说集合中只有一项,即获取event.touehes[0],event.changedTouches[0]),changedTouches存储的是手指发生改变操作的信息,但是最开始按下的时候和touches是一样的,但是它可以在手指离开的事件中获取到手指离开瞬间的信息,而touches在离开的时候则没有,真实项目中一般用changedtouches.

下列代码模拟了移动端的一个点击操作 

document.body.addEventListener('touchstart',function (ev) {
    let point = ev.changeTouches[0];
    this.startX = point.clientX;
    this.startY = point.clientY;
    this.isMove = false;
});

document.body.addEventListener('touchmove',function (ev) {
    let point = ev.changeTouches[0];
    let changeX = point.clientX - this.startX;
    let changeY = point.clientY - this.startY;
    // 误差判断
    if (Math.abs(changeX) >= 30 || Math.abs(changeY) >= 30) {
        this.isMove = true;
    }
}

document.body.addEventListener('touchend',function (ev) {
    if(this.isMove) {
        console.log('这是移动操作');
        return;
    }
    console.log('这是点击操作');
}

移动端的操作一般都使用类库,直接使用内部封装好的方法来实现效果,如zepto中的tap事件实现单击事件绑定,它们都是基于上述代码进行封装的,常见类库有:zepto,fastclick,hammerjs。

移动端键盘事件和PC端的区别

移动端是虚拟键盘,所以对keydown/keyup/keypress兼容很差,想要实现类似的需求,需要用input事件来完成


多手指事件 --  Gesture

gesturestart

gesturechange/gestureupdate

gestureend

gesturecancel

移动端的click事件

PC端click是点击事件,移动端click是单击事件,所以移动端使用click会存在300ms延迟的问题,在第一次触发后,会等待300ms,看是否第二次触发,存在则为双击,不存在则为单击,移动端所有操作基本上都是基于touch/gesture事件模型模拟出来的


7)表单元素常用事件

focus:获取焦点

blur:失去焦点

change:内容改变


8)音视频常用事件

canplay:可以播放,资源没有全部加载完,播放中可能卡顿

canplaythrough:可以播放,资源已经加载完,播放中不会卡顿

play:开始播放

playing:播放中

pause:暂停


9)拖拽事件

dragstart:拖动开始

drag:拖动

dragend:拖动结束

dragover:拖动元素到指定的目标区域上(不需要完全进入目标区域)

drop:拖动的元素放到目标区域中

1.给要拖拽的元素设置可被拖拽属性 draggable="true",默认link和图片是可被拖拽的。

2.在拖拽开始时dragstart,使用setData方法记录一些信息,可以在事件对象(DragEvent)中获取到DataTransfer的实例中访问,其中还包括如下常用的方法:

1.ev.dataTransfer.setData(类型标识,对应的值)

一般约定的类型标识:text/plain text/html text/uri-list

设置的内容最后都会变成字符串

2.ev.dataTransfer.getData(类型标识)

无法在dragover事件中获取

3.ev.dataTransfer.clearData() 

3.给拖动到的目标容器添加dragover和drag事件,但需要阻止容器默认行为 ev.preventDefault() 

示例:将元素box拖动到容器container中

<div class="box" draggable="true"></div>
<div class="container"></div>
<script>
    let box = document.querySelector('.box');
    let container = document.querySelector('.container');
    box.ondragstart = function (ev) {
        ev.dataTransfer.setData('text/plain',ev.target.className);
        console.dir(ev.target);
    };

    container.ondragover = function (ev) {
        ev.preventDefault();
    };
    
    container.ondrop = function (ev) {
        ev.preventDefault();
        let _CLASS = ev.dataTransfer.getData("text/plain");
        let _ELE = document.querySelector("." + _CLASS);
        container.appendChild(_ELE);
    }
</script>

10)其他事件

error:资源加载失败

scroll:滚动事件

readystatechange:AJAX请求状态改变事件

8.事件流 

描述的是从页面中接收事件的顺序,事件发生时会在元素节点中按照特定的顺序传播,这个传播过程即DOM事件流,DOM事件流分为三个阶段:

1.捕获阶段

2.当前目标阶段

3.冒泡阶段

如果给一个div注册了点击事件:

 431926cc8ea34b738907fe13b9fc9077.png

1)事件的冒泡

事件的向上传导,当后代元素的事件被触发时,其祖先元素的相同事件也会被触发;

如何取消冒泡

event.cancelBubble=true;   IE

event.stopPropagation();

写在子元素的事件回调函数中

 2)事件的委派

将事件统一绑定给元素共同的祖先元素,这样当我们后代元素上的事件触发时,会一直冒泡到祖先元素上,从而通过祖先元素事件来处理事件。

事件的委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能;

<button id="01">添加超链接</button>
    <ul id="ul">
        <li><p>我是p元素</p></li>
        <li><a href="javascript:;" class="link">超链接1</a></li>
        <li><a href="javascript:;" class="link">超链接2</a></li>
        <li><a href="javascript:;" class="link">超链接3</a></li>
    </ul>

......

var ul =document.getElementById("ul");
ul.onclick=function(evnet){
    // alert("我是ul的单击响应函数");
    event=event||window.event;
    // target: event中的target表示的是触发事件中的对象,由a触发的就是a,由li触发的就是li
    if (event.target.className=="link") {
        alert("我是ul的单击响应函数");  
    };
}

这里我们给父元素ul绑定单击事件,这样ul内的p元素,a元素上发生点击事件就会冒泡到ul身上,触发ul的单击事件回调,而我们只需要通过事件对象的className属性确定点击的是a标签;

9.事件绑定

DOM0事件绑定:

绑定原理:使用对象.事件=函数的形式,绑定的函数只能同时为一个元素的一个事件绑定一个响应函数不能绑定多个,若绑定了多个,则后面的会覆盖掉前面的,其原理是给元素的私有属性赋值(注:DOM1事件绑定和DOM0是一致的)

box.onclick = function () {
    console.log('哈哈哈');
}

box.onclick = function () {
    console.log('啊啊啊');
}

上述案例中,点击元素box只会打印'啊啊啊',以最后一次绑定的为主。 

DOM0移除事件绑定:直接赋值为null即可,例如移除上述示例中的单击事件:

box.onclick = null;

 DOM2事件绑定:

1) addEventListener()这个方法也可以为元素绑定响应函数

参数:

1.事件的字符串,不要on

2.回调函数,当使事件触发时该函数会被调用

3.是否在捕获阶段触发事件,需要一个布尔值,一般都是false

它可以为一个元素的相同事件同时绑定多个响应函数,这样当事件被触发时,会按照响应函数绑定的顺序执行;

DOM2事件绑定原理:基于原型链查找机制,找到EventTarget.prototype上的方法且执行,执行此方法后,会把给当前元素某个事件绑定的所有方法,存放到浏览器的事件池中,当事件行为触发,会把事件池中存储的方法依次按顺序执行

box.addEventListener('click',function () {
    console.log('哈哈哈');
},false);
box.addEventListener('click',function () {
    console.log('啊啊啊');
},false);

上述示例中两个单击事件的回调都会执行。 

DOM2移除事件绑定:

需要使用实名函数绑定事件回调,这样可以基于实名函数去移除事件绑定;使用removeEventListener进行移除,需要指定好事件的类型,方法和信息,例如:

function fn () { console.log('hhh'); }

box.addEventListener('click',fn,false);  // 绑定事件
box.removeEventListener('click',fn,false);  // 解绑事件

基于addEventListener向事件池增加方法存在去重机制,同一个事件类型同一个元素在事件池中只能存储一遍同一个方法,不能重复存储。


2) addEventListener()不支持IE8以及以下的浏览器,在IE6-8中使用attachEvent()来绑定事件

参数:

1.事件的字符串,要on

2.回调函数

这个方法也是可以为一个事件绑定多个处理函数,不同的是执行顺序和addEventListener()相反---后绑定先执行,addEventListener()中的this是绑定事件的对象,attachEvent()中的this是window


注意:

1)如果希望在事件的捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true,一般情况下我们不会希望在捕获阶段触发事件。

2)IE8及以下浏览器没有捕获阶段;

3)DOM2可以给一些特殊事件类型进行事件绑定这些事件类型DOM0不支持,例如:DOMContentLoaded,transitioned;


十一.BOM对象

浏览器对象模型,通过JS操作浏览器,在BOM中提供一组对象来完成对浏览器的操作;

1.Window

代表了整个浏览器的窗口,也是网页中的全局对象,在全局作用域中的变量都会作为window的属性保存,全局作用里的函数都会作为window的方法保存。

1)setInterval()

定时调用,可以将一个函数每隔一段时间执行一次;

参数:

1.回调函数,该函数每隔一段时间执行一次

2.每次调用间隔的时间,单位是毫秒

2)setTimeout()

延时调用,一个函数不马上执行,而是间隔一段时间后再执行,而且只会执行一次;

参数:

1.回调函数,该函数会在指定延迟时间后执行一次

2.延迟调用的时间,单位是毫秒

setInterval,setTimeout的返回值:

返回一个Number类型的数字,这个数字用来作为定时器的唯一标识

3)clearInterval()

可以用来关闭一个定时器,该方法需要一个定时器标识作为参数,这样将关闭标识对应的定时;

4)clearTimeout()

可以用来关闭一个延时调用,该方法需要一个定时器标识作为参数;

示例:

let n = 0;
let timer1 = setTimeout(() => {
    console.log(++n); // 1 2 3 4 5 6
    if(n > 5) {
        // 清除定时器
        clearTimeout(timer1);
        timer1 = null;
    }
},1000);

let timer2 = setInterval(() => {},1000);
console.log(timer1,timer2); // 1 2

 


2.Navigator

代表当前浏览器的信息,通过该对象可以来识别不同的浏览器;

由于历史原因,navigator对象中的大部分属性都不能帮助我们识别浏览器了

一般我们只会使用userAgent来判断浏览器的信息,userAgent就是一个字符串,这个字符串包含描述浏览器信息的内容,不同的浏览器会有不同的userAgent

alert(navigator.userAgent);
// IE:Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; zhumu 4.0.0; Zoom 3.6.0; wbx 1.0.0; wbxapp 1.0.0; rv:11.0) like Gecko

如果通过UserAgent不能判断,还可以通过一些浏览器中特有的对象,来判断浏览器的信息,比如window.ActiveXObject构造函数IE特有;

判断浏览器类型:

var ua= navigator.userAgent;
if (/firefox/i.test(ua)) {
    alert("火狐");
}else if(/chrome/i.test(ua)){
    alert("谷歌");
}else if(/msie/i.test(ua)){
    alert("IE");
}else if("ActiveXObject" in window){
    alert("IE11");
}

3.Location

代表当前浏览器的地址栏信息,通过location可以获取地址栏信息,或者操作浏览器跳转页面

如果直接打印location则可以获取到地址栏的信息(当前页面的完整路径)

如果直接将location属性修改了一个完整的路径,或相对路径,则页面会自动跳转到该路径,并且会生成相应的历史记录

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546W5Ly0Xw==,size_20,color_FFFFFF,t_70,g_se,x_16

 location常用属性

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546W5Ly0Xw==,size_20,color_FFFFFF,t_70,g_se,x_16

 补充:

1)assign()

用来跳转到其他页面,作用和修改location一样的

 location.assign("http://www.baidu.com"):

2)reload()

重新加载当前页面,作用和刷新按钮一样;ctrl+F5手动强制清空缓存

location.reload(true);

  如果在方法中输入一个true作为参数则是强制清空缓存刷新,否则不清空

3)replace()

可以使用一个新的页面替换当前页面,调用完毕后也会跳转页面,不会生成历史记录不能使用回退按钮

4.History

代表浏览器的历史记录,可以通过该对象来自操作浏览器的历史记录,由于隐私的原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页,改操作只在当次访问时有效。

history.length:该属性可以获取到当此访问的链接数量

history.back():可以回退到上一个页面,作用和浏览器的回退按钮一样

history.forward():可以跳转到下一个页面,作用和浏览器的前进按钮一样

history.go():可以跳转到指定的页面,需要一个整数作为参数,1-向前跳转一个页面;-2-向后跳转两个页面

5.Screen

代表用户屏幕的信息,通过该对象获取到用户的显示器的相关信息。


这些BOM对象都是作为window对象的属性来保存的,可以通过window对象来使用,也可以直接使用

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值