Javascript学习随笔
想要运行js需要运行环境:
- 浏览器自带js解释器
- node.js需要安装环境
编译型:在程序正式运行之前,会检查有没有报错,如果有报错直接不运行,比如Java、c++、c#...(严格的)
解释型:在程序正式运行之前,不会检查有没有报错,直接运行,碰到错误后就会停止,比如:javascript、node.js、php(自由的)
弱类型:在创建一个变量时,想放什么数据类型的数据就放什么(非常自由)
强类型:在创建变量之前,必须先规定,此变量可以保存什么数据类型,才能往里面放入对应的数据类型的数据(严格)
面向对象(万物皆对象):一切对象都有两个对象(属性和方法)
对象名.属性名
对象名.方法名()
一切一阶段做不到的事,都靠二阶段 原生js(轮播图,购买商品数量的加减....)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新的一天:加油哦!</title>
<script type="text/javascript" src="../js/one.js"></script>
</head>
<body>
<script type="text/javascript">
</script>
</body>
</html>
- 引入方式
- 打桩输出
- 注释
- 变量和常量
- 数据类型
- 数据类型转换
- 运算符
- ascii码
- 符号的优先级
- 分支语句
- 循环
- function函数对象
- Object对象
- Array数组对象
- String 字符串对象
- RegExp正则对象
- Math数学对象
- Date日期对象
- Error错误对象
- DOM:Document Object Model:文档对象模型,专门用于操作html文档的,提供了一些方法
- ES5
- ES6(ES2015)
- BOM:Browser Object Model(浏览器对象模型)
- window对象
- history对象
- navigator对象
- *location对象
- event: 事件对象
- ES7:
- ES8:
- ES9:
- ES10:
- ES11:
- ES12:
- ES13 :
引入方式
- 直接在html上写一个script的双标签,里面书写js代码 - 临时测试玩法
- 在外部创建有个js文件,在其中写入js代码,html进行引入 - 正式开发玩法
<script src="路径">//此处不可以在写代码</script>
打桩输出
作用:检测代码是否有问题,找错,找bug
- 在控制台输出:console.log(想要输出的对象) //console控制台 log日志
- 在页面上输出:document.write(想要输出的东西) //document文档 write写入 能够识别标签;但是是个垃圾,如果搭配上点击事件,会替换原页面的内容。
- 弹出一个小框:alert(想要输出的内容) //垃圾:卡住html页面,用户不关掉,就只能看白板
- 在页面上弹出提示和用户输入框:prompt(提示文字) 卡住html页面,用户不关掉,就只能看白板
- 确认框confirm() 返回true和false
注释
作用:方便后期维护
- 单行注释 //
- 多行注释
/*内容*/
变量和常量
硬盘:外部存储器:外存,保存文件/数据
CPU:中央处理器,计算速度
内存:内部存储器,保存的是应用在执行过程中,所需要的一些数据(变量就是一个内存),我们取的变量名就是一个内存地址
变量var
可以保存数据,如果数据繁琐,以后可以使用变量名,相当于就是在使用变量的值;变量顾名思义,值以后可以改变。
-
语法:var 变量名=变量值;
-
特殊:
- 变量名不是随意的
- 不能数字开头,可以数字结尾
- 见名知意,驼峰命名
- name是一个关键字,不管你保存的数据类型是什么,最后都会悄悄地变成一个字符串
- 千万不要拿着关键字当变量名
- 可以不保存数据,会有一个默认值undefined,undefined是个垃圾,拿来干什么都要不得 - 非常不推荐大家创建变量不赋值
- 创建多个变量时可以简写,var只写一次,每个变量之间使用,进行分割
- 变量名不是随意的
常量const
一旦创建,值不允许修改
-
语法:const 常量名=值;
-
现实生活中哪些是常量?但是常量其实很少,代码中也会很少使用
1、一周7天
2、pi - 3.1415926....
3、一年12个月
4、地球的公转速度/自转速度
5、一年365/366天
数据类型
-
原始/基础/值类型:5个
-
number 数字类型,取值有无数个
-
string 字符串类型,取值有无数个,但是需要加上""或''括起来
-
boolean 布尔类型,取值只有两个:true(真/对) 或 false(假/错),一般用于做比较判断是才会出现
-
null 空,释放你不再需要的内存/变量,节约内存空间
-
undifined 拿来干什么都要不得,这是我们祖师爷犯下的一个错误
-
-
***引用/对象类型:11个
*String Number Boolean -> 为什么这三个人即使原始类型又是引用类型?原因是他们三个具有包装类型?
*Array *Function Date(日期) Math(数学) *RegExp(正则->表单的验证)
Error(错误)
*Object(面向对象开发方式)
Global(全局对象:保存着全局变量和全局函数) -> 特殊:浏览器中没有global,global被window给代替了,只不过window可以省略不写
只有JavaScript中global才被代替为了window,而且node.js后端语言中!全局对象就叫global。- ***面试题:什么是包装类型? 包装类型:专门将原始类型的值封装为一个引用类型的对象 为什么:原始类型的值原本是没有任何属性和方法的,意味着原始类型本身就是不支持.做操作的 但是前辈们发现字符串经常会被我们程序员所使用/操作 为了方便我们程序员为这三个人提供了包装类型,从值->对象(提供了很多属性和方法) 何时使用:只要你试图用原始类型的变量去调用属性或方法时,自动包装 何时释放:方法调用完毕后,包装类型就会自动释放,又变回了一个原始类型的值 为什么undefined和null不能使用. - 没有包装类型
数据类型转换
js获取到的页面上的一切东西,一定都是字符串
如果你想要查看数据类型:typeof(查看的东西);
隐式转换
-
算数运算符的隐式转换(默认:悄悄的将左右两边的东西,转为一个数字,再运算)
-
+运算,碰上一个字符串,两边都会悄悄的变成一个字符串,+运算,不再是+运算,而是一个拼接操作
-
别的数据类型也可以转为数字
true->1
false->0
null->0
undefined->NaN //只要是转为数字,但是不知道该转为什么数字,结果一定为NaN -
其实- * / %字符串也可以转为数字,前提要是一个纯数字组成的字符才可以,但凡包含了一个非数字字符则为NaN
"100"->100
"100px"->NaN -
、NaN:Not A Number,不是一个数字,但是确实是数字类型,不是一个有效数字
没得优点,但是有2个缺点:
1、NaN参与任何算术运算,结果仍为NaN
2、NaN参与任何比较运算,结果永远为false,带来了一个问题:我们没有办法使用普通的比较运算来判断该数据是不是NaN,NaN连自己都不认识自己
解决:!isNaN(x);
true->是一个有效数字
false->是一个NaN -
学完隐式转换,依然没解决一个事(给有单位的变量进行算数运算)
"100px"*2=NaN
"100px"+100="100px100" 所以接下来学习强制转换 ↓
-
显示/强制转换
隐式转换出来的结果不是我们想要的,我们就可以手动调用一些方法,强制转为我们需要的数据
- 转字符串:页面上一切的数据默认都是字符串
- var str=x.toString();//x不能是undefined和null。undefined和null不能使用.去访问任何东西
2. String(其他元素类型)可以将任何元素类型转换为字符串。完全相当于隐式转换,还不如直接用+
-
转数字:3种
-
*parseInt(str/num); parse->解析 Int->整型
原理:专门为字符串和小数转为整数数字准备的,从左向右依次读取每个字符,碰到非数字字符就停止转换,
如果一来就碰到了不认识的字符,则为NaN。 !!!不认识小数点!!!
console.log(parseInt(100.5));//100 console.log(parseInt("100.5"));//100 console.log(parseInt("100px"));//100 console.log(parseInt("px100"));//NaN console.log(parseInt("1px00"));//1 console.log(parseInt(true));//NaN console.log(parseInt(false));//NaN console.log(parseInt(undefined));//NaN console.log(parseInt(null));//NaN
-
、*parseFloat(str); parse->解析 Float->浮点型,小数
原理:几乎和parseInt一致,但是认识第一个小数点
console.log(parseFloat(100.5))//100.5 console.log(parseFloat("100.5"))//100.5 console.log(parseFloat("100.5px"))//100.5 console.log(parseFloat("px100.5"))//NaN console.log(parseFloat("10px0.5"))//10 console.log(parseFloat("10.5.5.5"))//10.5 console.log(parseFloat(".555"))//0.555
- Number(any);//此方法事万能的,任何人都可以转为数字
//垃圾:完全等效于隐式转换,还不如x-0 *1 /1 %1
-
-
转布尔:
- Boolean(x);任何数据都可以转为布尔
- 返回值大部分都为true,只有6个(0,空字符串,null,undefined,NaN,false)为false。
- 在分支,循环条件中,判断其会不会执行
- 用逻辑运算符非进行隐式转换
- !!null ---->false
- Boolean(x);任何数据都可以转为布尔
运算符
赋值运算符:
- = -= += /= %=
- i++和++i的区别:
- 单独使用没有区别
- 参与表达式使用,变量中的值都会+1
- ++前置 返回的是加了后的新值
- 后置++ 返回的是加之前的旧值
算数运算符: +-*/%
- 特殊:1、%:取余,俗称模,两个数相除,不去商,而且除不尽的余数
5%2 -> 1
3%5 -> 3
作用:
1、最大的作用就是判断奇偶性,num%2,如果等于0说明是偶数,如果等于1说明是奇数
2、获取某个数字的后几位
console.log(1234%10);//4
console.log(1234%100);//34
console.log(1234%1000);//234
3、控制一个数字永远不超过某个数字
num%3 -> 结果永远不会超过3 (1%3=1 2%3=2 3%3=0)
2、*算数运算都具有隐式转换:悄悄地都会转为数字,再运算
特殊:+运算,只要碰上一个字符串,则悄悄的两边都会转为字符串,+运算不再是+运算,变成了一个拼接操作 - 计算机很笨,虽然记忆能力很强(机械硬盘(永久保存)/固态硬盘(有效期10年)):计算机带有摄入误差,解决:num.toFixed(d);//d代表保留的小数位数,会四舍五入的功能
缺陷:搭配上一个parseFloat()使用最好,返回的结果是一个字符串
比较运算符:> < >= <= == === != !==
-
结果为布尔值
-
隐式转换:默认,转换为数字,再比较大小(出口全等和非全等)
-
如果参与比较的左右两边都是字符串,则按位PK每个字符的十六进制unicode号(十进制ascii码)
0-9<A-Z<a-z<汉字 48<=数字<=57 65<=大写字母<=90 97<=小写字母<=122 19968<=汉字<=40869
-
undefinednull;//true;
区分:undefined=null;//false === !== 不带隐式转换的等值比较和不等比较,要求数值相同,并且数据类型也要相同
function String(x){ if(x===null){ return "null"; }else if(x===undefined){ return "undefined"; }else{ return x.toString(); } // }
逻辑运算符:
-
&&:与,并且。要求全部条件都满足,结果为true,只要一个条件不满足就会false。
全部为true,才为true
一个false,则为false -
||:或者,要求全部条件都满足,结果为false,只要一个条件满足就true
全部为false,才为false
一个true,则为true -
! :颠倒布尔值
-
短路逻辑:如果前一个条件,已经能够得出最终结论了,则不看后续
-
&&短路:如果前一个条件满足,则后一个操作执行,如果前一个条件不满足,则后一个操作不执行
目的:简化【简单的】if分支:1、一个条件一件事,满足就做不满足就不做 2、操作只能有一句话
简化:条件&&(操作);
对比:(大于500,打八折)
以前:if(total>=500){total=0.8}
现在:total>=500&&(total=0.8) 因为&&的优先级比=的优先级高,因此加个小括号- ||短路:如果前一个为true,后一个不看 - 实现浏览器的兼容性,二选一操作
e=e||window.event;
-
三目运算
目的:简化分支
- if(){}else{}------> 简化为:条件?执行代码1:执行代码2;
- if(){}else if()else{}...------> 简化为:条件?执行代码1:条件?执行代码2:执行代码3;
位运算
左移:m<<n 表示m左移了n位,翻译:m*2的n次方
- 3的4次方----》3<<2------->`(3*2)*2`
右移:m>>n 表示m右移了n位,翻译:m/2的n次方
底数只能为2
ascii码
- 获取字符串中第一个字符的ascii码:str.charCodeAt(0);
数字:48-57
大写字母:65-90
小写字母:97-122
汉字:19968(4e00)-40869(9fa5)
符号的优先级
由高往低排序:
- ()
- ipt.value
- ++ --
- 逻辑非
分支语句
if()else{}分支
1、只有条件满足是才会执行操作,一个条件一件事,满足就做不满足就不做
if(条件){
操作
}
2、条件满足是才会执行操作,条件不满足则执行默认操作,一个条件两件事,满足就做第一件,不满足就做第二件
if(条件){
操作
}else{
默认操作
}
3、多个条件多件事,满足谁就做谁
if(条件1){
操作1
}else if(条件2){
操作2
}else{
默认操作
}
注意:1、第三种写法:else if可以有多个
2、分支只要走了一条路,就不会再看其他路了
3、else其实可以省略不写,但是不推荐,如果条件都不满足,则什么都不会执行
switch...case分支
- 语法:
switch(变量/表达式){
case 值1:
操作1;
case 值2:
操作2;
default:
默认操作
} - 特殊:
- case 的比较不带隐式转换
- 问题:默认只要一个case满足后,会将后面所有的操作全部做完
解决:break;
建议:每一个case的操作后都跟上一个break
有的地方也可以不加break:- 最后一个操作default可以省略break
- 如果中间多个操作做的事儿是一样,可以省略中间部分
- default可以省略不写,但如果条件都不满足,则什么都不会执行
- 面试题:if VS switch ?
- switch...case...,缺点:必须要知道最后的结果是什么才可以使用,在case中不能做范围判断,只能做等值判断
优点:执行效率相对较高 - if:缺点:执行效率相对较低
优点:可以随意的做范围判断 - 建议:代码优化时,要尽量的将所有的 if...else... 换成 switch...case...
- switch...case...,缺点:必须要知道最后的结果是什么才可以使用,在case中不能做范围判断,只能做等值判断
循环
while循环
- 语法:
var 循环变量=几;
while(循环条件){
循环体;
循环变量变化起来
} - 执行原理:首先创建了循环变量,判断循环条件,如果条件满足,则执行一次循环体,并不会结束循环,会回过头,继续判断循环条件,满足的话,则再做一次
......
直到循环条件不满足false时,才会退出循环
宏观上看循环感觉一瞬间就结束了,但是微观上看其实是一次一次执行的 - 特殊: 死循环
- 有的时候真有可能需要使用死循环:永远不会停下来的循环
何时使用:不确定循环次数的时候
while(true){
循环体
}
- 有的时候真有可能需要使用死循环:永远不会停下来的循环
- 退出循环语句:
- break - 只能用在循环中,多半都是搭配死循环使用的;
- continue 退出当前次循环。
do while循环
-
语法:
var 变量=几;
do{
循环体;
变量的变化
}while(循环条件) -
面试题:while和do...while的区别?
只看第一次,如果条件都满足,则没区别
如果条件不满足,则while一次都不会执行,do...while至少会执行一次
for循环
-
*for循环:和while原理是一样的,但是他比while看上去更加的整洁,简单,舒服
- 语法:
for(var 循环变量=几;循环条件;循环变量变化起来){
循环体;
} - 死循环:
for(;😉{
循环体
} - 面试题:for 和 while的区别?
几乎没区别:
一般来说我们不确定循环次数的时候,会使用while循环 - 死循环
一般来说我们确定循环次数的时候,会使用for循环 - 大部分情况
- 语法:
-
猜数字小游戏
- 随机数 var rodom = parseInt(Math.random() * (max - min + 1) + min);
function getrodom() { var rodom = parseInt(Math.random() * (100 - 1 + 1) + 1); var hp = null; var user = null //难度 for (; ;) { user = prompt('请选择难度 1简单,2普通,3困难') if (user > 0 && user <= 3 && user != "") { break; } else { alert("请重新选择难度") } } if (user == 1) { hp = 10 } else if (user == 2) { hp = 5 } else if (user == 3) { hp = 3 } // } else { // hp=1 // alert('开启地狱模式') // } while (true) { if (hp > 0) { var shuru = parseInt(prompt('0-100猜数字,猜猜我是几,剩余次数' + hp)) if (!isNaN(shuru) && shuru !== "") if (shuru > rodom) { hp--; alert('大了,剩余生命' + hp); } else if (shuru < rodom) { hp--; alert('小了,剩余生命' + hp) } else { alert('对了') break; } else { hp-- alert("恶意输入,生命值-1,剩余次数" + hp) } } else { alert('你的生命值不足,请重新开始游戏,本轮游戏正确答案为' + rodom); break; } } }
function函数对象
函数,也称之为方法,需要【预定义】好的,以后就可以【反复使用】的【代码段】
创建与使用函数
-
创建/定义/封装函数
-
声明方式:function 函数名(){
若干代码
return 返回值;
};
-
直接量方式:var 函数名=function(形参列表){//通过此处看出函数名其实也是一个变量名,衍生出,elem.on事件名这一坨其实就是函数名,无非涨得长了点
函数体;
return 返回值;
} -
*构造函数:var 函数名=new Function("形参1","形参2",.....,"函数体,return返回值");
何时:如果你的函数体不是固定的,而是动态拼接的(函数体现在毕竟是一个字符串) -
return的本意,退出函数的意思
只不过如果return后面跟着一个数据,会顺便将其返回到全局作用域之中,但是只负责返回不负责保存!
所以再调用函数时,如果有return,记得拿一个变量接住他
调用函数:var result=函数名(实参列表)
-
-
使用/调用函数
函数名()
注意: 1、调用几次,就会执行几次
2、交给用户来触发,只需要在某个元素上绑定点击事件:
<div onclick="函数名()">内容</div>
- 何时使用:
1、不希望打开页面立刻执行,而在需要时调用,或者由用户来触发
2、希望能够反复执行,不需要刷新页面
3、以后任何一个独立的功能体,都要单独封装在一个函数之中(你做的每一个作业)
4、函数在js中的地位最高:第一等公民地位,以后要有封装的思想,!!!函数里面的变量会自动释放
带参数的函数:
何时使用:如果我的函数体,希望根据传入的实参的不同,做的略微不同
-
创建:形参:形式参数,其实就是一个变量名,但是不需要写var,而且默认保存的也是undefined
function 函数名(形参,形参,...){
若干操作/代码段;
} -
调用:实参:实际参数,真正的值,需要在你调用函数时从外部传入进去
函数名(实参,实参,...) -
特殊:1、传参的顺序一定要一一对应上,并且数量也要对应
2、不是一定要带参数的函数,才是好函数,具体要不要带参数,要看我们的需求
如果你的函数体事固定的 - 则普通函数就够了
如果你的函数体是根据参数的不同,做的略微不同 - 则带参数的函数完成
作用域:2种
-
全局作用域:成员:全局变量和全局函数,在任何位置都可以访问|使用.
-
函数/局部作用域:成员:局部变量和局部函数,只能在【函数调用时内部可用】
带来了变量得使用规则:优先使用局部的,局部没有找全局,全局没有就报错
特殊:
1、千万不要对着未声明的变量直接赋值:a=1; - 会导致全局污染,全局本来没有,突然就被添加上了一坨内存,建议任何变量使用之前一定要先var
2、儿子不孝啊,局部的东西全局居然不能用,解决:return 返回值;
3、哪怕没有写return,其实也会悄悄给你return一个undefined,默认
4、return一般只会出现再函数的最后,而且只能出现一次
5、其实前辈们提供给我们的方法,大部分底层都有return操作,因为前辈们考虑到了,调用了他这个方法过后得到的结果可能对我们以后有用
声明提前
在程序正式执行之前
会将var声明的变量和function声明的函数
集中提前到当前作用域的顶部,但是赋值留在原地
变量比函数轻
我们写代码绝对不会碰到,只要遵守规则:1、先创建后使用 2、变量名和函数名尽量的不要重复
只会在鄙视中碰到:如果以后你碰到先使用后创建,多半都是在靠你声明提前
面试题:
按值传递(浅拷贝)
- 如果传递的是【原始类型的值】
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方 - 如果传递的是【引用类型的对象】:Array、Function
修改一个变量,另一个变量是会受到影响的,因为两者使用的是【同一个地址值】 - 浅拷贝:好像是复制了,但是复制的不全
//深浅拷贝的区别:
// var arr1=[1,2,3,4];
// // var arr2=arr1 //浅拷贝, 这时改变arr2;arr1也会改变
// var arr2=arr1.slice(); //深拷贝
// arr2.length--;
// console.log(arr1);
// console.log(arr2);
// // -----------------------------------------------------
// var obj1={a:1,b:2};
// // var obj2=abj1 ////浅拷贝, 这时改变obj2;obj1也会改变
// var obj2={...obj1}; //深拷贝
// obj2.a=100;
// console.log(obj1);
// console.log(obj2);
重载
相同的函数名,传入不同的实参,可以自动选择对应的函数执行
- 问题:JS的语法不支持重载的!
JS不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前所有的 - 解决:在【函数中】有一个对象 - arguments对象
- 什么是arguments:自动创建的,是一个类数组对象(都有下标,都有length,都可以遍历),不是数组!
- 作用:接受住所有的实参(以后有没有形参无所谓了,变相的实现了重载,可以在函数内部判断arguments的不同,执行不同的操作)
- 正式开发中,开发结束后,需要做代码的优化,可能就会将多个函数整合为一个函数
function chongzai(){
if(arguments.length==2){ ///////可以判断它的长度,也可以判断数据类型
return arguments[0]+arguments[1]
}else if (arguments.length == 1) {
return arguments[0]**2
} else if (arguments.length == 3) {
return Math.max(arguments[0],arguments[1],arguments[2])
}
}
console.log( chongzai(4));
console.log( chongzai(4,4));
console.log(chongzai(4,5,6));
匿名函数
没有名字的函数,理论上来说我们无法调用
-
自调:函数自己调用自己
-
特点:自调的代码只能执行一次 === 全局代码(为什么:节约内存,因为匿名函数,没有变量引用着,用完后,就会立刻释放)
-
语法:
(function(){
//代替全局代码
})();
-
-
回调:将函数作为了实参,传递给其他函数使用
-
学习回调的目的:让你们知道哪些叫回调,匿名函数,只要不是自调,就是回调
arr.sort(function(a,b){return a-b})
str.replace(reg,function(){})
btn.onclick=function(){} -
以后ES6有一个技术:箭头函数:简化一切的【回调函数】
- 箭头函数口诀:省略function,(形参为1个时,可省略括号,形参没有时,保留括号),return后面为一句话时省略return和{}
var arr = [1,5,6,8,9,0] arr.sort(function (a, b) { return a - b }) //省略function,形参为1个时,可省略括号,return后面为一句话时省略return和{} arr.sort((a, b)=>a - b) console.log(arr.sort(function (a, b) { return a - b })); console.log(arr.sort((a, b) => a - b));
-
了解了以下回调函数的原理 - 其实不是没调,只是我们没调用,前辈们早就帮我们调用好了
-
预定义全局函数:
前辈们提前创建好的,我们可以直接使用的,在哪里都可以使用
-
编码和解码:- 玩玩悄悄话
问题:url中不允许出现多字节字符,如果出现会乱码
utf-8编码格式下,一个汉字,占3字节
解决:发送前,前端将用户输入的多字节字符编码为单字节字符(符号、字母)
发送后,后端将单字节字符解码为多字节原文编码:var code=encodeURIComponent("原文"); 解码:var 原文=decodeURIComponent(code); 其实百度根本就没做这个操作,这个东西在某次浏览器更新后,就自带此功能了,当场就淘汰了
-
isFinite(num); 判断num是不是无穷大,true->有效数字 false->无穷大
哪些会为false:NaN,分母为0(java就会错),Infinity -
牛逼的:parseInt/Float/isNaN/eval()
- eval(str);解析字符串(去掉字符串的引号)
- console.log(eval(‘1+2+3’))会直接得到结果 6
- eval(str);解析字符串(去掉字符串的引号)
函数执行原理
-
程序加载时:
- 创建执行环境栈ECS:保存了函数调用的顺序的数组
- 首先压入全局执行环境(全局EC):它引用着全局变量window
- window中保存着全局变量
-
定义函数时:
- 创建函数对象:封装代码段
- 在函数对象中有个作用域scope属性:记录着函数来着作用域是那里
- 全局函数的scope都是window
-
调用前:
- 在执行环境栈(ECS)压入一个新的EC(函数的EC)
- 创建活动对象(AO):保存着本次函数调用时用到的局部变量
- 在函数中的EC有一个属性scope chain(作用域链)引用AO
- AO有一个parent的属性是函数的scope引用着的对象
-
调用时:
- 正是因为有了前三个步骤,我们才有了变量的使用规则:优先使用局部的,局部没有找全局,全局没有则报错
-
调用完:
- 函数的EC会出栈,AO无人引用,垃圾回收器登场,AO自动释放,局部变量也就自动释放
-
两链一包:
作用域链:以函数的EC为起点,经过AO,逐级引用,形成一条链式结构,作用:查找变量,带来了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错
原型链:每个对象都有一个属性.__proto__
,可以不断的找到自己的父亲/爷爷/祖先...形成的链式结构,作用:查找属性和方法,哪怕自己没有属性或方法可以看看原型有没有
闭包:保护一个可以反复使用的局部变量的一种词法结构,使用场景:防抖节流
闭包
希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数
-
结构:
-
两个函数进行嵌套
-
外层函数创建受保护的变量
-
外层函数return出内层函数
-
内层函数要去操作受保护的变量
-
内层函数return出操作后的变量
function f1() { var a = 0; return function(){ a++ return a; } } var yx = f1() var xy = f1() console.log(yx()); console.log(yx()); console.log(yx()); console.log(xy()); console.log(xy());
-
-
注意:
- 判断是不是闭包,有没有两层函数嵌套,返回内层函数,内层函数在操作外层受保护的变量
- 外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
- 同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
-
缺陷:受保护的变量,永远不会被释放,使用过多,会导致内存泄漏
-
闭包的使用场景:防抖节流
-
公式:
function fdjl(){ var timer=null;//局部的timer准备保存定时器,定时器的序号 return function(){ if(timer!==null){clearTimeout(timer)}//停止一次性定时器 timer=setTimeout(function(){ 操作即可! },1000) } } var inner=fdjl();
-
需要防抖节流的地方
- elem.onmousemove - 频繁的修改DOM树,影响性能
- input.oninput - 每次input的内容修改都会触发,性能不好
- window.onresize -补充搭配上innerWidth可以实时获得窗口改变的尺寸
xxx.以上三个事件=function(){ inner(); }
-
Object对象
三大特点:封装,继承,多态
-
object封装:
- *直接量方式:
var obj={
"属性名":属性值,
...
"方法名":function(){操作},
...
};
- *直接量方式:
-
预定义构造函数方式:var obj=new Object();//空对象
//需要自己后续慢慢添加
obj.属性名=属性值;
obj.方法名=function(){} -
以上两个方法都有一个缺陷:一次只能创建一个对象
-
自定义构造函数:批量创建
function f1(形参1,形参2.....){
this.属性名1=形参1;
this.属性名2=形参2;
this.属性名3=形参3;
。。。。
}
var one=new f1(实参...)
var two=new f1(实参...)
......
-
特殊
-
其实属性名和方法名的""可以不加的,暂时不推荐,因为以后我们要学习一种数据格式JSON,要求属性名和方法名必须是一个"",单引号都不可以,为了习惯,我们就一直加上使用
-
访问对象的属性和方法
*obj.属性名 === obj["属性名"] *obj.方法名() === obj["方法名"](); ***js有一句话万物皆对象,一切对象的底层都是hash数组
-
访问到不存在的属性,返回undefined
-
可以随时随地的添加新属性和方法
-
希望遍历出对象所有的东西,使用for in循环
-
如果你希望在对象的方法里使用对象自己的属性:this.属性名
-
-
难点:this的指向
- 单个元素绑定事件:this->这个元素
- 多个元素绑定事件:this->当前触发事件的元素
- 函数中使用this:this->当前调用此函数的对象,看看函数前面是谁
- 定时器中this->window
- 箭头函数this->外部对象
- 构造函数中的this->当前正在创建的对象
-
批量创建对象:【自定义】构造函数方式
- 创建自定义构造函数
function 类名(name,age,hobby){
this.name=name;
this.age=age;
this.hobby=hobby
} - 调用构造函数创建出对象
var obj=new 类名(实参列表);
- 创建自定义构造函数
-
面向对象:
-
缺点:新手不友好
-
优点:
- 逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实生活
-
每个功能特地分开写 - 便于后期维护,哪怕没有注释,我也能一眼看懂代码
- 铁锁链舟 - 一个方法触发多个方法联动
面向对象开发: var fn=function(x){ this.x=x; this.btns = parent.getElementsByTagName('button'); this.init() } fn.prototype.init=function(){ //初始化 代码... this.bind() } fn.prototype.bind=function(){ //绑定事件 var me=this; for(var i=0;i<this.btns.length;i++){ this.btns[i].onclick=function(){ if(this.btns[i]==xx){me.animation(a,b)}else{me.animation(a,b)} } } } fn.prototype.animation=function(a,b){ //操作 代码... } ....... var nav=new fn(x) nav.init() ------------------------------------------------------------------------------ 封装对象: var carousel={ btns:parent.getElementsByTagName('button'), init:function(){ 代码... this.bind() }, bind:function(){ 代码... this.animation() }, animation:function(){ 代码... } } carousel.init();
-
-
object继承:
父对象的成员(属性和方法),子对象可以直接使用
- 为什么:【代码重用】!节约内存控件,提升网站的性能!
- 何时继承:只要多个子对象公用的属性和【方法】,都要集中定义再父对象之中,尤其是方法,方法/函数也是一个对象,创建的函数越多性能越差何时继承:只要多个子对象公用的属性和【方法】,都要集中定义再父对象之中,尤其是方法,方法/函数也是一个对象,创建的函数越多性能越差。
-
如何去找到父对象(原型对象):保存一类子对象共有属性和共有方法的
对象名.__proto__;//必须先有一个对象
- 构造函数名.prototype;//构造函数名:String、Function、Object、RegExp、Array、Date、h52205...,几乎人人都有,除了undefined,null,Math
-
原型链:
- 每个对象都有一个属性:
__proto__
,可以一层一层的找到每个人的父亲,形成的一条链式结构,我们就称之为叫做原型链
可以找到所有父对象的成员(属性和方法),作用:查找共有属性和共有方法
最顶层是Object的原型,上面放着我们眼熟的toString,怪不得人人都可以toString();
JS万物皆对象
- 每个对象都有一个属性:
-
有了原型对象,设置共有属性和共有方法:
1、原型对象.属性名=属性值;
2、原型对象.方法名=function(){}
笔试题:
判断自有还是共有
- 判断自有:obj.hasOwnProperty("属性名");
- 如果结果为true,说明是自有属性,如果结果为false,有两种可能:共有或没有
- 判断共有:
if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in自动去整条原型链上进行查找
console.log("共有")
}else{
console.log("没有")
}
组合公式:
if(obj.hasOwnProperty("属性名")){
console.log("自有")
}else{
if("属性名" in obj){
console.log("共有")
}else{
console.log("没有")
}
}
修改/删除 自有和共有
-
自有:修改:obj.属性名=新属性值;
删除:delete obj.属性名; -
共有:修改:原型对象.属性名=新属性值; - 千万不要对着对象直接修改共有属性,这样会导致再本地添加一个同名属性
删除:delete 原型对象.属性名;``` var peple = function (name, age, sex) { this.name = name this.age = age this.sex = sex } peple.prototype.eat = '吃饭' var yx = new peple('杨鑫', 18, '男') console.log(yx); // 修改自有 yx.sex = '未知' console.log(yx); //删除自有 delete yx.age console.log(yx); // 修改共有 peple.prototype.eat = '喝水' console.log(yx); // 删除共有 delete peple.prototype.eat console.log(yx); ```
如何为老IE的数组添加indexOf
不是固定的,但是是一种类型题,为一类人添加方法
if(Array.prototype.indexOf===undefined){//说明你打开了老IE
Array.prototype.indexOf=function(key,starti){
starti===undefined&&(starti=0);
for(var i=starti;i<this.length;i++){
if(this[i]==key){
return i;
}
}
return -1;
}
}
*如何判断x是不是一个数组:
千万别用typeof,typeof只能判断原始类型,引用类型显示的都是一个object,这道题也不是固定的
-
判断x是否继承自Array.prototype
Array.prototype.isPrototypeOf(x);//方法一 console.log(Array.prototype.isPrototypeOf(arr)); console.log(Array.prototype.isPrototypeOf(reg)); console.log(Array.prototype.isPrototypeOf(date)); // 方法一扩展 console.log(RegExp.prototype.isPrototypeOf(reg)); console.log(Date.prototype.isPrototypeOf(date));
-
判断x是不是由Array这个构造函数创建的
x instanceof Arrayconsole.log(arr instanceof Array); console.log(obj instanceof Object); console.log(reg instanceof RegExp); console.log(date instanceof Date);
-
Array.isArray(x); - ES5提供,只要是ES5+,老IE都不支持!只有数组有
console.log(Array.isArray(arr));
console.log(Array.isArray(obj));
console.log(Array.isArray(reg));
console.log(Array.isArray(date));
console.log(Date.isDate(date)); //报错 -
*输出【对象的字符串】形式,说白了就是转为字符串来判断
-
在Object的原型上保存着最原始的toString方式
-
原始的toString输出的形式:[object 构造函数名]
-
***多态:肤浅理解,一个方法有多种表现形态,子对象觉得父对象提供的成员不好用,在本地定义了同名成员,来覆盖父对象
固定套路:借用:Object.prototype.toString.apply(x) === "[object Array]";
true->数组 false->不是数组// console.log(Object.prototype.toString.apply()); // 意思为:在Object的原型上保存着最原始的toString方式、原始的toString输出的形式:[object 构造函数名] console.log(Object.prototype.toString.apply(arr)); //[object Array] // console.log(Object.prototype.toString.apply(date)); // console.log(Object.prototype.toString.apply(reg)); // console.log(Object.prototype.toString.apply(obj)); // console.log(Object.prototype.toString.apply()); // [object Undefined] console.log(Object.prototype.toString()); // [object Object] 万物皆对象 // console.log(Object.prototype.toString(date)); // [object Object] // console.log(Object.prototype.toString(reg)); // [object Object] // console.log(Object.prototype.toString(obj)); // [object Object] // console.log(Array.apply()); // [] // console.log(Object.apply()); {} // console.log(RegExp.apply()); /^\d$/
-
5.实现自定义继承:
1. 实现两个对象之间继承:
`子对象.__proto__=父对象;`
var wsc={
"phone":"威图",
"car":"三B三",
"gf":"一堆"
};
var wjl={
"money":10000000000000000
};
var dy={
js:150
};
wsc.__proto__=wjl;
wjl.__proto__=dy;
console.log(wsc.money);
console.log(wsc.js);
2. 实现批量对象继承
构造函数名.prototype=父对象;
但是要小心时机,必须在创建对象之前,确定关系
var dy = {
"js": 150;
};
function h52205(name, age, hobby) {
this.xingming = name; //hxy.xingming="糖稀"
this.nianling = age; //hxy.nianling=30
this.aihao = hobby; //hxy.aihao="骗钱"
};
h52205.prototype = dy; //必须在创建对象之前,确定关系
var hxy = new h52205("化学有", 35, "吃饭");
var tx = new h52205("糖稀", 30, "骗钱");
var zx = new h52205("招新", 128, "睡觉");
var lww = new h52205("廖文五", 300, "扣脚")
console.log(tx);
console.log(hxy);
console.log(zx);
console.log(lww);
Array数组对象
数组的基础
-
概念:什么是数组:一个内存(变量)中保存多个数据的一个集合结构
何时:只要存储的多个相关的数据,都要用数组集中保存
为什么:一个好的数据结构,可以极大的提升程序员的开发效率 -
创建:2种
- *直接量:var arr=[值1,....]
- /ES3之前是没有[]这个操作的,只能使用new Array,而为了简化,才提供了[],[]的底层就是new Array
- 构造函数:var arr=new Array(值1,....);
- var arr=new Array(1,2,3,4,5);//创建了一个数组,里面装有1,2,3,4,5
- 有一个坑var arr=new Array(5);//创建了一个长度5的空数组
- *直接量:var arr=[值1,....]
-
访问:
- 数组名[i] - 当前元素
- 添加/修改:数组名[i]=新值
-
特殊:
- 读取元素,下标越界 - 返回undefined
- 添加元素,下标越界 - 下标不在连续,导致变成一个稀疏数组
-
数组的三大不限制:长度、类型、下标越界(不好)
-
数组的唯一的属性:长度:arr.length
三个固定套路:
1、末尾添加:arr[arr.length]=新值;
2、获取倒数第n个:arr[arr.length-n];
3、缩容:arr.length-=n; -
遍历数组:
for(var i=0;i<arr.length;i++){
arr[i];//当前次元素
} -
如何释放引用类型:看清楚你的这个引用类型有几个变量引用着,都要释放后才能真正的释放干净
建议:我们的代码尽量的都要封装在一个函数之中!函数中的内存会自动释放 -
索引数组 - 下标都是由数字组成的数组。
*关联(hash)数组 - 下标都是可以自定义的数组
为什么:索引数组的下标,无具体的意义,不便于理解和查找
如何使用:- 创建:2步
1、先创建一个空数组:var arr=[];
2、为空数组添加自定义下标以及内容:arr["自定义"]=新值; - 访问:arr["自定义下标"];
- 强调:hash数组的length永远失效了,永远为0!
遍历数组,不能再使用for循环,必须使用for in循环 - 纯自动化循环,【专门为遍历数组准备的】
for(var i in 数组名){
i;//自动得到下标
数组名[i];//得到当前次元素
}
很棒:不光可以遍历hash数组,还可以遍历索引数组。
个人:遍历索引数组使用for循环,遍历hash数组再使用for in - hash数组的原理:
hash算法:将字符串,计算出一个尽量不重复的数字(地址值)
字符串的内容相同,则计算出来的数字也是相同的
添加元素:将自定义下标交给了hash算法,得到了一个数字(地址值),直接将你要保存的数据放到了此地址
获取元素:将指定的自定义下标交给hash算法,得到一个和当初添加上完全相同的数字(地址值),通过地址就可以找到你当初保存的数据 - ***js里万物皆对象(除了undefined和null),【一切对象的底层都是hash数组】
- 创建:2步
-
二维数组:
数组的元素,又引用着另一个数组
-
创建
- var arr=[
["杨鑫",18,3500],
["是新建",19,4500],
["杨婷婷",20,5500]
];
- var arr=[
-
访问:
arr[行下标][列下标];
-
特殊:列下标越界,得到undefined
行下标越界,会报错,因为只有数组才可以用[下标]。行下标越界已经得到undefined,没有资格再加下标 -
遍历二维数组:必然需要两层循环,外层循环控制行,内层循环控制列 - 现在都流行扁平化
公式:for(var r=0;r<arr.length;r++){ for(var c=0;c<arr[r].length;c++){ console.log(arr[r][c]) } }
-
数组的API
-
*****转字符串 arr.join('自定义连接符号')
-
固定用法
- 无缝拼接(将数组里面的内容拼接为一句话/单词)
- 渲染页面:将数组里面的数据拼接为DOM页面元素
- //数据
var arr=["-请选择-","北京","南京","西京","东京","重庆"];
//将数组转为字符串,只不过是拼接了标签
var str=""+arr.join("
")+"
";
//渲染到DOM树上
bd.innerHTML=str
- //数据
-
补充:
- 二级联动:
专属事件:select.onchange - 只有选中项发生变化才会触发
专属属性:select.selectedIndex - 选中项的下标,不需要自定义下标,但是只有select可以
二维数组:细分分类
- 二级联动:
-
-
*拼接数组:添加元素的新方式
-
//根据你传入的实参全部拼接到数组的末尾
var newArr=arr.concat(新值1,...);
特殊:1、不修改原数组,只会返回一个拼接后的新数组
2、支持传入一个数组参数进行拼接,悄悄的将你的数组打散放入-
var arr = [1, 2, 3] var arr1=[4,5,6] var arr2 = arr.concat(arr1,7,8,9) console.log(arr); console.log(arr2);
-
-
-
*截取子数组:
- //根据你传入的开始下标一直截取到结束下标
var subArr=arr.slice(starti,endi+1);
特殊:1、不修改原数组,只会返回一个截取后的新数组
2、含头不含尾
3、endi可以省略不写,会从starti一直截取到末尾
4、两个实参都可以省略,从头截到尾,复制了一份(深拷贝-两者互不影响)
5、支持负数参数,-1代表倒数第一个
- //根据你传入的开始下标一直截取到结束下标
以上的API都是不会修改原数组的
-------------------------------------------------------------------------------------------------------------------------------------------------
以下的API都会修改原数组
-
*删插替:
var dels=arr.splice(开始索引,删除个数,新数据);
- 删除:var dels=arr.splice(starti,n);//n代表删除的个数
特殊:其实splice也有返回值,返回的是你删除的元素组成的一个新数组 - 插入:arr.splice(starti,0,新值1,...);
特殊:1、原starti位置的元素以及后续元素都会被向后顺移
2、千万不要插入一个数组,会变成二维数组 - 替换:var dels=arr.splice(starti,n,新值1,...);
特殊:插入的个数和删除的个数不必相等
- 删除:var dels=arr.splice(starti,n);//n代表删除的个数
- 反转数组:arr.reverse();
冒泡排序:
强调:排序非常重要,记住:只要以后页面中有排序功能,说明他的底层一定是一个数组,因为js中只有数组才可以排序
-
手写(鄙视题):
- ``` //手写冒泡排序:从第一个元素开始,依次比较相邻的两个元素,如果前一个>后一个,两者就要交换位置 for(var j=1;j<arr.length;j++){ for(var i=0;i<arr.length-j;i++){ console.log(1); if(arr[i]>arr[i+1]){ var m=arr[i]; arr[i]=arr[i+1]; arr[i+1]=m; } } } ```
-
开发中,使用数组的API
-
arr.sort();
默认会将元素们转为字符串,按位PK每个字符unicode(ascii) -
升序arr.sort(function(a,b){a-b})
-
降序arr.sort(function(a,b){b-a})
-
原理:
``` arr.sort(function(a,b){//sort传了一个实参:比较奇怪,是一种匿名回调函数,不需要程序员自己调用,前辈们已经帮我们调好了,而且我们不需要管前辈们怎么创建的,只需要记忆固定用法即可 // console.log(a);//后一个数字 // console.log(b);//前一个数字 return a-b;//如果return 是一个正数:说明后一个数>前一个数 //如果return 是一个负数:说明后一个数<前一个数 //如果return 是一个0:说明后一个数==前一个数 //而sort方法会根据你return的正/负数,来自动进行排序操作 }) ```
-
-
栈和队列:
-
栈:其实就是数组,只不过要求是一段封闭,只能从另一端进出的数组
何时:希望优先使用最新的数据时,现实生活中:电梯、旅游公交车...现实生活中少的,代码中用的也少
如何使用:
开头进:arr.unshift(新值1,...);//添加元素的新方式,缺陷:导致其余元素的下标发生变化
开头出:var first=arr.shift();//一次只能删除一个,删除元素的新方式,可以接住你删除的元素,缺陷:导致其余元素的下标发生变化结尾进:arr.push(新值1,...);//添加元素的新方式 结尾出:var last=arr.pop();//一次只能删除一个,删除元素的新方式,可以接住你删除的元素
-
队列:其实就是数组,只不过要求是一端进,另一端出
何时:希望按照先来后到的顺序使用数据时,生活中多个一批
开头进:arr.unshift(新值1,...);
结尾出:var last=arr.pop();结尾进:arr.push(新值1,...); 开头出:var first=arr.shift();
-
扩展:
定时器:每过一段时间,就会执行一次的代码
开启: var timer=setInterval(function(){
操作
},间隔毫秒数)停止:clearInterval(timer)
事件:
鼠标移入:onmouseover 鼠标移出:onmouseout
String 字符串对象
Sring的概念:
-
什么是字符串:多个字符组成的【只读】字符【数组】(只读:我们要学习的任何字符串API,都是不会修改原字符串,意味着必须要拿一个变量接住结果)
和数组有相同的点:
1、字符串中个数(长度):str.length
2、获取字符串中的某个字符:str[i];
3、遍历字符串
4、所有数组不修改原数组的API,字符串都可以使用(concat、slice) -
和数组也有很多不同的地方:
所有的数组直接修改原数组的API,字符串都不可以使用!比如排序只有数组可以使用,但是
字符串也有很多很多属于自己的API
String的API
只有字符串可以使用的方法,无需创建,直接使用。
-
转义字符:\
作用: 1、将字符串和程序冲突的字符转为原文 "\"" 2、包含特殊功能的符号 \n -> js字符串换行 \t -> js字符串中的制表符,就是你平时敲键盘的tab键,大空格 3、输出unicode编码的字符 \uXXXX:第一个汉字:4e00 ->ascii:19968 最后一个汉字:9fa5 ->ascii: 40869```
-
*大小写转换:将字符串中的每个英文字符统一的转为大写或小写
何时:只要程序不区分大小写,就要【先统一】的转为 大写 或 小写,再比较(验证码)
如何:
大写:var upper=str.toUpperCase();
小写:var lower=str.toLowerCase(); -
获取字符串中指定位置的字符:str.charAt(i); 还不如str[i]
-
*获取字符串中指定位置的字符的ASCII码:
var ascii=str.charCodeAt(i);
*通过ascii码转回原文
var 原文=String.fromCharCode(ascii);
-
***检索字符串:检查索引:获取关键字的下标
var i=str/arr.indexOf("关键字",starti); ========str.macth("关键字")
starti可以省略,如果省略了,从下标0开始向右查找
返回值:找到了,返回的是第一个关键字的第一个字符的下标
*没找到,返回-1,我们不关心下标为多少,我们只关心下标为不为-1
作用:判断有没有
强调:不光字符串可用,数组也可用!老IE的数组是没有此方法的
鄙视题:此方法默认只能找到关键字的第一个下标,找到关键字的所有下标,如何完成? var str="no zuo no die no can no bibi";
var index=-1;//开始位置 -1
while((index=str.indexOf("no",index+1))!=-1){
console.log(index);
} -
*截取字符串:3个
*var subStr=str/arr.slice(starti,endi+1);//用法和数组一摸一样
str.substring(starti,endi+1);//垃圾:用法几乎和slice一致,但是不支持负数参数,而且只有字符串可用
str.substr(starti,n);//n代表截取的个数,不再需要考虑含头不含尾,也支持负数下标 -
拼接字符串:var newStr=str.concat(新字符串,...);//还不如+运算
-
*替换字符串:var newStr=str.replace("关键字"/正则表达式,"新内容");
- 本身这个方法非常强大,但是由于我们现在暂时不会正则表达式,所以只能替换固定的关键字(下周一就牛逼了)
-
*****切割/分割字符串:
作用:str<=>arr
var arr=str.split("自定义切割符"/reg);
特殊:1、切割后,切割符就不存在了,转为一个数组
2、切割符"",切散每一个字符 -
xx.trim() 去除首尾空格
RegExp正则对象
定义字符串中字符出现规则的表达式
语法:
-
最简单的正则:/关键字原文/
-
/no/后缀
- 后缀:g:匹配全部(字符串里面有一个满足正则的); i:忽略大小写
-
-
正则对象
- 创建:2种
- 直接量方式:var reg=/正则表达式/后缀;
- 构造函数方式:var reg=new RegExp("正则表达式","后缀");
- 方法:1个
- var bool=reg.test(str); //验证 括号里面的内容是否满足正则
- true->说明用户输入正确
- false->说明用户输入错误
- var bool=reg.test(str); //验证 括号里面的内容是否满足正则
- 创建:2种
-
备选字符集:/[备选字符集]/;
- 强调:
- 一个中括号,只管一位字符
- 正则表达式默认只要满足了就不管后续了,我们希望从头到尾完全匹配
- 解决:前加后加$:/[备选字符集]$/;
- 特殊:如果备选字符集中ascii码是连续的,那么中间部分可用-省略
- 比如:
- 一位数字:/[0-9]/; 输入11,为true (一个中括号,只管一位字符)
- 一位数字:
/^[0-9]$/; 输入11,为false
- 一位字母:/[A-Za-z]/;
- 一位数字或字母或下划线:/[0-9A-Za-z_]/+++
- 一位汉字:/[\u4e00-\u9fa5]/
- 除了xxx之外的:
/[^0-9]/ - 很少使用,范围太广了
- 预定义字符集:前辈们提前定义了一些字符集,方便我们程序员 - 简化备选字符集
- 一位数字:/\d/===/[0-9]/;
- 一位数字、字母、下划线: /\w/ ===/[0-9A-Za-z_]/;
- 一位空白字符(空格、回车、制表符): /\s/
- 一位除了换行外的任意字符:/./ (很少使用,范围太广了)
- 建议:优先使用预定义字符集,预定义字符集满足不了再用备选字符集补充
(不管是预定义字符集还是备选字符集,一个都只管一位)
- 量词:规定了一个字符集出现的次数:
1、有明确数量:
字符集{n,m}:前边相邻的字符集,最少n次,最多m次
字符集{n,}:前边相邻的字符集,最少n次,多了不限
字符集{n}:前边相邻的字符集,必须n次
2、无明确数量:
字符集? 前边相邻的字符集,可有可无,最多一次
字符集* 前边相邻的字符集,可有可无,多了不限
字符集+ 前边相邻的字符集,至少一次,多了不限
-
选择和分组:
选择:在两个规则中选一个
规则1|规则2
分组:将多个字符集临时组成一组子规则
(规则1|规则2) -
指定匹配位置:
开头:^
结尾:$
特殊:两者同时出现,前加^后加$,表示从头到尾要求完全匹配 - 只要你做【验证】必须加上 -
预判:密码强度验证:4位密码,数字和字母都可以,至少要有一位大写和一位数字。
(?![xxxx]+$)
/^(?![0-9]+$)[0-9A-Za-z]{4}$/; - 前面的预判意思是:不能纯由数字组成,可能由大写、小写 /^(?![A-Za-z]+$)[0-9A-Za-z]{4}$/; - 前面的预判意思是:不能纯由大写字母组成,也不能纯由小写字母组成,也不能纯由大写和小写字母组成 /^(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{4}$/; - //4位密码,数字和字母都可以,至少要有一位大写和一位数字。 /^(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{4}$/; - //4位密码,数字和字母都可以,至少要有一位大写和一位数字和一位小写。 /^(?![0-9A-Za-z]+$)[0-9A-Za-z\u4e00-\u9fa5]{4}$/ - //至少要有一位汉字
String支持正则的API
-
分割:var arr=str.split(‘str’/reg);
//身份证获取年月日 var sfz = '51050420020312181X' //方法一: var reg = /(\d{6})(\d{4})(\d{2})(\d{2})(\d{3}[\dX])/gi var arr=sfz.split(reg) console.log(arr); // ["", "510504", "2002", "03", "12", "1814", ""] console.log('生日'+arr[2]+','+arr[3]+','+arr[4]); // 方法二 var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3}[\dXx])$/ // var reg = /^\d{6}\d{4}\d{2}\d{2}\d{4}$/ var newstr=sfz.replace(reg,function(a,b,c,d,e){ // //正则加括号分组时 没分组时 // console.log(a); //整个身份证 整个身份证 // console.log(b); //正则第一个小括号里面的内容 0 // console.log(c); //正则第二个小括号里面的内容 整个身份证 // console.log(d); //正则第三个小括号里面的内容 undefined console.log('生日' + c + ',' + d + ',' + e); }) ```
-
替换:replace
1. 基本替换法: var newStr=str.replace(str/reg,"新内容"); //replace支持正则,搭配上后缀就可以替换所有的关键字,而且替换往往都要加后缀 //问题:只能替换为固定的内容 2. 高级替换法: ``` var newStr=str.replace(/[卧我握窝沃][槽操草糙艹屮肏你]+/g,function(a,b,c){ console.log(a);//要替换的关键字 - 正则匹配到的关键字 console.log(b);//要替换的关键字的下标 - 正则匹配到的关键字的下标 console.log(c);//原文本身 return a.length==2?"**":a.length==3?"***":"****"; }); \\手机号中间四位马赛克 var phn = '17721988849' var reg = /(\d{3})(\d{4})(\d{4})/; phn = phn.replace(reg, function (a, b, c, d) { return phn = b + '****' + d; }) console.log(phn);
3.格式化
var idCard="500103198602215933";
var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
idCard=idCard.replace(reg,function(a,b,c,d){
//再replace的时候,如果正则中出现了分组,那么我们会得到更多的形参
//再形参a的后就会再出现n哥形参,具体多少个看你有多少个分组
//第一个分组获取到的内容会保存在第二个形参之中
//第二个分组获取到的内容会保存在第三个形参之中
//...
//倒数两个形参分别是下标和原文
return b+"年"+c+"月"+d+"日";
})
Math数学对象
专门提供了数学计算的API,不需要创建,直接使用的,由浏览器的JS的解释器自动创建
- 取整
- 上取整:超过一点点,就会取下一个整数 - 现实生活少
var num=Math.ceil(num); - 下取整:无论超过多少,都会省略掉小数部分 - 现实生活少
var num=Math.floor(num); - 四舍五入取整:
var num=Math.round(num);//进制只看小数的第一位 - parseInt(str)+num.toFixed(d)
- num.toFixed(d)
- 优点:可以四舍五入保留指定的小数位数
- 缺点:返回的结果是一个字符串,建议搭配上parseFloat一起使用
- 鄙视题:不允许使用toFixed的情况下,自己封装一个函数,实现toFixed的功能,并且最后返回一个数字?
原理:看用户提供的数字和保留几位小数,对着用户的这个数字*10几次方(保留几位小数),再使用Math.round四舍五入取整
对着这个数字/10几次方(保留几位小数)
- 上取整:超过一点点,就会取下一个整数 - 现实生活少
- 乘方Math.pow(底数,幂) === 底数**幂
- 开平方:Math.sqrt(num)
- 绝对值:把负数转为正数
Math.abs(num); - 最大值和最小值:
var max/min=Math.max/min(a,b,c,d,e,f,g,....);
问题:本身不支持数组参数的
*解决:固定用法:
var max/min=Math.max/min.apply(Math,arr);
apply其中一个功能是可以将数组打散为单个元素 - **随机整数:Math.random() - 在0~1之间取出一个随机的小数,可能取到0,但是绝对不可能取到1,
意味着可能取到最小数,但是不可能取到最大数
公式:parseInt(Math.random()(max-min+1)+min); - 三角函数
- 略
Date日期对象
-
创建日期
- 创建一个当前日期时间:
var now=new Date(); - 创建一个自定义时间:
var birth=new Date("yyyy/MM/dd hh:mm:ss"); - 创建一个自定义时间:
var birth=new Date(yyyy,MM,dd,hh,mm,ss);
//修正月份:从0开始的,0->11月 - 复制一个日期:
为什么:日期的所有的API都是直接修改原日期的,无法获得修改之前的日期
所以,在执行API之前先进行复制,然后在对复制的日期进行操作
var end=new Date(start);
- 创建一个当前日期时间:
-
操作日期
-
两个日期对象之间可以相减,得到一个毫秒差,通过毫秒差就可以换算出你想要的任何一部分 - 日期的本质其实底层就是保存的毫秒数
创建日期的最后一种方式:var date=new Date(毫秒数); -
、API:
分量:时间的单位:
年月日星期:FullYear、Month、Date、Day
时分秒毫秒:Hours、Minutes、Seconds、Milliseconds;
每一个分类都有一对儿getXXX/setXXX
其中getXXX负责获取某一个分量的值;
其中setXXX负责设置某一个分量的值;
特殊:
1、取值范围:
FullYear - 当前的年份
Month - 0~11
Date - 1~31
Day:0~6,外国人的眼里星期天是一周的第一天
Hours:0~23
Minutes、Seconds:0~59
2、在设置时间的时候,如果超过了范围,会自动进制
3、Day是不允许设置的 4、设置时分秒 date.setHours(11,0,0)
-
如果希望对某个分量进行加减操作,推荐一句话完成
date.setXXX(date.getXXX()+/-num) -
格式化日期为字符串:
作用:1、date->string:stringAPI更多更实用
2、默认的标准国际日期,不是所有人都认识
date.toLocaleString();//转本地字符串
问题:存在兼容性问题
解决:自定义日期格式化format
-
daojishi()
//获取现在的时间,日+1,时分秒清零
var end = new Date()
end.setDate(end.getDate()+1)
end.setHours(00,00,00)
//计算倒计时函数
function daojishi() {
var start = new Date()
// var end= new Date('2023')
var end = new Date('2022/11/2 00:00:00');
var time = end - start
// console.log(time);
var h = parseInt(time / 1000 / 3600) //h
h < 10 ? h = '0' + h : h
var m = parseInt(time / 1000 % 3600 / 60)
m < 10 ? m = '0' + m : m
var s = parseInt(time / 1000 % 60)
s < 10 ? s = '0' + s : s
box.innerHTML = ('距离明天还有' + h + '时' + m + '分' + s + '秒');
}
// 定时器让页面上时间动起来
setInterval(function () {
var start = new Date()
// var end = new Date('2022/11/2 00:00:00');
var time = end - start
var h = parseInt(time / 1000 / 3600) //h
h < 10 ? h = '0' + h : h
var m = parseInt(time / 1000 % 3600 / 60)
m < 10 ? m = '0' + m : m
var s = parseInt(time / 1000 % 60)
s < 10 ? s = '0' + s : s
box.innerHTML = ('距离明天还有' + h + '时' + m + '分' + s + '秒');
}, 1000);
Error错误对象
-
浏览器自带4种错误类型,可以快速找到自己的错误:
语法错误:SyntaxError - 多半都是语法/符号错了
引用错误:ReferenceError - 没有创建过就去使用了
类型错误:TypeError - 不是你的方法/属性/单词写错,你却使用了
范围错误:RangeError - 只有一个API:num.toFixed(d);//d取值范围的,0~100之间 -
只要发生错误,就会报错,会导致后续代码终止执行了(后面代码没效果、如果你做的是APP、软件发生报错,则会闪退,体验感差),我们不希望
错误处理:就算发现错误,我们也不希望报错,而是给一个错误提示,后续代码依然可以继续执行
语法:
try{
只放可能出错的代码
}catch(err){
只有发生错误后才会执行
} try...catch...的性能非常差,几乎是所有代码中效率最差的
*可用一个技术代替:if...else...
*开发经验:提前预判用户:记住一切的客户端输入/用户输入都是坏人 - 你也不必担心,只要做好你该做的防护即可(!isNaN、正则); -
、自定义错误:
throw new Error("自定义错误描述");
DOM:Document Object Model:文档对象模型,专门用于操作html文档的,提供了一些方法
DOM树概念:DOM将我们的HTML看作了是一个倒挂的树状结构,但是树根不是html标签,而是document对象
document对象:不需要程序员创建,由浏览器的js解释器自动创建,一个页面只有一个document对象
作用:只要是对象就提供了属性和方法,等待我们学习,【找到】想要的任何DOM元素/对象/节点,甚至进行增、删、改、查(元素、属性、样式、文本)
DOM会将页面上的每个元素、属性、文本、注释都当作一个DOM元素/对象/节点
-
面试题:
- HTML-网页
- XHTML -更严格的网页
- DHTML -动态的网页 D:Dynamic 使我们的网页在离线状态怡然具有动态效果,这个不是新的技术和概念,原理:DHTML=HTML+CSS+JS(dom)
- XML -数据格式
-
DOM:原本是可以操作一切结构化文档的 HTML 和 XML,后来为了方便各类开发者细分为了3部分:
- 核心DOM:【无敌的】,既可以操作HTML,又可以操作XML
- 缺点:API比较繁琐
- 比如:getAttribute()
- 缺点:API比较繁琐
- HTML DOM:只能操作HTML,
- 优点:API非常简单
- 缺点:比如属性部分,只能访问/设置标准属性,不能操作自定义属性
- 比如: elem.class
- XML DOM:只能操作XML,XML已经淘汰了,现在最流行的数据格式是JSON
开发建议:优先使用HTML DOM,HTML DOM满足不了的操作我们再用核心DOM进行补充
- 核心DOM:【无敌的】,既可以操作HTML,又可以操作XML
-
每个DOM元素都有三大属性:
- xx.nodeType - 描述节点的类型
- document节点:9
- element节点:1
- attribute节点:2
- text节点:3
- 以前有用:判断xx是不是一个页面元素,因为我们以前没有children找儿子,只有childNodes
- xx.nodeValue - 获取元素的属性值的
- 以前有用:获取一个属性节点的值,因为以前没有getAttribute,只有getAttributeNode
- ***xx.nodeName:节点的名称 - 判断xx是什么标签
注意:返回的是一个全大写的标签名!
- xx.nodeType - 描述节点的类型
查找元素
-
通过ID查找元素
- var elem=document.getElementById('Id值')
- 页面有重复的ID,只会找到第一个
- 没找到返回null
- 其实ID值不用查找,可以直接使用
- 我们一般用class,不用id,id留给后端使用
- var elem=document.getElementById('Id值')
-
通过 标签名查找元素
- var elem=document/parent.getElementsByTagName/Name("标签名")
- 返回的是一个类数组对象,没找到返回空类数组
- js不能直接操作DOM集合,解决:1.搭配下标得到单个元素;2.使用 遍历得到每个元素
- parent-->代表你已经找到的某个父元素
- var elem=document/parent.getElementsByTagName/Name("标签名")
-
通过class名 查找元素
- var elem=document/parent.getElementsByClassName('class值')
- 返回的是一个类数组对象,没找到返回空类数组
- js不能直接操作DOM集合,解决:1.搭配下标得到单个元素;2.使用 遍历得到每个元素
- parent-->代表你已经找到的某个父元素
- var elem=document/parent.getElementsByClassName('class值')
-
通过关系查找元素:前提必须先找到一个元素才可以操作
- 查找该元素的父亲:elem.parentNode
- 查找该元素的亲儿子:elem.children; ------------返回的是一个集合
- 查找第一个子元素:elem.firstElementChild
- 最后一个子元素:elem.lastElementChild
- 前一个兄弟:elem.previousElementSibling
- 下一个兄弟:elem.nextElementSibling
-
*通过css选择器获取元素:
- 单个元素:var elem=document.querySelector("任意css选择器");
强调:1、万一选择器匹配到多个,只会返回第一个
2、没找到null - *多个元素:var elems=document.querySelectorAll("任意css选择器");
强调:找到了 返回集合,没找到返回空集合
更适合用于做复杂查找
- 单个元素:var elem=document.querySelector("任意css选择器");
-
属性选择器:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> [name=pwd]{ background: red; } </style> </head> <body> <input name="name" /> <input name="name" /> <input name="name" /> <input name="pwd" /> <input name="pwd" /> <input name="pwd" /> <script type="text/javascript"> var inps=document.getElementsByName("pwd"); console.log(inps); </script> </body> </html>
-
面试题:
getXXX 和 queryXXX的区别?返回结果不同?
-
getXXX:返回的是一个动态集合HTMLCollection
- 动态集合:根据DOM树的改变,悄悄的一起跟着变化,每一次修改DOM,都会悄悄的再次查找页面元素。缺点:性能低,而且不能使用forEach
-
queryXXX:返回的是一个静态集合NodeList
- 静态集合:根据DOM树的改变,不会一起变化,只会认准当时找的时候的数据。优点:复杂查找时简单、性能高、使用forEach
-
操作元素
前提:找到元素才可以操作。
内容
-
innerHTML获取和设置开始标签到结束标签之间的内容,包含标签
- 获取:elem.innerHTML;
- 设置:elem.innerHTML='内容';
-
innerText获取和设置开始标签到结束标签之间的文本内容,不支持标签
-
获取:elem.innerText
-
设置:elem.innerText='新文本'
(原本这个只有老IE可用,但随着各个浏览器的升级发展也支持了,老IE没有将就大家 ,反而大家在将就老IE)
-
获取:elem.textContent
-
设置:elem.textContent="新文本"
-
-
获取或设置input的值
- 获取:input.value;
- 设置:input.value="新值";
属性
-
设置获取属性: (核心DOM)
- 获取属性值:elem.getAttribute("属性名");
- 设置属性值:elem.setAttribute('属性名',"属性值"); 自定义属性名和属性值
-
简化版: (HTML DOM)
- 获取属性值:elem.属性名
- 设置属性值:elem.属性名="属性值"
- 特殊:
- class需要写为className
- 只能操作标准属性,自定义属性只能用Attribute进行操作
- 特殊:
-
移除属性:
- elem.removeAttribute('属性名'); (核心DOM)
- elem.属性名=""; (HTML DOM)
-
判断有没有该属性
垃圾:只能判断有没有,不能判断具体是什么
- elem.hasAttribute("属性名"); (核心DOM)
- elem.属性名!=""; (HTML DOM)
-
强调注意:
- class必须写为className
- 简化版:只能操作标准属性,不能操作自定义属性(自定义属性只能用Attribute进行操作)
- 删除属性时,删不干净,有的属性删不干净依然具有功能:比如:href没有属性值会有默认的刷新功能,disabled没有属性值依然具有禁用的功能
样式
-
*只能操作(内联)行内样式:优先级最高,一定会覆盖其他的样式
- 获取:elem.style.css属性名;
- 设置:elem.style.css属性名="css属性值";
- 特殊:
- css属性名,有-的地方,要去掉-,变为小驼峰命名法 border-radius -> borderRadius
- 获取样式时,也只能获取到内联样式
- 特殊:
-
操作样式表的css (内部样式style的css,或外部引入的css)
-
获取你想要操作的样式表
- var sheet=document.styleSheets[i];
-
获取所有的样式规则
- var rules=sheet.cssRules;
-
所有的规则中挑选出你需要操作的规则
- var rule=rules[i];
-
做获取 或 设置
- console.log(rule.style.width);
- rule.style.width="100px"; ……
-
给元素绑定事件
一切的获取,都是为了做判断
一切的设置,都是在修改|添加
- elem.on事件名=function(){
操作;
*****this关键字:这个
如果单个元素绑定事件,this->这个元素
如果多个元素绑定事件,this->当前触发事件的元素
}
事件
- onclick
- onchange
- onmouseover
- onmouseout
- onfocus
- onblur
- onsubmit - 点击提交按钮的前一刻会触发,阻止提交:return false;
- oninput
- onresize
- window.oncontextmenu - 右键菜单事件
- window.onkeyDown - 键盘按下事件 ,e.keyCode获取键盘键码
创建和添加元素并且渲染DOM树:3步
- JS创建空标签:
var elem=document.createElement("标签名"); - 为其设置必要的属性和事件
- elem.属性名="属性值";
- elem.on事件名=function()
- 上树
- 父元素.appendChild(elem); - 将elem追加到父元素里面当了最后一个儿子
- 父元素.insertBefore(elem,已有子元素); - 将elem追加到父元素里面,并且插入到已有子元素的前面
- 父元素.replaceChild(elem,已有子元素) - 将elem追加到父元素里面,并且替换已有子元素
- 补充:
- elem.remove(); 删除当前元素
递归
简单来说就是函数中,又一次调用了函数自己,迟早有一天也会停下来
-
何时使用:遍历DOM树,专门用于【层级不明确】的情况,既可以遍历层级不明确的DOM,又可以遍历层级不明确的数据
-
如何使用:
function 函数名(root){ 1、第一层你要做什么操作直接做! 2、判断他有没有下一级,如果有下一级再次调用此函数,但是传入的实参是下一级的东西 } 函数名(实际的根);
-
算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕后才会跳到兄弟节点
-
缺点:同时开启大量的函数调用,消耗大量的内存,只有一个情况才会使用:【遍历层级不明确】
-
递归 vs 纯循环
- 递归:
- 优点:直观易用
- 缺点:性能低下
- 纯循环:
- 优点:性能高,几乎不占用内存
- 缺点:难得批爆
- 递归:
遍历层级不明确的API
-
语法:
-
创建tw对象
- var tw=document.createTreeWalker(根元素,NodeFilter.SHOW_ELEMENT);
- 反复调用tw的nextNode()函数找到每一个元素
while((node=tw.nextNode())!=null){
node要干什么操作
}
缺点:1、必然跳过根元素,不会对根元素做操作
2、不可以遍历层级不明确的数据,仅能遍历层级不明确的DOM元素
-
HTML DOM提供了一些常用对象:
(并且对这些对象进行了简化,但是不是人人都可以简化)
-
image对象:图片对象,仅仅只是简化了创建
- (注意:不是人人都有构造函数创建方式)
- 创建:var img=new Image();//完全等效于var img=document.createElement("img");
-
form对象: 简化了表单对象,简化的是查找:
- 查找form元素:var forms=document.forms;
- 查找当前这个form元素下面的input:var inps=forms[1].elements;
- 专属事件:form.onsubmit=function(){//提交事件,只会在提交的一瞬间触发,防止用户输错还能提交
return false;
}
-
*select对象:
- 属性:
- select.options;//得到select下面的所有的option,完全等效于xx.children;
- select.selectedIndex;//获取到当前选中项的下标,当时我们还说除了select其他元素都需要自定义下标才能完成此操作
- 方法:*select.add(option); //完全等效于appendChild
- 专属事件:select.onchange=function()
- 属性:
-
*option对象:仅仅只是简化了创建,但是却非常牛逼!!
-
var opt=new Option("innerHTML","value");
-
搭配select.add(option);使用。
一句话:完成4个操作:创建,设置value值,设置innerhtml
select.add(new Option("innerHTML","value"));
-
ES5
只是在ES3的基础上添加了新特性 和 一些简化操作
保护对象:
-
底层具有四大特性:
"value": "威图", - 真正实际保存值的地方 "writable": true, - 开关:控制着是否可以被修改 "enumerable": true, - 开关:控制着是否可以被for in循环遍历到 "configurable": true - 开关:控制着是否可以被删除,总开关:一旦设置为false,其他特性不可以再次修改,自己也不可逆 如何修改四大特性: Object.defineProperties(对象名,{ "属性名":{四大特性}, ... })
-
三个级别:
-
防扩展:防止添加:Object.preventExtensions(obj);
-
密封:防止添加、删除:Object.seal(obj);
-
冻结:防止添加、删除、修改:Object.freeze(obj);
缺点:
- 如果你不用面向对象开发,你保护个串串
- 前辈们都没有保护,你更不用保护了
-
*数组的新API:3组6个:
-
判断:
-
every:
每一个,理论完全等效于&&,必须全部满足结果才为true,只要一个不满足,结果就为false
var bool=arr.every(function(val,i,arr){ //val - 当前值 //i - 当前值的下标 //arr - 数组本身,前辈们提供了3个形参,不代表3个形参对我们都有用 //如果你没有写return,默认return undefined,那一来就false了 return 条件;//必写的 })
-
some:
有一些,理论完全等效于||,必须全部不满足结果才为false,只要一个满足,结果就为true
var bool=arr.some(function(val,i,arr){ return 条件;//必写的 })
-
-
遍历:对数组中的每一个元素执行相同 或 相似的操作
-
forEach:直接修改原数组
arr.forEach(function(val,i,arr){ 操作 })
-
map:不修改原数组,返回一个新数组
var newArr=arr.map(function(val,i,arr){ return 操作; })
-
-
过滤和汇总
-
过滤:根据你的条件,筛选出你需要的部分,但是不会修改原数组
var subArr=arr.filter(function(val,i,arr){ return 条件 })
-
汇总:把元素汇总到一起
arr.reduce(function(prev,val,i,arr){//prev - 我们当初取名字的sum,prev为0,不断的做操作就完了 return prev+val; },基础值);//基础值会和arr累加后的结果再次相加
-
-
其实以上6个API,都是简化了for循环,一切的回调函数再拥有ES6过后都可以简化为箭头函数,以后我们不要再写for循环了,前提的是一个数组
-
Object.create():根据父对象创建子对象,设置好继承
var 子对象=Object.create(父对象,{ "属性名":{四大特性}, ... });
-
严格模式:
- 开启:在你的任何作用域的顶部加上一句话:"use strict"
- 功能
- 禁止了给未声明的变量赋值 - 解决全局污染
- 静默失败升级为了错误
一旦报错后续代码就不再运行了
call、apply、bind:借用
不是自己的方法也可以用 - 笔试面试都很容易碰到
- *call/apply:【临时】替换了函数中的this - 借用
- 语法:
- 函数名.call(借用的对象,实参,...) - 单独传入每一个实参
- 函数名.apply(借用的对象,arr) - 只能传入一个实参,要求是一个数组实参,apply会悄悄的打散数组
- 强调:call/apply,相当于立刻调用函数,立刻执行
- 语法:
- bind:【永久】替换了函数中的this - 买
- 创建了一个和原函数功能完全相同的新函数
- 将新函数中的this永久绑定为了指定对象
- 将新函数中的部分参数永久固定
- 固定套路:
- Math.max/min.apply(Math,arr);
- Object.prototype.toString.call/apply(x);
- ***类数组转为普通数组:
- var 普通数组=Array.prototype.slice.call/apply(类数组对象);
- ES5还给出了一种方案:var 普通数组=Array.from(类数组对象)
ES6(ES2015)
不光带了新特性,还带来了新语法,都是简化操作
是一个大版本,也是现今市场上最常用的一个版本,更新了很多的新特性(9个)
1、*模板字符串:
可以在字符串中放入变量 - 不需要再做字符串拼接,在字符串中实现了一个简单的js环境
`我的名字叫${name}`
2、*let关键字:
创建变量,以后要【优先】使用let创建变量
-
let 变量名=值;
-
作用:
- 禁止声明提前
- 添加了块级作用域 - 一个{}就是一个块!
- 记录着当前触发事件的元素的下标!
3、 *箭头函数:简化一切回调函数
- 口诀:function删掉,在()和{}之间添加=>,形参如果只有一个,可以省略小括号,函数体只有一句话,删除{},函数体只有一句话并且是return,{}和return都删掉
4、 for of循环:垃圾
- for(var v of arr){
v;//当前值
} - 缺点:
- 因为没有下标,无法修改原数组
- 无法遍历hash数组,也不能遍历对象
5、*****解构赋值:
解析结构,然后进行赋值 - 赋值的新方式,赋值方式得到了增强;
如果等号左右两边的结构长得一样,则会悄悄的脱掉两边的结构,再一一进行赋值;
-
语法:
-
类似数组一般的解构赋值
-
var [a,b,c]=[1,2,3];
-
-
类似对象一般的解构赋值 - 传参的顺序其实无所谓了!
-
var {a,b=默认值,c}={b:2,c:3,a:1}; 没传则为默认值,传了肯定用传的新值
-
-
常用方法:
-
//常用一: function zwjs({name,age,hobby}){ return `我的名字叫${name},今年${age}岁,喜欢${hobby}`; } console.log(zwjs({age:18,hobby:"打游戏",name:"袍哥"})) 以后但凡你见到:xxx({ 配置名:值, ... }) 他的底层就是解构赋值 //常用二: //return的结果可以不止一个 function f1(){ return ["袍哥","打游戏"]; } var [name,hobby]=f1(); //常用三: function f1() { // return "yy"; // return "xx"; return ["yy", "xx"] } var [val1, val2] = f1() console.log(val1, val2); //自己封装juery提供的ajax请求:(验证类似对象可以设置默认值;没传则为默认值,传了肯定用传的新值) nodejs部分: var http = require('http'); var url = require('url') var fs = require('fs'); const { log } = require('console'); var app = http.createServer() app.listen(81) app.on('request', (req, res) => { var objUrl = url.parse(req.url, true); var router = objUrl.pathname if (router == '/') { // console.log(__dirname); fs.readFile(__dirname + '/code/html/解构赋值.html', (err, buf) => { res.end(buf) }) } else if (router == '/ajax'){ console.log(objUrl.query.talk); if (objUrl.query.talk == 1) { res.end("[1,2,3]") } else if (objUrl.query.talk == 2) { res.end("[4,5,6]") } else if (objUrl.query.talk == 3) { res.end("[7,8,9]") } else { res.end("[]") } } }) ------------------------------------ js部分: var $ = {} $.__proto__.ajax = ({ type="GET", data, url, success, dataType }) => { //type:get/post //验证类似对象可以设置默认值;没传则为默认值,传了肯定用传的新值 var xhr = new XMLHttpRequest() if (type =="GET") { xhr.open(type, url + '?' + data) xhr.send(null) } else { xhr.open(type, url) xhr.setRequestHeader("Content-Type", "application?x-www-form-urlencoded") xhr.send(data) } xhr.onreadystatechange = () => { if (xhr.status == 200 && xhr.readyState == 4) { if (dataType == "HTML") { success(xhr.responseText) //回调函数 } else if (dataType == "JSON") { success(JSON.parse(xhr.responseText)) //回调函数 } } } } $.ajax({ data:"talk=1", url:"/ajax", dataType:"JSON", // type:'GET', success:(data)=>{ console.log(data); } })
-
-
6、Set和Map类型:它们都属于映射类型(类似于数组/对象)
- Set:和数组相似,但是没有length,用size获取自己的长度 - 功能:对数组去重,但是set可用的API很少,功能很少,所以还需要再转回数组
创建:var s=new Set(arr);
如何set变回arr:[...s];//...用于展开结构 - 扩展运算符 - Map:和对象相似,可以为他设置值,也可以读取值 - 毫无卵用
创建:var m=new Map();
7、*****class关键字:
简化了面向对象(封装、继承、多态)开发 - 学习react框架时(先学好vue,就算学到react也不会原生的敲这些代码,底层有些API带有此功能)
//创建飞行物类
class flyer{
constructor(name,speed){//constructor里面放着的就是自有属性
this.name=name;
this.speed=speed;
}//constructor外面的就是原型 - 共有
fly(){
return `${this.name}正在以时速${this.speed}飞行`;
}
}
//创建飞机类
class plane extends flyer{//extends不光继承了自有,也继承了共有
constructor(name,speed,rl){//constructor里面放着的就是自有属性
super(name,speed);//就算有一万句,我也只写这一句,super方法回自动调用,你extends继承的人的constructor的函数
this.rl=rl
}
fly(//实现多态
return `${this.name}正在以时速${this.speed}飞行,可以坐${this.rl}人`;
}
}
var j20=new plane("歼20",1000,2);
console.log(j20);
console.log(j20.fly());
var bird=new flyer("钟清翰",10);
console.log(bird);
console.log(bird.fly());
8、***ES6模块化开发:
作用:分工合作,每个js都是一个模块,每个工程师都可以单独开发自己的模块
- 子模块要公开
export var obj={} - 主模块要引入,刚好巧了,Node也可以用这句话,因为Node只是不支持DOM和BOM而已
import {obj as 别名} from "./子模块路径.js"
注意:
1、用了别名,原名就不可以使用了
2、./和.js是不可以省略不写的 - HTML要引入主模块
<script src="主模块文件" type="module"></script>
- 运行时需要用live本地代理服务方式打开,因为import的引入是动态引入。简单的说,import引入的模块,如果更新了,就会实时显示到网页上。live方式运行特点:修改代码时,页面会自动更新,不需要刷新页面
9、*Promise 承诺:
以后你在使用异步代码时,但是也有希望能够保证执行顺序的时候,使用Promise -- 三阶段不会自己写,但是三阶段学习的API会带有此功能
function ajax1(resolve){//渲染头部
setTimeout(()=>{
console.log("11111111111111111111111111111111");
resolve();//放行
},Math.random()*5000);
}
function ajax2(){//渲染身体
return new Promise((resolve)=>{
setTimeout(()=>{
console.log("22222222222222222222222222222222");
resolve();//放行
},Math.random()*5000);
})
}
function ajax3(){//渲染底部
setTimeout(()=>{
console.log("33333333333333333333333333333333");
},Math.random()*5000);
}
new Promise(ajax1).then(ajax2).then(ajax3);//是不是5秒?每次都是重新开始的5s内
//希望,异步(不要卡住后续代码),但是又要保证顺序(1、2、3)
console.log("后续代码");
- 面试题:
- 涉及promise的面试题爱问这种,可以了解
完全理解【宏任务和微任务】
https://zhuanlan.zhihu.com/p/355648428
- 涉及promise的面试题爱问这种,可以了解
BOM:Browser Object Model(浏览器对象模型)
专门提供了用于操作浏览器的API - 没有标准,不像DOM由W3C来制定标准,但是大部分浏览器已经统一实现了,唯独老IE与众不同,相当于DOM使用也会较少
(BOM之中只有两个重点:定时器和event,BOM存在大量的兼容性问题)
window对象
- 扮演着两个角色:
-
在前端/浏览器端,他代替了全局对象global,保存着全局变量和全局函数
window.变量名;
window.函数名(); - 只不过window可以省略不写 -
指代当前窗口本身
目的:优化用户的体验感,多为用户考虑
- 网页打开新链接的方式:
- 替换当前页面,可后退
HTML:<a/ href="url">内容
JS:open("url","_self"); - 替换当前页面,禁止后退:使用场景:结账后不允许后退
history对象:保存着当前窗口打开过的历史记录url,只有产生了窗口历史才能前进后退
location对象:保存着当前窗口正在打开的url
JS:location.replace("url") - 替换网址后页面肯定变化,但是替换并不叫跳转,不会产生任何的历史纪录 - 在新窗口打开,可以打开多个
HTML:<a/ href="url" target="_blank">内容
JS:open("url","_blank"); - 在新窗口打开,只能打开一个:使用场景:打开结账页面时
HTML:<a/ href="url" target="自定义name">内容
窗口的底层其实是有名字的,如果重新打开相同的名字,新打开的窗口会替换掉旧的窗口
JS:open("url","自定义name"); - 扩展:a标签的其他用处:
- 跳转
- 锚点
下载:<a href="xx.exe/zip/rar">下载</a>
打开:<a href="xx.txt/jpg...">图片/文本</a>
直接执行js操作:<a href="javascript:js代码;">图片/文本</a>
- 替换当前页面,可后退
- 网页打开新链接的方式:
window窗口的属性和方法:
-
属性:
- 获取大小:
- 获取浏览器的完整的大小:outerWidth/Height
- *获取浏览器的文档内容显示区域的大小:innerWidth/Height
- 获取完整屏幕的大小:screen.width/height //没用,我们并不做桌面应用
- 获得页面内容的完整长度document.body.scrollHeight=document.body.offsetHeight=document.body.clientHeight
- 获取大小:
-
方法:
-
打开新窗口:var newW=open("url","自定义name","width=?,height=?,left=?,top=?");
//第三个参数如果没传入,那么新窗口大小和位置会与浏览器融为一体
//第三个参数如果传入,新窗口会脱离浏览器独立成为一个小窗口 -
关闭新老窗口:window/newW.close();
//以下两个操作:仅仅只能对脱离浏览器的新窗口可用 -
移动新窗口:newW.moveTo(x,y);
-
改变新窗口的大小:newW.resizeTo(new宽,new高);
-
*定时器:
何时使用:只要过一段时间就希望执行的特效或操作,必须使用定时器
- 周期性:只要不停止会等待时间,反复不断的执行
开启:timer=setInterval(callback,间隔毫秒数);
停止:clearInterval(timer); - 一次性:等待时间后只会执行一次,就结束
开启:timer=setTimeout(callback,间隔毫秒数);
停止:clearTimeout(timer); - 其实以后你不会是周期性还是一次性都无所谓,因为两者的底层是一样的,用哪个停止其实无所谓,甚至可以互换!
- 周期性:只要不停止会等待时间,反复不断的执行
-
扩展:
***获取鼠标的位置:2步
- 获取事件对象event:在事件函数中传入一个形参e即可,自动接收到event对象
- 获取鼠标的位置:
1、鼠标相对于屏幕的位置:e.screenX/Y
2、鼠标相对于文档显示区域的位置:e.clientX/Y
3、*鼠标相对于网页的位置:e.pageX/Y
-
history对象
封装着当前窗口打开过的历史记录url。
- 前进:history.go(1);
- 后退:history.go(-1);
- 刷新:history.go(0);
- 垃圾:浏览器自带此功能,没必要
navigator对象
封装着当前浏览器的基本信息,判断此浏览器是什么浏览器以及版本号。
-
navigator.userAgent; -想办法截取你需要的部分
-
垃圾:说白了专门为判断是不是IE存在的,但是开发效率慢远远不如css hack来的简单(其实现在做兼容越来越少了:1、微软不推荐我们用IE 2、流行移动端,移动端不存在IE)
-
补充:css hack
-
<!--[if lte IE 8]> 小于ie8 才引用下面的css <link rel="stylesheet" type="text/css" href="css/ie.css"/> <![endif]-->
-
-
*location对象
封装着当前窗口正在打开的url。
-
*常识:面试时:一个url由几部分组成?
https://www.baidu.com/s?wd=%E6%9D%A8%E9%91%AB&rsv_spt=1&rsv_iqid=0xc099ebd40001a15e&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=02003390_82_hao_pg&rsv_dl=tb&rsv_enter=1&rsv_sug3=9&rsv_sug1=4&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=9283&rsv_sug4=10043 (https协议)://(www.baidu.com 域名):(443端口号)(/s 相对路径)?(wd=%E6%9D%A8%E9%91%AB&rsv_spt=1&rsv_iqid=0xc099ebd40001a15e&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=02003390_82_hao_pg&rsv_dl=tb&rsv_enter=1&rsv_sug3=9&rsv_sug1=4&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=9283&rsv_sug4=10043 请求信息) http://127.0.0.1:8020/bom02/new/02%E5%88%A4%E6%96%AD%E6%B5%8F%E8%A7%88%E5%99%A8%E4%BB%A5%E5%8F%8A%E7%89%88%E6%9C%AC%E5%8F%B7.html?__hbt=1668478029615 (http 协议)://(127.0.0.1 域名):(80 端口号)20/(bom02/new 相对路径)/(02%E5%88%A4%E6%96%AD%E6%B5%8F%E8%A7%88%E5%99%A8%E4%BB%A5%E5%8F%8A%E7%89%88%E6%9C%AC%E5%8F%B7.html?__hbt=1668478029615 请求信息)
- 五部分:协议://域名:端口号/文件相对路径?前端告诉后端的话
- 协议: https/http/ftp/ws... - 固定的,我们想要免费肯定用http
- 域名/主机号/IP: 域名/主机号/IP:域名是需要花钱购买的,ip是免费的,而且自己访问自己可以用主机号127.0.0.1
- *端口号: 默认端口号可以省略不写:https默认端口为443,http默认端口为80 (域名冒号 后面的内容)
- *文件相对路径/路由:百度加密了,意味着你现在看到的是哪个页面
- *查询字符串/请求消息: 前端提交到后端的东西,前端说的话 (?后面的内容)
- 五部分:协议://域名:端口号/文件相对路径?前端告诉后端的话
-
*属性:其实一这块根本不需要记单词属性,你只需要打开控制台输出location对象即可查看
- protocol - 协议
- hostname - 域名/主机号/IP
- port - *端口号
- pathname - *文件相对路径/路由
- search - *查询字符串/请求消息
-
方法:
- 跳转:location="新url";
- 跳转后禁止后退:location.replace("新url");
- 刷新:location.reload()
event: 事件对象
用户手动触发的 或 浏览器自动触发的事件
绑定事件:3种
-
在html标签中定义处理属性:
<elem on事件名="js语句"></elem>
-
缺点:
- 不符合内容(HTML)和行为(JS)的分离
- 无法动态绑定事件 - 一次只能绑定一个元素
- 无法同时绑定多个函数对象
-
-
*在JS中使用元素的事件处理函数属性
elem.on事件名=function(){}
-
优点:
- 符合内容(HTML)和行为(JS)的分离
- 动态绑定事件
- 没有兼容性问题
-
缺点:
- 无法同时绑定多个函数 - 个人认为不是缺点,我们可以把多个函数汇总为一个函数
-
-
使用API绑定事件
主流:elem.addEventListener("事件名",callback); 老IE:elem.attachEvent("on事件名",callback); 兼容: if(elem.addEventListener){ elem.addEventListener("事件名",callback);//老ie不认识箭头函数,因此要用回调函数 }else{ elem.attachEvent("on事件名",callback); elem.attachEvent("on事件名",callback); //会先执行后绑定的事件 }
- 优点:
- 符合内容(HTML)和行为(JS)的分离
- 动态绑定事件
- 同时绑定多个函数对象
- 缺点:
- 存在兼容性问题 - 导致代码量巨大
- 优点:
*事件周期:
从事件发生到事件结束的全过程
- 捕获阶段:由外向内:记录着要执行的事件有哪些
- 目标触发:目标元素:实际发生事件的元素
- 冒泡阶段:由内向外执行
*获取事件对象event:
主流:在事件处理函数中传入一个形参,就会自动得到event对象了
老IE:window.event;
兼容:var e=event;
这是我们第二次见到小三上位,老IE的操作,主流也可以使用了,建议:如果不考虑老IE,明显写一个形参e更简单
*****事件对象event可以干什么?
-
获取鼠标位置
- 鼠标相对于屏幕的位置:e.screenX/Y
- 鼠标相对于文档显示区域的位置:e.clientX/Y
- *鼠标相对于网页的位置:e.pageX/Y
-
阻止冒泡:面试/笔试
主流:e.stopPropagation(); propagation传播 老IE:e.cancelBubble=true; cancel取消 bubble泡泡 兼容:这是我们第三次见到小三上位,老IE的操作,主流也可以使用了 divs[i].onclick=function(){ var e=event; alert(1); e.cancelBubble=true; }
-
*****事件委托:
-
优化:如果多个子元素定义了相同的事件处理函数,最好只给父元素定义一次
-
为什么:每次绑定一个事件处理函数,其实就创建了一个事件监听对象,创建的监听对象越多,那么网页的效率就越低下
-
获取目标元素:【实际】触发事件的元素
主流:e.target; 老IE:e.srcElement; 兼容:这是我们第4次见到小三上位,老IE的操作,主流也可以使用了
-
优点:
- 事件只需要给父元素绑定一次,搭配上nodeName/className等判断则无敌
- 不用担心冒泡了,我们就是要利用冒泡
- 再也不用使用this这个【当前对象】了,【目标对象】更舒服
-
-
阻止浏览器的默认行为:
比如:a标签href默认刷新,submit按钮默认提交,比如F5刷新页面,比如F12打开控制台,比如阻止右键的弹出菜单
主流:e.preventDefault(); 老IE:e.returnValue=false; 兼容:e.returnValue=false; //不光老IE可用,主流也可以用 - 小三上位
-
获取键盘的键码
- e.keyCode - 不需要你记忆:要么console输出看,要么百度搜索
-
补充事件:
- window.oncontextmenu - 右键菜单事件
- window.onkeyDown - 键盘按下事件
- 回车提交表单,Esc返回等快捷键操作—提高用户体验
ES7:
市场上最流行的es6(es2015)- es6是2015年推出的,超级大版本,2015年过后,ECMA组织像吃了春药一样,疯狂的在更新,几乎每年一个版本,但是又没带来多少东西
学了ES7-13有什么用?装逼,你写的代码,别人不一定看得到,但是功能他也会,语法不同。
1、Array API:includes
在ES7之前,如果我们想要判断一个数组中是否包含某个元素,通过 indexOf获取结果,判断是否为-1
在ES7之后,我们可以通过includes来判断一个数组中是否包含一个指定元素
var arr=[10,20,30];
console.log(arr.indexOf(30)!=-1);//true
console.log(arr.indexOf(40)!=-1);//false
console.log(arr.includes(30));//true
console.log(arr.includes(40));//false
2、*指数/乘方运算符:
在ES7之前,如果我们想要计算数字的乘方:Math.pow(底数,幂);
在ES7之后,我们可以通过**运算符,可以实现乘方
Math.pow(8,6);
8**6;
ES8:
1、Object.values(obj);对象=>数组
在ES8之前,我们想要获得一个对象里所有的属性值,我们只能for in循环遍历对象
在ES8之后,提供了Object.values(obj),直接把你所有的属性值保存在一个数组之中。
var wsc={
"name":"王思聪",
"age":40,
"hobby":"吃屎"
}
wsc=Object.values(wsc);//["王思聪",40,"吃屎"] - 你想用什么循环都无所谓
2、将对象变成有一个二维数组,同时保存着键(下标0)和值(下标1)
Object.entries(obj)
var wsc={
"name":"王思聪",
"age":40,
"hobby":"吃屎"
}
var arr=Object.entries(wsc);
//输出arr为:[["name","王思聪"],["age",40],["hobby","吃屎"]]
3、String Padding
某些字符串我们需要对其进行前后的填充,来实现某种格式化效果
var str="hello";
//在str前面拼接了字符串a,总共填充到15位
var newStr1=str.padStart(15,"a");
//在str后面拼接了字符串a,总共填充到15位
var newStr2=str.padEnd(15,"a");
console.log(str);
console.log(newStr1);
console.log(newStr2);
应用场景:比如时间是一位数的时候,需要在前面填充一个
4、任何代码(数组,对象),尾部逗号的添加,都不会导致出错
5、***async 和 await:
async是一个关键字放在函数前面,用于表示函数是一个异步函数,异步函数也就意味着该函数的执行不会阻塞后续代码
async function f1(){
return "我是f1函数";
}
f1().then(result=>{
console.log(result);
console.log("f1函数执行完毕后要干什么")
})
console.log("后续代码");
意味着任何函数都可以变为异步函数
await是等待的意思,他在等什么?他后面跟着什么?其实他后面可以放任何表达式,不过我们更多的是放Promise对象
注意:await关键字只能放在async的函数之中
async function f1(){
return 100;
}
async function f2(){
return 200;
}
async function f3(){//f3必须要等待f1和f2
return await f1()+await f2();
}
f3().then(rs=>{
console.log(rs)
})
console.log("后续代码");
同时出现多个异步函数,f3异步函数希望使用f1和f2的结果,可以用上await进行等待
ES9:
1、*扩展运算符:
...其实ES6就已经引入了,但是仅仅只有【数组可用】 - 脱/穿衣服,关键就看有没有数组外套,如果有就是脱,如果没有就是穿
function f1(a,b,...c){
console.log(a);//1
console.log(b);//2
console.log(c);//[3,4,5]
}
f1(1,2,3,4,5)
ES9对象也可以使用了:
var obj={
a:1,
b:2,
c:3,
}
var {a,...x}=obj;
console.log(a);//1
console.log(x);//{b:2,c:3}
数组深拷贝(slice),那对象深拷贝怎么办?
var obj1={
a:1,
b:2,
c:3,
}
var obj2={...obj1};//对象深拷贝,脱了衣服再穿一个新衣服
obj2.a=100;
console.log(obj1)
console.log(obj2)
//关于(arr、obj)深浅拷贝的区别
var arr1=[1,2,3,4];
// var arr2=arr1 //浅拷贝, 这时改变arr2;arr1也会改变
var arr2=arr1.slice(); //深拷贝
arr2.length--;
console.log(arr1);
console.log(arr2);
// -----------------------------------------------------
var obj1={a:1,b:2};
// var obj2=abj1 //浅拷贝, 这时改变obj2;obj1也会改变
var obj2={...obj1}; //深拷贝
obj2.a=100;
console.log(obj1);
console.log(obj2);
2、正则表达式-命名捕获组
var str="500103198602215933";//格式化:1986年2月21日
var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
str=str.replace(reg,(a,b,c,d,e,f)=>{
return b+"年"+c+"月"+d+"日"
})
console.log(str);
//代码很难读懂,并且改变正则而表达的结构有可能匹配到的对象有所改变,ES9允许命名捕获组使用?<自定义name>
var str="500103198602215933";//格式化:1986年2月21日
var reg=/\d{6}(?<年>\d{4})(?<月>\d{2})(?<日>\d{2})\d{4}/;
var rs=reg.exec(str);//查找,支持正则,既可以得到下标,又可以得到所有的内容
console.log(rs.groups.年);
console.log(rs.groups.月);
console.log(rs.groups.日);
ES10:
1、flat() 方法会按照一个可指定的深度,递归遍历数组,并将所有元素与遍历到的子数组的元素合并为一个新数组
例如:一个数组中有很多层,第一层数组中还有第二层,第三层..n层
var arr=[10,20,[30,40],[50,[60]],[70,[80,[90]]]];
//将数组里面前三层数组进行了扁平化
var newArr=arr.flat(3);
console.log(newArr);//[10,20,30,40,50,60,70,80,90]
2、flatMap() 方法首先会映射函数映射每个元素,然后将结果压缩成一个新数组
注意一:flatMap是先map操作,再做flat操作
注意二:flatMap的flat相当于深度为1
var arr=["hello world","hello coder","你好 世界"];
var arr2=arr.flatMap(str=>str.split(" "));//即带有map遍历操作,又带有flat扁平化操作(不能设置扁平化的级别,只能固定为1层)
console.log(arr2);
3、trimStart/trimEnd
ES10之前去掉一个字符串首尾的空白,我么们可以通过trim方法,如果单独去掉前面或后面?
ES10为我们提供了trimStart/trimEnd
ES11:
全局对象全新写法
比如在浏览器中可以通过过window来获取
比如在Node服务器端中可以通过global来获取
在ES11中对获取全局对象进行了统一的规范:globalThis
for...in循环标准化
在ES11之前,虽然很多浏览器支持for...in来遍历对象类型,但是并没有被ECMA标准化
ES12:
逻辑赋值运算符
function f1(a){
a||="默认值";
console.log(a);
}
f1("我是新值");
var obj={
"name":"钟sir"
}
obj&&=obj.name;
console.log(obj);
ES13 :
at() - 获取字符串、数组的元素,跟下标差不多,但是他支持负数参数
var arr=[1,2,3,4,5];
console.log(arr[0]);
console.log(arr[arr.length-1]);
console.log(arr.at(0));
console.log(arr.at(-1));
var str="hello";
console.log(str.at(0));
console.log(str.at(-1));
posted on 2022-10-29 15:06 Elementinner 阅读(52) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端