- 内容太多 不定时更新
简介
含JavaScript、Bom、Dom、ES6超全总结
文中所使用全是个人项目中实际运用到的知识
写这篇文章既是一种分享也是对自己知识点的巩固
不正之处欢迎指
内容较长--15847字
一、js常用事件
pc端
| 事件 | 触发方式 | |
|---|---|---|
| click | 当鼠标点击时触发 | |
| mouseover | 当鼠标指针移动到元素上时触发 | |
| mouseout | 当鼠标指针移出元素时触发 | |
| mouseenter | 当鼠标指针移动到元素上时触发(不支持冒泡) | |
| mouseleave | 当鼠标指针移出元素上时触发(不支持冒泡) | |
| mousemove | 当鼠标指针移动到元素上时触发 | |
| mousedown | 当元素上按下鼠标按钮时触发 | |
| mouseup | 当在元素上释放鼠标按钮时触发 | |
| mousewheel | 当鼠标滚轮正在被滚动时运行的脚本 | |
| keydown | 在用户按下按键时触发 | |
| keyup | 当用户释放按键时触发 | |
| load | 页面结束加载之后触发 | |
| scroll | 当元素滚动条被滚动时运行的脚本 | |
| blur | 元素失去焦点时运行的脚本 | |
| focus | 当元素获得焦点时运行的脚本 | |
| change | 在元素值被改变时运行的脚本 |
移动端
| 事件 | 触发方式 |
|---|---|
| click | 当点击时触发(单击) |
| load | 页面结束加载之后触发 |
| scroll | 当元素滚动条被滚动时运行的脚本 |
| blur | 元素失去焦点时运行的脚本 |
| focus | 当元素获得焦点时运行的脚本 |
| change | 在元素值被改变时运行的脚本 |
| input | 代替keyup、keydown |
| touch事件模型 | 处理单手指操作 |
| gesture事件模型 | 处理多手指操作 |
| touchstart | 手指按到屏幕上 |
| touchmove | 手指在屏幕上滑动 |
| touchend | 手指离开屏幕 |
| touchcancle | 特殊情况下关闭/退出时触发 |
二、变量和常量
2.1 量:可以变化的量(值,数据)
2.1.1 关键字 var 声明(创建、定义)变量
var 变量名;
2.1.2 值:通过 = 完成对变量的赋值
=:赋值符号,将=右边的数据放到等号的左边
var age; age=18;
2.1.3 简写:var 变量名=变量值;
通过一句话同时声明多个变量
var name,age,gender,height,weight;
2.1.4 变量名 命名规范
-
1、不能使用JS关键字进行命名
-
2、不能以数字或数字开头
-
3、尽量的要见名知意var name;//推荐
-
4、尽量的使用驼峰命名法,或者下划线命名法
2.1.5 变量的使用
-
1、获取变量的值,直接使用,变量名就是在使用变量的值
-
2、变量的值可以随时随地的修改
-
3、以后你用到的变量值为undefined,说明没有赋值
总结:以后何时用到变量?
只要你需要反复使用的数据,咱们就要用变量提前保存起来,以后用变量名相当于就是在用变量值
2.2 常量:一旦被创建好,就不允许被修改的数据,如果修改就会报错
语法:const关键字声明常量 const 常量名=常量值;
2.3 作用域(scope):一个变量的可用范围
变量的使用规则的:优先使用自己的,自己没有找全局要,全局都没有则报错
-
1、全局作用域:拥有全局变量和全局函数:在任何位置都可以访问使用
-
2、函数作用域:拥有局部变量和局部函数:只能在函数本次调用时,内部可用
注意:后续会专门讲一讲这个作用域,这里就不浪费篇幅解释了 在函数中创建的变量一定要记得var,不然会导致全局污染:自动创建到全局去。
声明提前:在程序执行前,
将 var 声明的变量 和 function 声明的函数集中提前到当前作用域的顶部
但是赋值(var)留在原地
2.4 按值传递:两变量之间进行赋值
如果传递的是原始类型的值,修改新变量,原变量不会受影响,其实是复制了一个副本给对方,修改副本并不影响自己
如果传递的是引用类型的对象,修改新变量,原变量会受影响,因为其实两者用的是同一个地址值
三、数据类型转换
javascript是弱类型语言声明变量时,不需要指定其数据类型是什么,数据类型是由变量的值来决定的,对于不同数据类型的运算时,数据类型会进行自动转换(隐式转换)
查看具体的数据类型是什么的话:typeof(变量/表达式)
3.1 隐式转换:自动转换,根据程序自己来的转换
默认都是转为数字再运算
特殊:+运算,只要有一边是字符串,则两边都会变为字符串,+变为连接操作
注意:默认为转为数字只有 - * / %,必须是纯数字组成的字符串才能转为数字
只要不是纯数字组成的字符串,则为NaN(Not a Number)
"1000"*2;//1000*2
"1000a"*2;//NaN*2
如果我们都不知道转为这个数字应该为多少,结果绝对是NaN
NaN:Not a Number:不是一个数字,但是他确实是number类型
全是缺点:1、NaN参与任何算数运算,结果仍未NaN
2、NaN参与任何比较运算,得到结果永远为false,甚至不认识自己!!!
那我们怎么才能判断是不是NaN呢?
isNaN(变量名) - true:是NaN false:不是NaN
***往往反用:!isNaN(变量名) - true:是一个数字 false:是NaN
3.2 显示转换,即强制转换:可以手动调用某些方法实现数据类型转换
1、转string:
1、var str=x.toString();//null和undefined,不能使用.执行函数调用
2、var str=String(x);//万能的,相当于隐式转换,不会手动实用,还不如+""
2、转number:
1、number():其实他就是真正的隐式转换
2、***parseInt(str/num):
执行原理:从左向右读取x的每一个字符,碰到非数字字符就停止转换,并且不认识小数点,如果一来就碰到了不认识的字符,则为NaN
parseInt("35.45") -> 35
parseInt("3hello5")-> 3
parseInt("hello35") -> NaN
3、***parseFloat(str/num):
执行原理:和parseInt几乎完全相同,认识第一个小数点
3.3 转boolean:Boolean(x); //万能的,相当于隐式转换,不会手动实用,还不如 !!x
*强调:只有当x是"",0,NaN,null,undefined,false,才会转为false
其余都转为true
**何时会出现bool的隐式:2个点(条件判断中)
1、分支的条件中:if(x){};其实小括号之中都会被悄悄的转为一个bool值
2、循环的条件中:while(x){};其实小括号之中都会被悄悄的转为一个bool值
四、数据类型:
4.1 原始/值/基本类型
1、number:数字
2、string:"" 或 ''
3、boolean:布尔值,往往表示正确还是错误,取值只有2个:true(真)、false(假)
参与算术运算:true->1 false->0
4、undefined:创建了未赋值,默认值为undefined,undefined是垃圾
5、null:空,作用:释放变量,何时释放:变量使用后,以后不会再用了,就要记得释放他
变量名=null;
6、Symbol:表示独一无二的值
7、bigint:任意大的整数
4.1.1 字符串常用api
什么是字符串:多个字符组成的
只读字符数组!(字符串所有的API都不会修改原字符串)
-
和数组有相同的地方:
-
1、字符串字符的个数:str.length
-
2、获取字符串中指定位置的字符:str[i];
-
3、所有数组不修改原数组的API,字符串都可以使用(join、concat、slice),而且用法一致
-
String 专属API:只有字符串可以使用的方法
1、转义字符:\
1、包含和程序冲突的字符,需要使用转义符变为原文; \" \\
2、特殊功能的符号;\n -> 换行;\t -> tab键
3、输出unicode编码的字符; \uXXXX
记住:常识:中文的第一个字:4e00;最后一个字:9fa5
中文ASCII:19968~40869
2、大小写转换:
何时使用:只要程序不区分大小写时,就要先统一转换大写或者小写,在比较,验证码()
大写:var upper=str.toUpperCase(); 小写:var lower=str.toLowerCase();
3、获取指定位置字符的ascii码:
var num=str.charCodeAt(i);
从ascii码变回unicode号:
var 原文=String.fromCharCode(num);
4、查找字符串:查找关键字的位置(数组也能用它)
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
var i=str/arr.indexOf("关键字",fromi);
从fromi位置开始,查找右侧的下一个关键字的位置;fromi可以省略,从0开始
返回:找到,返回关键词第一个字符的下标;没找到(不重复),-1
作用
a:***判断有没有;鄙视题:查找一句话中所有的关键字的下标
公式:var i=-1;
while((i=str.indexOf("no",i+1))!=-1){
console.log(i);
}
b、判断是否重复,没有重复会返回-1
if(reds.indexOf(num)==-1){
reds.push(num);
}
5、截取字符串:
var substr=str/arr.slice(starti,endi+1); //最重要的,因为一个API,两个对象都可以使用
str.substring(starti,endi+1); //几乎和slice一样,但是不支持负数 垃圾
str.substr(starti,n); //n:截取的个数 垃圾
6、替换字符串://很棒,但是现在差正则
var newStr=str.replace(/'关键字'/g,"新内容");
var newStr=str.replaceAll("关键字","新内容");等同于replace+正则
7、切割字符串://相当屌
作用:字符串转为数组
var arr=str.split("切割符");
var a = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
a.map(Number); //结果:[1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.map(String); //结果: ['1', '2', '3', '4', '5', '6', '7', '8', '9']
8、拼接字符串、
var newStr=str.concat("str",...); // 完全等效于 +
9、获取指定位置的字符:str.charAt(i); 等效于 str[i]
获取随机数:var a=parseInt(Math.random()) 取值0-1;
Math.random().toString(16).substring(2)
4.2 引用/对象(属性和方法)类型
String Number Boolean -> 包装类型
- Function
- Object
- Array
- Date
- RegExp
- Error
- Math
- Global全局对象 -> 浏览器中全局对象被window代替了
4.2.1 数组
多个元素的组成集合,在一个变量中保存:一个变量希望保存多个数据,索引数组中的元素都是按照线性顺序排列,数组中每个元素都有一个唯一的位置序号,称之为下标/索引,下标从0开始到最大个数-1;
1、创建索引数组:
1、*直接量方式:var h52004=[ ];//空数组
var h52004=[元素1,元素2,元素3...];
2、构造函数方式:var arr=new Array();//空数组
var arr=new Array(元素1,元素2,元素3...);
2、访问数组:访问数组中的元素
数组名[下标];
2、添加数组元素:
数组名[下标]=值;//不推荐,需要去数下标
属性:arr.length:本意可以获取数组的长度;最大下标 一定等于 长度-1
固定套路:
1、获取倒数第n个元素:arr[arr.length-n];
2、添加到元素最后:arr[arr.length]=值; - 千万不要往中间添加,因为会变成稀疏数组
3、删除末尾的n个元素:arr.length--;
3、遍历索引数组:for
for(var i=0;i<arr.length;i++){
arr[i];//当前元素
}
4、关联(hash)数组:可自定义下标的数组(hash数组中的length为0,无意义)
1、创建关联(hash)数组:
1、创建空数组:var arr=[ ];
2、添加自定义下标并且赋值:arr["自定义"]=值
2、访问:arr["自定义下标"];
3、遍历hash数组:for in
for(var i in arr){
arr[i];//当前次元素
}
5、数组的API:API->预定义函数,只有数组能用的函数
1、Array.from()
1、将类数组对象转换为真正数组(document获取的dom列表); Array.from(arrayLike)
2、将Set结构的数据转换为真正的数组:Array.from(set)
3、将Map结构的key,Values转换为真正的数组:Array.from(new Map().keys()) Array.from(new Map().values())
2、var str=arr.join("自定义连接符");
可以自定义连接符,省略连接符,等效于arr.toString();
3、拼接数组:将concat后面的实参,拼接到数组arr之中
语法:var newArr=arr.concat(arr1,值1,值2...);
强调:1、concat不修改原数组,而返回一个新数组 - 必须拿一个变量去接住
2、concat参数中的数组会被悄悄的打散为单个参数传入
4、截取子数组:从arr中截取开始位置到结束位置的元素组成新的数组
语法:var subArr=arr.slice(starti,endi+1);
强调:1、不修改原数组,而返回一个新数组 - 必须拿一个变量去接住
2、含头不含尾
3、省略第二个参数,表示从starti一直截取到结尾
4、两个参数都省略,从头到尾复制了一份
5、可以传入负数参数,-1代表倒数第一个
此线向下都要修改原数组
5、splice:删除、插入、替换
删除:var dels=arr.splice(starti,n);
强调:其实splice也有返回值,返回的是所有被删掉的元素组成的新数组
插入:arr.splice(starti,0,值1,值2...);
强调:原starti位置的元素以及后续元素都会被向后推动
替换:var dels=arr.splice(starti,n,值1,值2...);
强调:插入的个数和删除的个数不必相同
6、翻转:arr.reverse();
7、排序
a、冒泡排序:见下文
b、API排序:sort
arr.sort(); 默认:按照转为*字符串*按位PK
问题1、:希望按照数字排序:
解决:arr.sort(function(a,b){
return a-b;})
通过a-b返回的数字来判断大小的
a>b,返回一个正数;a<b,返回一个负数;a==b,返回0
而sort会通过你返回的数字的来进行自动的排序
问题2:希望降序排列
解决:arr.sort((a,b)=> b-a)
总结:1、排序字符串 2、排序数字(升序、降序)
8、栈和队列:数组,新的添加元素和删除元素的方法
头入:
arr.unshift(值);
尾入:
arr.push(值); 代替 arr[arr.length]=值;
头出:
var first=arr.shift();
尾出:var last=arr.pop(); 代替arr.length--
栈指一端封闭,出入位置相同;用于希望获取最新数据
常用:头出+头入 尾出+尾入
队列指一端进另一端出;用于按照先来后到的顺序
常用:头出var first=arr.shift()+尾入arr.push(值) 尾出pop+头入unshift
一次只能删除一个,所以得到的值也只是一个值
8、扁平化数组
var newArray = arr.flat([depth])
arr4.flat(Infinity);
三不限制:
1、不限制下标越界 - 不要下标越界
2、不限制元素的类型
3、不限制元素的个数
特殊:读取元素:下标越界:undefined
添加元素:下标越界:自动在新位置添加新元素
结果:下标可能不连续 -- 稀疏数组
二维数组:数组中的元素又是一个数组
何时:在一个数组内,再次细分每个分类
访问:arr[r][c];
强调:列下标越界,返回undefined
行下标越界,报错!undefined[c],undefined没有资格使用[ ]
遍历二维数组:外层循环控制行,内层循环控制列
for(var r=0;r<arr.length;r++){
for(var c=0;c<arr[r].length;c++){
console.log(arr[r][c]);
}
}
冒泡排序:
var a = [36,26,27,2,4,19,50,48];
for(var i = 0; i < a.length; i++){
for(var j = 0; j < a.length-i; j++){
if(a[j] > a[j+1]){
[a[j],a[j+1]]=[a[j+1],a[j]] //解构赋值
}
}
}
console.log(a);
顺序排序:
var a = [4,12,13,4,3,42,33,4,43,44];
var temp;
var minIndex;
for(var i = 0; i < a.length; i ++){
minIndex = i;
for(var j = i + 1; j < a.length; j++){
if(a[minIndex] > a[j]){
minIndex = j;
}
}
temp = a[minIndex];
a[minIndex] = a[i];
a[i] = temp;
}
console.log(a);
4.2.2 函数
什么是函数:函数(Function)是一段被预定义好的,可以反复使用的代码段,是一个独立的功能体,可以将若干代码放在一个函数里。
1、定义函数 的两种方式
a、使用声明 function 关键字
function 函数名(形参列表){//函数体;return 返回值; }
return返回的东西需要用变量接住,且return会结束函数,所以return只能用一次
b、直接量方式:用于 绑定事件流
var 函数名=function(形参列表){//函数体;return 返回值;}
函数名其实是一个变量:函数名引用着真正的函数对象
c、构造函数:var 函数名=new Function("形参1","形参2",...,"函数体 return 返回值");
注意:函数定义好之后,不会立即执行,只有在调用后才会执行
2、调用函数:
语法:函数名(实参列表);
调用的方式:2种:
1、在JS内部调用 函数名();
2、可以绑定在页面元素上:比如:
<div onclick='eat()'></div>
3、什么东西适合封装为函数
1、要反复执行的代码
2、不希望打开页面立刻执行,可能是用户触发
3、本身就是一段独立的功能。
4、带参数的函数
定义在函数小括号中的变量,不需要写var,称之为形式参数,简称形参,默认值为undefined
function 函数名(形参,...){//函数体 }
注:形参相当于局部变量
调用带参数的函数,必须向里面传递参数,称之为实际参数,简称实参
function zhazhiji(fruit){
console.log("正在榨"+fruit+"汁");}
zhazhiji("苹果");
强调:1、传递实参的顺序要和形参的顺序一一对应上
2、函数不一定非要带有参数:如果是固定的操作,不需要传入参数,
5、预定义全局函数:前辈们定义好的,我们可以直接使用的,并且在任何位置都可以使用
常用:isNaN() 、paseInt()、pasefloat()、eval()
问题1:url中不允许出现多字节字符(汉字),如果出现会乱码(没有任何人看得懂)
解决:发送前,将多字节内容*编码*为单字节内容,服务器端人员接到后再*解码*
utf-8,1个汉字,3字节
如何使用:
编码:var codes=encodeURI("文字");
解码:var 原文=decodeURI(codes);
问题2:url中不允许出现保留字符,比如:/ :
使用升级版编码解码·
编码:var codes=encodeURIComponent("文字");
解码:var 原文=decodeURIComponent(codes)
6、重载(overload):相同函数名,根据传入的参数的不同,而执行不同的函数
问题:js不允许多个同名函数同时存在,最后定义的函数会覆盖之前所有同名函数
解决:arguments对象,类数组对象,只能在函数之中使用,并且不需要创建
作用:哪怕没有一个形参,也可以接住所有的实参;
特点:1、具有下标;2、具有length
总结:我们用arguments在函数中进行分支判断,通过判断arguments的长度或类型执行不同的操作 - 变相的实现重载;根据传入实参的不同,执行不同的操作
7、匿名函数:没有函数名的函数
1、自调函数:自己调用自己
语法:(function(){
//自己的操作
})();
特点:1、此函数只会执行一次,不能反复执行
2、其实此写法完全等效于写在全局的代码,但是函数中的东西会自动释放
建议:以后全局代码都在包含在自调之中
2、回调:回过头来再次调用,只要不是自调就是回调
以下都是回调:
d1.onclick=function(){}
str.replace(reg,function(){})
arr.sort(function(){})
{"eat":function(){}}
意味以后会遇到很多很多的回调函数
目的:认识哪些东西属于回调函数,并且等我们学习了箭头函数就牛逼
8、闭包
作用域链(scope chain)对象执行顺序以EC中scope chain属性为起点,经过AO逐级引用,形成的链式规则
作用域:查找变量
全局变量:优:可以反复使用;缺:随处可用,容易被污染
局部变量:优:仅内部可用,不会被污染; 缺:只能用一次
闭包的目的:保护一个可以反复使用的局部变量的一种词法结构
如何使用:3步
1、创建一个外层函数
2、外层函数里面创建一个受保护的变量
3、外层函数里面创建一个一个内层函数,专门操作受保护的变量,并且调用外层函数返回内层函数
*缺点:一个页面不要用的太多闭包,因为受保护的变量永远不能被释放,会导致内存泄露
*鄙视:1、外层函数调用几次,就创建了几个闭包,受保护的变量就有了几个副本(互不影响)
2、同一次外层函数调用,返回的内部函数,操作的都是同一个受保护的变量
注:形参相当于局部变量
真实开发时:防抖节流
公式:window.onresize=function(){
f2();
}
function f1(){
var timer=null;
return function(){
if(timer){clearTimeout(timer)}//false
timer=setTimeout(function(){
//真正要做的操作
},1000);
}
}
var f2=f1();
汇率:
function factory(rate){
return function(money){
return money*rate;
}
}
var rmb2$=factory(0.1463);
var rmb2E=factory(0.1098);
console.log(rmb2E(10000));
console.log(rmb2$(10000));
9、函数的原理:
0、程序加载时:
创建执行环境栈(ECS):保存函数调用顺序的数组
首页压入全局执行环境(EC)
全局EC引用全局对象window
window中将要保存全局变量
1、创建时
创建函数对象:封装函数的定义;在函数对象中定义scope属性:记录函数来自的作用域;
全局函数的scope都是window
3、调用前
在执行环境栈压入新的EC;创建活动对象AO:保存本次函数调用用到的局部变量
在EC中添加scope chain属性引用AO;设置AO的parent为函数的scope引用的对象
4、调用时
变量的使用规则:优先使用局部变量,局部没有才找全局
5、调用后
函数的EC出栈,AO自动释放,局部变量才会自动释放
4.2.3 对象Object
面向对象三大特点(封装、继承、多态)
1、常用api:
(1)、Object.keys(obj)
参数:返回一个数组,其元素是在对象上找到的可枚举属性
返回值:一个表示给定对象的所有可枚举属性的字符串数组
(2)、Object.values
Object.values()返回一个数组,其元素是在对象上找到的可枚举属性值
(3)、归属检测
in运算符能够检测左侧操作数是否为右侧操作数的成员
var o = { a : 1, b : function() {} }
console.log("a" in o); //返回true
console.log("b" in o); //返回true
console.log("c" in o); //返回false
console.log("valueOf" in o); //返回true,继承Object的原型方法
console.log("constructor" in o); //返回true,继承Object的原型属性
instanceof 检测数组 a 是否为 Array、Object 和 Function
var a = new Array(); //定义数组
console.log(a instanceof Array); //返回true
console.log(a instanceof Object); //返回true,Array是Object的子类
console.log(a instanceof Function); //返回false
(4) 、 Object.fromEntries() 方法把键值对列表转换为一个对象
常用将new Map()转换成普通Object
2、封装
1、面向对象和面向过程?
1、面向过程:过程:开始 -(过程)-> 结束,具有先后顺序(人的一生)
特点: - 难度低,不便于维护
2、面向对象:对象:属性和方法
属性:姓名,性别, 方法:吃饭,睡觉,
属性和方法必须包含一个对象中才有意义,可以将一个功能看做一对象,
2、创建自定义对象:3种
1、直接量方式:
var obj={
"属性名":属性值,
.....,
"方法名":function(){}
}
2、构造函数:var obj=new Object();//只会得到空对象
添加属性和方法:
obj.属性名=属性值;
obj.方法名=function(){};
问题:第一种和第二种创建方式都只适合创建单个对象,多个对象的话过于复杂,维护麻烦
3、自定义构造函数:
1、创建构造函数
function h52004(name,age,salary){
this.name=name;//this.name="王欢"
this.age=age;
this.salary=salary;
}
2、调用构造函数创建对象
var wh=new h52004("王欢",18,1000000);
强调:其实属性名和方法名的引号是可以省略的,但是不要省略
访问对象的属性和方法:
obj.属性名 === obj["属性名"]
obj.方法名() === obj["方法名"]()
强烈建议:使用.访问对象的属性和方法
JS中一切对象的底层都是hash数组:
特殊:1、访问到不存在的属性:返回undefined
2、可以随时随地的添加东西 obj.属性名=值;
3、this:在当前对象的方法内,引用对象自己的属性,可以使用this.属性名
引用对象自己的方法,可以使用this.方法名
指向:1、this只能出现在函数中,谁在调用此方法this指的就是谁
2、构造函数中this->正在创建的对象
3、单个元素绑定事件,this->这个元素
4、多个元素绑定事件,this->当前触发的元素
一定要看清this指向,活用var name=this来保留this
工作中何时创建对象:先封装对象,再调用对象的属性和方法,做出来我们需要的效果。
4、不管是哪种方式创建的对象,都可以使用for in遍历
for(var i in obj){
obj[i];
}
3、继承:父对象的成员(属性和方法),子对象可以直接使用
为什么继承:代码重用!节约内存空间
何时继承:只要多个子对象公用的属性和方法,都要集中定义在父对象之中
如何继承: 原型链概念
实例、构造函数、原型对象之间的关系
明确:
1、构造函数的prototype指向原型对象
2、原型的对象的constructor指向构造函数
3、实例的_proto会指向原型对象的prototype,所以可以逐级向上使用原型上的方法
document.__proto__.__proto__.__proto__.__proto__.__ .......
通过.__proto__可以逐级找到所有的祖先,可以使用祖先的东西
function Person(name) { this.name = name }
let p = new Person('Tom');
p.__proto__ --> Person.prototype
Person.__proto__ --> Function.prototype

1、如何找到父亲/原型:2种
1、构造函数名.prototype;//推荐:哪怕没有创建任何对象也可以找到原型
2、对象名.__proto__; //前提:必须要有一个对象才可以使用
3、最顶层的原型是谁:Object的原型,上面有一个方法叫toString,
作用域链:ec的scope chain属性 逐级引用AO,获取变量
原型链:document.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
通过.__proto__可以逐级找到所有的祖先,可以使用祖先的东西
2、可以再原型上设置共有属性或共有方法
原型对象.属性名=属性值
原型对象.方法名=function(){}
query[c]=d; c是变量
man.__proto__.xin_ff=function(){return this.name+"嘟嘟嘟"}
添加的都是共有的,儿子哪怕没有,也可以使用到
1、判断自有属性和共有属性:
1、判断自有:obj.hasOwnProperty("属性名");
true,说明"属性名"就是自有属性
false,有可能是共有也可能没有
2、判断共有:2个条件
1.obj.hasOwnProperty("属性名") 必须要为false
2、在原型链中进行检查:"属性名" in 对象名
公式:if(obj.hasOwnProperty("属性名")==false && "属性名" in 对象){
"共有"
}
完整的公式:注意(wsc是一个function)
if(wsc.hasOwnProperty("属性名")==false && "属性名" in wsc){
console.log("共有");
}else{
if(wsc.hasOwnProperty("属性名")==true){
console.log("自有")
}else{
console.log("没有")
}
}
2、修改或删除属性
1、修改或删除自有属性
修改:obj.属性名=新值
删除:delete obj.属性名;
2、修改或删除共有属性
必须找到原型再做操作 delete obj.属性名;
3、解决浏览器的一些兼容问题&可以为一类人添加方法
比如:1、解决老IE不支持arr.indexOf()
2、str.trim()
构造函数名.prototype.方法名=function(){}
4、判断一个对象是不是数组:4种
不能使用typeof因为typeof只能判断原始类型数据,引用类型区分不了得到结果都是object
1、Array.isArray(x); //true说明是数组,false不是数组,ES5的新方式,老IE不支持
2、判断当前对象是否继承自Array.prototype
Array.prototype.isPrototypeOf(x);
判断两者之间是否存在继承关系:
父.isPrototypeOf(子);
3、x instanceof 构造函数
判断x是否是由构造函数创建的 arr instanceof RegExp
4、输出Object的字符串形式:
在Object的原型之上保存着最古老的toString
原始的toString,默认输出[object 构造函数]
使用:Object.prototype.toString.apply(now) 推荐
多态:子对象觉得父对象的属性/方法不好,可以在本地定义同名成员,覆盖父对象的成员
比如:arr.toString();//1,2,3,4,5
obj.toString();//[object Object]
3、自定义继承:
为什么:其实程序会自动设置继承,但是有的时候不符合现实生活
1、两个对象之间继承
子对象.__proto__=父对象
2、多个对象之间实现继承
构造函数.prototype=父对象
时机:应该在开始创建对象之前!
4.2.4 正则表达式RegExp.
正则表达式:定义字符串中字符出现规则的表达式
何时:验证 替换replace 切割split
语法:/正则表达式/
1、匹配的位置:只要是做验证绝对需要它
/^ 严格匹配 $/ reg.test(str)
2、最简单的正则:关键字原文 "原文"==/原文/
/原文/gi:g:global全局/全部 m:执行多行匹配 i:不区分大小写
3、自定义备选字符集:规定一位字符可用的备选字符列表
如何:/[备选字符列表]/
注意:1、一个中括号,只能匹配一位字符
2、正则表达式默认只要满足了就不管后续的
解决:只要是做验证就必须要求完全匹配的我的要求。/^[备选字符集]$/
3、如果备选字符集中有连续的字符(ascii码),可用-省略中间的字符
一位数字:[0-9]
一位字母:[A-Za-z]
一位数字,字母或下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]
除了xxx之外的,其他都行:[^xxx],范围太广了
4、预定义字符集:专门对常用的字符集做了简化操作
一位数字:\d => [0-9]
一位字母,数字或下划线:\w => [0-9A-Za-z_]
一位除了字母数字下划线:\W
一位空白字符:\s 包括:空格,换行,制表符
一位除了换行外的任意字符:. 范围太广了
优先使用预定义字符集,满足不了再用自定义备选字符集补充
5、量词:规定一个字符集出现的次数
1、有明确数量
{n,m}:前边相邻的字符集,最少出 现n次,最多出现m次
{n,}:前边相邻的字符集,最少出现n次,多了不限
{n}:前边相邻的字符集,必须出现n次
2、没明确数量
?:可有可无,但最多一个
*:可有可无,多了不限
+:至少一个,多了不限
6、选择和分组;一般搭配使用
(规则1|规则2);
选择:在两个规则中二选一匹配规则1|规则2 分组:将多个字符集临时组成一组子规则
7、预判公式
(?![xxxxx]+$),表示除了。。。格式之外
如:密码强度验证:4位字母数字的组合,至少一位大写字母和一位数字和小写字母
/^(?![a-zA-Z]+$)(?![0-9a-z]+$)[0-9A-Za-z]{4}$/
8、单词边界
\b 单词开头结尾的空格
9、正则对象:验证
创建正则对象:
1、直接量方式:var reg=/正则表达式/;
2、构造函数方式:var reg=new RegExp("正则表达式","后缀");
方法:
RegExp.exec 返回找到的值,并确定其位置。
RegExp.test 返回 true 或 false。
RegExp.toString 返回正则表达式的字符串。
10、string API:支持正则表达式的API:
1、切割:var arr=str.split("切割符"/reg);
切割符可以是不固定的
2、***替换:
基本替换操作:
str=str.replace(str/reg,"**")
强调:1、replace不修改原字符串,只能返回新的
2、reg一般都要加g,如果不加g,只替换第一个匹配到的关键字
高级替换操作:
str=str.replace(str/reg,function(a){
//a自动获取到你正则匹配到了关键字
return "**"
})
替换中出现了分组():那么可以格式化
str=str.replace(str/reg,function(a,b,c...){
//a自动获取到你正则匹配到了关键字
//b第一个分组匹配到的关键字
//c第二个分组匹配到的关键字
return "**"
})
4.2.5 Math对象:不需要创建,由浏览器JS解释器自动创建
属性:Math.PI - 以后不需要自己创建PI
1、取整:parseInt() >>>0 ^0
上取整:只要超过了,就取下一个正数
Math.ceil(num); //小数位数不能超过15位,方法就失效了,变为普通取整
下取整:无论超过多少都省略小数部分
Math.floor(num); //小数位数不能超过15位,方法就失效了,变为向上取整
四舍五入取整:
Math.round(num);
问题:四舍五入取整但是只能取整,不能保留小数位数
开发中解决方式:var num=parseFloat(num.toFixed(d)); //d:保留的小数位数,d只能是0~100之间
鄙视中解决:要求,不能使用toFixed(d);
function round(num,d){
num*=Math.pow(10,d);
num=Math.round(num);
num/=Math.pow(10,d);
return num;
}
2、乘方:Math.pow(底数,幂); //底数和幂都可以自己设置
Math.pow(8,4);//8*8*8*8
开方:Math.sqrt(num);//只能开平方
3、最大值:var max=Math.max(a,b,c,d...);
最小值:var min=Math.min(a,b,c,d...);
问题:传入的是一个数组,返回是一个NaN
解决:var max=Math.max/min.apply(Math,[a,b,c,d...]);
4、绝对值:把负数转为正数
Math.abs(num); 相当于 * -1
5、随机整数:parseInt(Math.random()*(max-min+1)+min);
其他API:e 和 三角函数
4.2.6 Date对象:日期
只要计算时间,必须使用Date,可以自动进制
1、创建:5种
1、*创建当前时间:var now=new Date();
2、*创建自定义时间:var birth=new Date("yyyy/MM/dd hh:mm:ss");
3、创建自定义时间:var birth=new Date(yyyy,MM,dd,hh,mm,ss);
注意:月份需要修正,从0开始的
4、复制日期:var end=new Date(now);
为什么:因为日期的所有API都是要修改原日期对象的
5、var now=new Date(1000);//本质:日期就是保存的毫秒数
2、计算:
1、两个日期之间可以相减,得到一个毫秒差,换算出来任何一部分
2、单个日期对象可以操作自己的任何一个分量:
分量:年月日星期:FullYear Month Date Day
时分秒毫秒:Hours Minutes Seconds Milliseconds
API:每一个分量都有一对儿getXXX/setXXX
特殊:Day没有set方法:
问题:date+3年
date.setFullYear(date.getFullYear()+3);
3、取值范围:
FullYear:当前的年份
Month:0~11
Date:1~31
Day:0 1 2 3 4 5 6
hours:0~23
minutes,seconds:0~59
3、格式化:转为字符串,则不能使用日期API了
date.toLocaleString(); // 注意:此法存在兼容性问题,相同的截取字符串,出来的结果却是不一样的
解决:自己定义一个format方法,来完成格式化
function format(Date){
var arr=["日","一","二","三","四","五","六"]
var y=Date.getFullYear();
var m=Date.getMonth()+1;
var z=Date.getDay();
var h=Date.getHours();
var M=Date.getMinutes();
if(M<10){M="0"+M}
var s=Date.getSeconds();
if(s<10){s="0"+s}
return y+"年"+m+"月"+z+"日 "+h+"时"+M+"分"+s+"秒"+" 星期"+arr[z];
}
4.2.7 Error:错误对象封装了错误的信息,发生错误的时候自动创建
1、js中错误对象的类型:6种
1、SyntaxError:语法错误 - 往往都是符号错误
2、ReferenceError:错误引用 - 没有创建就直接使用了
3、TypeError:错误类型 - 不是你的方法,你却在使用
4、RangeError:范围错误 - toFixed(d),d范围0-100之间
EvalError、URIError - 客户端是不会见到的
2、自定义错误,并且抛出:throw new Error("自定义错误信息")
3、问题:不管预定义错误还是自定义的错误,都会打断后续代码
解决:即使出现了错误,也不要报错,而给一个提示比较好,才能保证后续代码执行(错误处理)
关键:要防用户 ①垃圾处理方式:
语法:try{
//可能出错的代码
}catch(err){
//出错了才会执行
}
五、运算符和表达式
5.1 算数运算:+ - * / %
隐式转换:默认都转为数字(Number())在运算 特殊:+运算,只要碰上一个字符串,就两边都转为字符串(String()),+运算变成一个连接符
%:m%n,取余:
何时:1、取出某个数字的后n位
1234 % 10 -> 4
判断奇偶性:
num % 2 :结果为0说明偶数,结果为1说明是奇数
控制一个运算的结果,不能超过指定值
num % 5 :结果永远不会到5
5.2 关系运算:作比较:> < >= <= == != === !==
返回结果:bool值:true false 隐式转换:默认一切转为数字,再比较大小
特殊:
1、参与比较的两个值都是字符串
会按位PK每个字符的十六进制unicode号(ascii码十进制)
直到比出结果位置,通过提供的ascii表:0-9 A-Z a-z,中文不需要记忆
2、NaN不大于,不小于,不等于任何值
问题:没有办法通过普通的比较运算去判断是不是NaN
解决:!isNaN(num):num是一个普通数字则为true
num是一个NaN则为false
3、null 和 undefined:
问题:null==undefined -> true 区分不开
解决:全等:===:数值相同 并且 数据类型也要相同,不再带有隐式转换
!==:不带有隐式转换的不等比较
5.3 逻辑运算:&& || !
返回值:bool值
其中:
&&:只有两个条件都为true,结果才为true
只要有一个条件为false,结果则为false
||:只要有一个条件为true,结果则为true
只有两个条件都为false,结果才为false
!:颠倒bool值
短路逻辑:只要前一个条件已经可以得出最终结论,则后续条件不再执行
&&:实现简单的分支:if(条件){操作}===条件&&(操作);
5.4 三目运算:
简化了if...else...结构:条件?操作1:默认操作;
简化了if...else if...else:条件1?操作1:条件2?操作2:默认操作;
d3.style.display=this.innerHTML=="<<"?"none":"block";
this.innerHTML=this.innerHTML=="<<"?">>":"<<"
何时使用三目:操作简单的时候,操作只有一句话的时候就使用,特殊:默认操作不能省略
何时使用if:操作复杂,有多句操作的时候
5.5 位运算:
左移:m<<n,读作m左移n位,相当于:m*2的n次方,底数固定为2
右移:m>>n,读作m右移n位,相当于:m/2的n次方,底数固定为2
取整:m>>>0 m^0
5.6 扩展赋值运算:一句话执行两个操作:先运算,再保存
何时使用:只要取出变量的值做计算,之后还要在保存回去的时候,千万不能这么写:i=i+1;
+= -= *= /= %=
比如:total=total*0.8 --> total*=0.8;
m=m+n --> m+=n ; 累加
m=m*n -->m*=n ; 累乘
递增,递减:i++ i--
i++ --> i+=1 --> i=i+1; //++只能固定的+1
***单独使用,不管是前++还是后++,变量中的值都加了1
参与了其他表达式,变量中的值都被+1
前++,返回递增后的--新值
后++,返回递增前的--旧值
***鄙视题:
var i=2;
var sum=i++ + ++i + i++ + ++i;
拓展:1、var str=num.toFixed(n);//对num按n位进行四舍五入,但是返回的是一个字符串,用于解决计算机带来的舍入误差
2、var code=str.charCodeAt(0);//把str的第一个字取出来转换出他的ascii码
六、Dom
avascript:3部分组成(ES3/5/6(内功心法)+DOM(操作网页)+BOM(操作浏览器))
- 1、DHTML概念:D:Dynamic:动态的 动态的HTML,不是新技术、新语言、新标准、新规范,是三个技术的整合统称:HTML+CSS+JS(dom)
- 2、DOM介绍:Document Object Model
文档 对象 模型:专门用于动态操作网页的HTML和css - 3、DOM树: DOM将HTML看做了一个倒挂的树状结构
其实我们的树根是document对象(属性和方法),不需要我们创建,由浏览器的JS解释器自动创建,一个页面只有一个document 在HTML文档中,所有的元素、属性、文本、注释都会被视为DOM树的一个节点
DOM树根:document对象,把我们页面上任何一个内容都视为了一个节点/元素/对象(元素、文本、属性、注释...)
每个节点都有三大属性:
1、elem.nodeType:获取节点的类型;判断你这个节点:是不是元素节点
document节点:9;
element节点:1;
文本节点:3;
属性节点:2
2、nodeValue:获取属性节点的值——垃圾
3、nodeName:获取节点的名称;只不过返回的都是大写的标签名
作用:判断标签是什么标签
W3C 定义DOM标准:3部分
- 1、核心DOM:可以操作HTML和XML,万能的,一些方法(API)会比较繁琐
- 2、HTML DOM:只能操作HTML,简单
- 3、XML DOM:只能操作XML,过时了 总结:开发中优先使用:HTML DOM,更简单,如果HTML DOM操作不了的,再用核心DOM补充
6.1 获取元素
6.1.1 通过 HTML 获取元素:
1 通过ID:
语法:var elem=document.getElementById("id值");
返回:保存住对应的id值的元素,称之为 DOM元素/DOM节点/DOM对象,只能单个元素才能执行操作
强调:1、id如果出现重复的,只会找到第一个
2、此方法不建议使用
3、单个元素如果没找到返回null
2 通过 标签名 获取元素:
语法:var elems=document/node.getElementsByTagName("标签名") [ ];
返回:类数组(可以使用下标和length),是一个集合,不能直接做操作,必须使用下标获取某一个 或者 使用遍历获取每一个
强调:1、没找到返回[ ]; 2、node的意思:你之前已经找到的某个节点
3 通过 class名 获取元素:
语法:var elems=document/node.getElementsByClassName("标签名") [ ];
返回:类数组(可以使用下标和length),是一个集合,不能直接做操作,必须使用下标获取某一个或者使用遍历获取每一个
强调:1、没找到返回[ ]; 2、node的意思:你之前已经找到的某个节点
4 通过节点之间的关系 获取元素:
使用前提:必须至少 先获取到一个元素
1、父元素:xx.parentNode; //返回单个元素,可以直接做操作
2、儿子:xx.children; //返回集合,不可以直接做操作
3、前一个兄弟:xx.previousElementSibling; //返回单个元素,可以直接做操作
4、后一个兄弟:xx.nextElementSibling; //返回单个元素,可以直接做操作
5、第一个儿子:xx.firstElementChild; //返回单个元素,可以直接做操作
6、最后一个儿子:xx.lastElementChild; //返回单个元素,可以直接做操作
6.1.2 通过css选择器获取元素:
1 var elem=document.querySelector("任意css选择器,如# .");
强调:万一匹配到多个元素,只会返回第一个
2、var elems=document.querySelectorAll("ul:nth-of-type(2)>li");
强调:返回一个集合,没找到返回空集合
面试题:getXXX vs querySelectorXXX 哪个好
1、复杂查找时querySelectorXXX使用更加的舒适
2、getXXX 返回的是一个动态集合:查询结果快,页面有变化就会重复检索DOM树
querySelector 返回的是一个非动态集合:查询结果慢,不会重复检索(只会查找一次)
总结:查找元素:
1、通过document.getXXXX
2、通过document.queryXXX
3、通过关系
4、递归(层级不明确)
6.2 操作元素
6.2.1 元素的内容:
1 xx.innerHTML - 能够识别标签
获取:xx.innerHTML 获取的目的往往都是再做一个判断比较
设置:xx.innerHTML="内容"
2 xx.innerText - 不识别标签
获取:xx.innerText 获取的目的往往都是再做一个判断比较
设置:xx.innerText="内容"
3 elem.textContent:获取或者设置元素开始标签到结尾标签之间的文本内容
获取:elem.textContent
设置:elem.textContent="新HTML";
兼容:老IE:elem.innerText; //随着时代的变化,反而innerText变为人人都能用,小三上位了
4 单标签的内容:
获取:elem.value
设置:elem.value="新值";
6.2.2 元素的属性:操作其实是HTML属性(id/class/title/href/alt/style...)
1 核心DOM:
1、获取属性值:xx.getAttribute("属性名"); - 获取的目的往往都是再做一个判断比较
2、设置属性值:xx.setAttribute("属性名","属性值")
2 HTML DOM:
1、获取属性值:xx.属性名 - 获取的目的往往都是再做一个判断比较
2、设置属性值:xx.属性名="属性值";
1、HTML DOM设置class必须写为className
2、不能操作自定义属性,只能操作标准属性
3 删除css属性:
elem.removeAttribute("属性名");
4 判断有没有,并不能判断值是什么
var bool=elem.hasAttribute("属性名");
6.2.3 添加单个元素
1、创建空元素:var elem=document.createElement("标签名div");
2、为元素添加必要的东西(属性、事件)elem.innerHTML="内容"
3、渲染到DOM树之上:3种
*1、父.appendChild(新); //新元素追加到父元素中,而且是最后一个儿子之后,不会影响到别人的下标
2、父.insertBefore(新,已有子元素);//新元素追加到已有子元素之前
3、父.replaceChild(新,已有子元素);
注意:此方式只适合添加单个元素,DOM树渲染的次数越多,页面的效率越低下
6.2.4 添加多个元素
1、创建文档片段:var frag=document.createDocumentFragment();
//临时的父元素:真的渲染页面上过后就不会再出现了,会自动释放
2、把你创建的元素都先放到frag之中
frag.appendChild(新);//把散着的元素都放在了frag之中成为了一个整体
3、最后再把frag渲染到页面之上
bd.appendChild(frag);//只渲染了真实DOM一次
6.2.5 删除元素:
1、elem.remove();//elem会被删掉
2、父.removeChild(子);//子会被删掉
七、BOM
BOM:Browser Object Model 浏览器对象模型,操作浏览器
含:window history navigator location screen event
7.1 window对象:扮演两个角色
7.1.1 代替ES中Global充当了全局对象,指代当前的窗口
网页打开新连接的方式:4种
1、替换当前页面,可后退
HTML:<a href=""></a>
JS:window.open("url","_self");
2、替换当前页面,不可后退:比如购物车结账后不可后退
JS:location.replace("新url");//替换了当前的url,并不叫跳转,不会生成历史记录
3、在新窗口打开,可以打开多个
HTML:<a href="" target="_blank"></a>
JS:window.open("url","_blank");
4、在新窗口打开,只能打开一个:结账界面
HTML:<a href="" target="自定义"></a>
JS:window.open("url","自定义");
自定义,如果重新打开相同名字的窗口,新窗口会覆盖掉旧的窗口
7.1.2 window对象:
属性:
1、完整的浏览器的大小:outerWidth outerHeight
2、浏览器文档显示区的大小:innerWidth innerHeight
方法:
1、关闭窗口:close();
2、开启新窗口:var newW=open("url","target","width=,height,top=,left=");
3、改变新窗口的位置:newW.moveTo(x,y);
网页可见区域宽: document.body.clientWidth;
网页可见区域高: document.body.clientHeight;
网页可见区域宽: document.body.offsetWidth (包含滚动条的宽);
网页可见区域高: document.body.offsetHeight (包含滚动条的高);
网页正文全文宽: document.body.scrollWidth;
网页正文全文高: document.body.scrollHeight;
网页被卷去的高: document.body.scrollTop;
网页被卷去的左: document.body.scrollLeft;
网页正文部分上: window.screenTop;
网页正文部分左: window.screenLeft;
屏幕分辨率的高: window.screen.height;
屏幕分辨率的宽: window.screen.width;
屏幕可用工作区高度: window.screen.availHeight;
屏幕可用工作区宽度:window.screen.availWidth;
1、offsetWidth width+padding+border) 当前对象的宽度。
2、offsetHeight :(Height+padding+border) 当前对象的高度。
3、offsetLeft :当前对象到其上级层左边的距离。
4、offsetTop :当前对象到其上级层顶部边的距离。
5、scrollWidth:获取对象的滚动宽度 。
6、scrollHeight: 获取对象的滚动高度。
7、scrollLeft:设置或获取位于对象左边界和对象中目前可见内容的最左端之间的距离(width+padding为一体)
8、scrollTop:设置或获取位于对象最顶端和对象中可见内容的最顶端之间的距离;(height+padding为一体)
9、clientWidth: 获取对象可见内容的宽度,不包括滚动条,不包括边框;
10、clientHeight: 获取对象可见内容的高度,不包括滚动条,不包括边框;
11、clientLeft: 获取对象的border宽度
12、clientTop:获取对象的border高度
13、offsetParent :当前对象的上级层对象.
获取:鼠标的位置
e.pageX
e.pageY 页面尺寸,用的最多
e.screenX
e.screenY
e.clientX
e.clientY
7.2 可视区域 scrren:屏幕,作用:获取屏幕的大小
属性:完整的屏幕大小:screen.width screen.height
去掉任务栏的大小:screen.availWidth screen.availHeight;
7.3 history:历史记录:当前窗口打开过的url的记录
操作:
前进:history.go(1);
后退:history.go(-1);
刷新:history.go(0);
7.4 location:保存正在打开的url(不采用a标签实现跳转)
跳转:location="http://www.jd.com";
location.href="http://www.jd.com";
location.assign("http://www.jd.com");
主机、域名:location.host
锚点:location.hash
域名:location.origin
请求路径:location.pathname
端口:location.port
协议:location.protocol
url中请求参数:location.search
跳转后禁止后退:location.replace("新地址");
7.5 navigator:保存了当前浏览器的基本信息
1、判断是否开启了cookie
navigator.cookieEnabled
true:说明cookie是开启的
false:说明cookie是关闭的
作用:提示用户有没有开启cookie
2、判断当前浏览器是什么浏览器以及版本号
var str=navigator.userAgent;
截取到你想要的部分
常识:一个url由哪些部分组成?
1、协议:http/https/ftp/ws
2、域名/主机号:127.0.0.1/www.baidu.com
3、端口号:http默认端口80 https默认端口443,默认端口可以省略不写
4、相对文件路径:/bom02/new/03location对象.htm
5、查询字符串:往往都是跟用户提交的东西相关
7.6 event:
事件:用户手动触发的 或 浏览器自动触发的
7.6.1 创建事件:3种
1、在HTML中添加事件
<elem on事件名="fn()"></elem>
缺点:1、内容和行为没有分离
2、无法动态绑定 - 一次只能绑定一个元素
3、无法绑定多个事件函数
2、在js中添加事件属性绑定
elem.on事件名=()=>{}
特点:无法绑定多个事件函数 - 这其实也算不上什么缺点
没有兼容性问题
3、使用专门的API绑定事件:
主流:elem.addEventListener("事件名",callback);
老IE:elem.attachEvent("on事件名",callback);
兼容:if(elem.addEventListener){
elem.addEventListener("事件名",callback);
}else{
elem.attachEvent("on事件名",callback);
}
本质上没有任何缺点,需要做兼容
7.6.2 移除事件绑定:
elem.removeEventListener("事件名",fn);
elem.onclick=null(一次性事件);
7.6.3 事件对象:
创建事件对象:其实主流浏览器事件函数中的第一个形参会自动获得事件对象,但是老IE只能用window.event获取 兼容:e=e||window.event;
1、获取鼠标的位置:
2、取消浏览器的默认行为:1、a标签 2、form标签中submit 3、右键(比如游戏)
主流:e.preventDefault();
老IE:e.returnValue=false;
兼容:if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue=false;
}
3、阻止冒泡
事件周期:3个
1、捕获阶段:由外向内,记录所有要执行的事件
2、目标触发:目标元素优先触发
3、冒泡阶段:由内向外,依次冒泡触发
如何阻止冒泡:
1、获取事件对象
2、主流:e.stopPropagation();
老IE:e.cancelBubble=true;
兼容:if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble=true;
}
如果只使用stopPropagation是只能阻止事件冒泡至其父节点,而stopImmediatePropagation既能阻止事件冒泡至父节点,也能阻止当前节点上其他同类型事件的触发。
4、事件委托/利用冒泡:
1、获取事件对象
主流:e.target;
老IE:e.srcElement;
e=e||window.event;
兼容:var target=e.target||e.srcElement;
获取元素名:event.target.nodeName==("大写")
获取id:event.target.id
获取text:event.target.textContent
获取class:event.target.className
获取value:event.target.value
完美的代替以前的this,并且可以使用箭头函数简化事件回调
从此以后都只能使用事件委托
总结:this的指向
1、单个元素绑定事件,this指定就是这个元素
2、多个元素绑定事件,this指的是触发的当前元素
以上两种都不用了,被target代替了,事件中不需要this了
3、定时器中this->window
4、箭头函数this->外部的对象
5、函数中this->当前调用的人
八、ES6
ES6是一个广义的概念,包括并不限于ES6、ES7、ES8、ES9、ES10等的一个统筹概念
8.1 保护对象:面向对象开发,需要保护对象,因为如果不保护,那么我们的对象可以被修改和删除
如何保护对象:
对象中的一个属性拥有4大特性:
{
configurable: true,//1、可否被删除 2、总开关 3、不可逆
enumerable: true,//可否被for in循环遍历到
value: 1001,//真正保存值的地方
writable: true,//可否修改
}
1、修改一个对象的属性的四大特性:
Object.defineProperties(obj,{
"属性名":{四大特性},
...
})
2、三种级别:
防扩展:Object.perventExtensions(obj); //防添加
密封:Object.seal(obj);//防添加和删除
冻结:Object.freeze(obj);//防添加和删除和修改
8.2 数组API
8.2.1 判断:返回的结果一定是一个布尔值
every:每一个,类似于&&,每一个都满足条件结果才为true
var bool=arr.every((val,i,arr)=>{
return 判断条件;
})
some:有一些,类似于||,有一个满足条件结果就为true
var bool=arr.some((val,i,arr)=>{
return 判断条件;
})
8.2.2 遍历:对每个元素执行相同 或 相似的操作
forEach:直接修改原数组
arr.forEach((val,i,arr)=>{
操作
})
map:只会返回新数组
var newArr=arr.map((val,i,arr)=>{
return 操作
})
8.2.3 汇总和过滤:
filter过滤:和现实生活有点区别:不会修改原数组
var subArr=arr.filter((val,i,arr)=>{
return 判断条件,满足true的数据被返回;
})
(1)、清除数组中的null、undefined
list = list.filter(n => n)
//清除对象中的null、undefined
const DATA = _.omitBy(params, _.isNil);
(2)、hash数组去重
const beforeData = [{id:1,name:'1'},{id:1,name:'2'},{id:2,name:'2'}]
const lst = new Set()
const filters=beforeData.filter(item => !lst.has(item.id) && lst.add(item.id))
(3)、删除一行数据的方法
handleDelete = (key: React.Key) => {
const dataSource = [...this.state.dataSource];
this.setState({
dataSource: dataSource.filter(item => item.key !== key)
});
};
reduce汇总:接收一个函数作为累加器,数组中的每个值从左到右开始合并
var subArr=arr.reduce((prev,val,i,arr)=>{
return 累加结果;
},100)
const list = data.reduce((a: YouCliptListT[], b: CliptListType) => {
a.push({
label: b.name,
value: b.id,
});
return a;
}, []);
8.2.4 查找与查找下标:
find (返回数组中满足提供的测试函数的第一个元素的值)
let arr = [1, 2, 3, arr , 5, 1, 9];
arr.find((value, keys, arr) => {
return value > 2;
}); // 3 返回匹配的值
findIndex返回第一个符合条件的数组成员的下标,都不符合返回-1
findIndex比indexOf更强大一些,可以通过回调函数查找对象数组,indexOf可以指定开始查找位置的索引
[1, 5, 10, 15].findIndex((value, index, arr)=>{
return value > 9;
}) // 2
8.3 Object.create():直接用父对象创建子对象,并且扩展子对象的自有属性
let child=Object.create(father,{
"自有属性名":{四大特性},
...
});
8.4 call、apply、bind
call/apply:临时替换函数中的this,类似于借用
区别:call,要求传入的参数必须单独传入
apply,要求传入函数的参数必须放在一个数组之内传入,apply会自动打散数组
强调:call/apply相当于立刻调用函数
语法:函数名.call/apply(x,实参,...)
用于:对象调用函数
bind:永久替换的函数中的this,类似于买
1、创建一个和原函数功能完全一样的新函数
2、将新函数中的this永久绑定为指定对象
3、将新函数中的部分参数可以提前永久绑定
强调:bind绑定在新函数中的this,无法被call/apply再替换,不是立刻执行
语法:var 新函数=函数名.bind(x);
总结:如果临时调用一个函数,立刻执行时,使用call/apply
如果创建一个函数提前绑定this,不一定立刻执行,使用bind
固定套路:将类数组转为普通数组
公式:lis=Array.prototype.slice(lis);
8.5 严格模式:开启严格模式:"use strict";
可以再任意作用域顶部使用此语句
1、禁止给未声明的变量赋值:a=1;//报错
2、静默失败升级为错误:以前失败的操作并没有报错,现在会升级为错误
8.6 模板字符串:简化了字符串的拼接
语法:反引号``
具体使用:`我的名字叫${name}`;
8.7 块级作用域:
语法:let 替换以前的 var
何时:优先使用let
特点:2个
1、let之前不允许在出现未声明的同名变量 - 阻止了声明提前
2、添加了块级作用域 - {}就叫一个块
8.8 箭头函数:简化了 回调函数(无名且不自调)
去掉function,在()和{}之间添加=>
如果形参只有一个,则省略()
如果函数体只有一句话,可以省略{},如果是return,return和{}都省略
强调:箭头函数中不能使用this -> 外面的对象,所以事件流函数暂不推荐使用
8.9 for...of
遍历:只能遍历索引数组,只能返回新数组,不能修改老数组
语法:for(var v of arr){
v//当前值
}
8.10 解构赋值
作用:赋值的方式进行了增强
写法:
1、数组解构赋值
var [a,b,c]=[1,2,3];
2、对象解构赋值
var {a,b=默认值,c}={c:3,a:1}
3、函数中,传参数的顺序是可以随意的
function zwjs({name,age,hobby="迟到"}){
console.log(`${name}今年${age}岁喜欢${hobby}`);
}
zwjs({age:30,hobby:"吃饭",name:"袁洪"});
4、return 可以返回多个变量
function f1(){
var a=18;
var b=20;
return [a,b];
}
var [a,b]=f1();
可用于交换变量[a,b]=[b,a]
8.11 Set:新的数据类型,和数组非常的类似,但是没有length,用size获取自己的长度
去重: [...new Set(Arr)]
添加元素add
let list=new Set();
list.add(20).add(3).add(3) // 2只被添加了一次
list.has(20)//true
list.forEach((value, key) => console.log(key + ' : ' + value))
list.delete(20) //删除值为30的元素,这里的30并非下标
list.clear() //清空
8.12 Map:新的数据类型,和对象非常的类似,也可以设置值,也可以读取值 -- 保存一些数据
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
m.forEach((value, key, map)=> {
console.log();
});
Object.fromEntries() 方法把键值对列表转换为一个对象。
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }
8.13 可选链 ?.
可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。 前面的内容在属性的上层对象是否存在,是否为null 或者 undefined,当不存在即返回 undefined,如果存在,继续向后调用
const firstName = message?.body?.user?.firstName
8.14 空值合并运算符 ??
空值合并操作符( ?? )是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。
const foo = null ?? 'default string';
console.log(foo); // "default string"
const baz = 0 ?? 42;
console.log(baz); // 0
8.14 逻辑运算符
es6 新增的逻辑运算符||=、&&=、??= ,这三个运算符相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。
8.14.1 或(OR)逻辑运算符 ||=
逻辑OR赋值(x ||=y)运算符仅在 x 为 falsy时赋值。
const b = { a : 2 } ;
// b.a 左侧返回2,非false ,结果返回 2
b.a ||= 4 ;
console.log(b.a) ; // 2
// b.d 左侧b的原型链不存在d,返回false,向右赋值,结果b.d赋值为8
b.d ||= 8 ;
console.log(b.d) ; // 8
8.14.2 逻辑(AND)赋值运算符 &&=
逻辑 AND 赋值 (x &&= y) 运算符仅在 x 为真时赋值。
let a = 1;
let b = 0;
// 此时运算符左侧为真,赋值后返回结果 2
a &&= 2;
console.log(a); // 2
// 此时运算符左侧为假,不进行赋值
b &&= 2;
console.log(b); // 0
8.14.3 逻辑空赋值 ??=
逻辑空赋值运算符 (x ??= y) 仅在 x 是 (null 或 undefined) 时对其赋值。
// 首先来一个对象
const a = {b : 2, c : 0, d : false} ;
// 使用 ??= 来赋值,结果是可以添加到 a 对象上的
a.f??=100 ;
console.log(a) ; // {b: 2, c: 0, d: false, f: 100}
// 新的写法(简洁不少,而且通俗易懂):
function example(params) {
params?.loading ??= false
params?.okText ??= '确定'
}
8.15 求幂运算符 **
求幂运算符 返回将第一个操作数加到第二个操作数的幂的结果。它等效于Math.pow,不同之处在于它也接受BigInts作为操作数。
Math.pow(3,4) // 81
console.log(3 ** 4); // 81
console.log(10 ** -2); // 0.01
console.log(2 ** 3 ** 2); // 512
console.log((2 ** 3) ** 2); // 64
8.16 class:用于简化面向对象(封装、继承、多态)
class flyer{
constructor(name,speed){//自有属性
this.name=name;
this.speed=speed;
}
//共有方法
fly(){
return `${this.name}正在以时速${this.speed}进行移动`;
}
}
class plane extends flyer{//继承自flyer、
constructor(name,speed,rl){//自有属性
super(name,speed);//调用了flyer的constructor
this.rl=rl;
}
fly(){
return `${this.name}正在以时速${this.speed}进行移动,牛逼
}
}
8.17 Promise承诺;异步操作队列化,按照期望的顺序执行,返回符合预期序
8.17.1 promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例 参数:数组
这个Promise的resolve是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有了promise的时候执行。它的reject是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。
const p = Promise.all([p1, p2, p3]);
8.17.2 promise.any()返回第一个完成得promise值,参数:数组
const p = Promise.all([p1, p2, p3]);
8.17.3 promise.catch() 返回一个Promise,并且处理拒绝的情况。
8.17.4 Promise.race 当promise数组中任意一个promise被拒绝或者成功
Promise.race([]).then(res => {}).catch(err => {});