1.先执行执行栈中的同步任务
2.异步任务(回调函数)放入任务队列中
3一旦执行栈中的所有同步任务执行完毕,系统聚会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行
同步任务
同步任务都在主线程上执行,形成一个执行栈
js的异步是通过回调函数实现的
异步任务有以下三种类型
1普通事件,如click、resize等
2资源加载,如load、error等
3定时器,包括setIntterval、setTimeout等
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop)
变量实质上是一个具名的值。顾名思义,变量的值是可变的。例如,在开发一个温度监控系统,系统中可能有个变量叫做currentTempC:
let currentTempC = 22;
let关键字是es6新出的。在es6之前,var是唯一用来定义变量的关键字。
let仅用来声明变量,并且只能使用一次。
声明变量的时候也可以不指定初始值,此时变量会有一个特殊的默认值;undefined;
let currentTempC;
let关键字话可以同时声明多个变量:
let currentTempC, rooml=“conferrence_room_a”, room2=“lobby”;
上面的实例代码中声明了三个变量:currentTempC 没有初始值,所以它的值是undefined, room1、room2分别指定了初始值conferrence_room_a和lobby,它们都是字符串(文本)变量。
常量也可以储存值,但它与变量不同:常量一旦初始化就不能在改变。可以用常量来表示舒适的室内温度和最高温度(关键字cost也可以声明多个常量):
const Room_TEMP_c =21.5, MAX_TEMP_C =30;
一般情况下,应该优先使用常量。因为往往只是在努力的给某个数据取个好名字,二七并非改变它的值。是用常量的有优点是可以防止一些不应该更改的值被意外更改。
有一种情况必须用变量而非常量:
3.1 Math概述
Math对象不是构造函数,它具有数学常数和函数的属性和方法。跟数学相关的运算(求绝对值,取整、最大值等)可以使用Math中的成员。
Math.PI
Math. floor ()
Math.ceil ()
Math.round()
Math.abs ()
Math.max () /Math.min()
var str = 'andy';
console.log(str.length);
var temp = new String('andy')
str = temp;
temp = null;
1.获取属性值
element.属性 获取属性值
element.getAttribute(‘属性’);
element.属性 获取属性值(元素本身自带的属性)
element.getAttribute(‘属性’); 主要获得自定义的属性(标准)我们程序员自定义的属性
parentNode属性可返回某节点的父节点,注意数最近的一个节点
如果指定的节点没有父节点则返回null
cheriden
firistChild 第一个子节点/lastChild 最后一个节点 不管是文本点还是元素节点
fristElementChild 返回第一个子元素节点/ lastElementChild
实际开发的写法 既没有兼容性问题又返回第一个子元素
console.log(ol.children[ol.children.length-1]);
nexSibling 下一个兄弟节点 包含元素节点或者 文本节点等等
node.nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回null
node.previousElemetSiblig 返回当前元素上一个兄弟节点,找不到则返回null
创建节点元素节点
var li = document.createElement('li');
2.添加节点 node.appendChild(child) node 父级 child 子级 后面追加元素 类似于数组中的push
var ul = document.querySelector('ul');
ul.appendChild(li);
3.添加节点 node.insertBefore(child, 指点元素);
var lili = doucument.createElement('li');
ul.insertBefore(lili, ul.children[0]);
node.removeChild(child)
node.cloneNode()
如果括号位空或者位false,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
如果括号参数位true,则是深度拷贝,会复制节点本身以及里面的所有子节点。
var lili = ul.children[0].cloneNode(true);
ul.appenChild(lili)
三种动态创建元素区别
doucment.write()
element.innerHTML()
document.createElement()
区别
1 document.write是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
2 innerHTML 是将内容写入某个dom节点,不会导致页面全部重绘
3 innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接)用字符速度慢,结构稍微浮躁。
4 createElement () 创建多个元素效率稍低一点点,但是结构更清晰
总结:不同游览器下,innerHTML 效率要比 creatElement高
// let arr = [1,1,2]
// let arr = new Array(1,1,1)
{
/*push() 在数组后添加元素,并返回新的长度
* @params:数组要新增的元素(任意数据类型,一次可添加多个,用逗号隔开)
* @return:返回数组新增元素后的长度
* 是否改变原数组:改变
* */
let arr = [1, 2, 3];
let res = arr.push(6, 7, 8);
console.log(arr);
console.log(res);
}
{
* @params:数组要新增的元素(任意数据类型,一次可添加多个,用逗号隔开)
* @return:返回数组新增元素后的长度
* 是否改变原数组:改变
* */
let arr = [1, 2, 3];
let res = arr.unshift(6, 7, 8);
console.log(arr);
console.log(res);
}
{
* @params:无
* @return:返回数组被删除的元素
* 是否改变原数组:改变
* */
let arr = [1, 2, 3];
let res = arr.pop();
console.log(arr);
console.log(res);
}
{
*@params:无
*@return:返回数组被删除的元素
*是否改变原数组:改变
* */
let arr = [1, 2, 3];
let res = arr.shift();
console.log(arr);
console.log(res);
}
{
* @params:第一个参数,数组开始下标(必选),第二个参数删除几个(添加几个,取决于第三个是否有参数)(可选),第三个参数添加或替换什么(可选)
* @return: 返回值是一个数组,里面是删除项
* 是否改变原数组:改变
* */
// 删除
let arr = [1, 2, 3];
let res = arr.splice(0, 1)
console.log(arr); //[0,1]
console.log(res); //[2,3] || [1,2,3]
// 增加
let arr2 = [1, 2, 3];
let res2 = arr2.splice(2, 0, 1, 2)
console.log(arr2);
console.log(res2);
// 替换
let arr3 = [1, 2, 3];
let res3 = arr3.splice(1, 1, 1, 2)
console.log(arr3); //[1,1,2,3]
console.log(res3);
let arr4 = [1, 3, 2, 4]
let res4 = arr4.splice(1, 2) //3,2
let res5 = res4.splice(1, 1, 2) //2
let res6 = res5.splice(0, 1, 3, 5, 7) //2
//res6=[2]
let res7 = res6.push(6)
//res6=[2,6]
console.log(res7);
}
// 截取,拼接方法
{
* @params:多个任意项,可以是数组,可以是单个项
* @return:返回合并后的新数组
* 是否改变原数组:不改变
* */
let arr = [1, 2];
let res = arr.concat(1, [0, 1])
console.log(res);
console.log(arr);
}
{
* @params 起始下标与结束下标,起始start必传,end不传默认复制到数组结束位置,可使用负值从数组的尾部选取元素。
* @return:返回复制的子数组
* 是否改变原数组:不改变
* */
let arr = [1, 2, 3, 6, 7, 8];
let res = arr.slice(1, 4);
console.log(arr); //
console.log(res); //2,3,6
}
// 查询方法
{
/* includes()用来判断一个数组是否包含一个指定的值
* @params:参数1必传,表示要查询的元素,参数2选传,表示从指定位置查起(若为负数,从后查起,负数超过数组长度,则置为0)
* @return:返回布尔值
* 是否改变原数组:不改变
* */
let arr = [1, 2, 3];
let res = arr.includes(2);
console.log(res);
}
{
/* indexOf()用来查询检索项在数组中的位置下标
* @params:参数1必传,表示要查询的元素,参数2选传,表示从指定位置查起
* @return:如若检索项存在,返回其下标,没有就返回 -1
* 是否改变原数组:不改变
* */
let arr = [1, 2, 3];
let res = arr.indexOf(2);
let res1 = arr.indexOf(8);
console.log(res);
console.log(res1);
}
{
* @params:参数1必传,表示要查询的元素,参数2选传,表示从指定位置查起
* @return:如若检索项存在,返回其下标,没有就返回 -1
* 是否改变原数组:不改变
* */
let arr = [1, 2, 3, 2, 2, 3, 4];
let res = arr.lastIndexOf(3);
console.log(res);
}
{
/* sort()对数组进行排序
* @params:参数类型是函数
* @return:排序后的数组
* 是否改变原数组:改变
* */
let arr1 = [1, 3, 9, 2, 5, 3, 7, 4, 5]
let arr2 = [1, 3, 9, 1, 5, 4, 7, 8, 5]
arr1.sort((c, d) => c - d)
arr2.sort((a, b) => b - a)
console.log(arr1);
console.log(arr2);
}
{
* @params:无
* @return:倒转排序后的数组
* 是否改变原数组:改变
* */
let arr = [1, 3, 9, 1, 5, 4, 7, 8, 5]
arr.reverse()
console.log(arr)
}
{
/*Join() 把数组的所有元素放入一个字符串,元素通过指定的分隔符进行分割
* @params:指定的分隔符
* @return:转换后的字符串
* 是否改变原数组:不改变
* */
let arr = [1, 2, 3, 3, 4, 5, 5, 7, 9]
let res = arr.join(‘-’)
console.log(res)
}
{
* @params:无
* @return:转换后的字符串
* 是否改变原数组:不改变
* */
let arr = [1, 2, 3, 3, 4, 5, 5, 7, 9]
let res = arr.toString()
console.log(res)
}
{
let arr = [1,2,3,4,5,6]
let res1 = arr.slice(0,3) //123
let res2 = res1.push(2) //1232
let res3 = res1.push(3) //12323
let res4 = res1.splice(1,2) //23
let res6 = res4.reverse() //32
let res5 = res4.unshift(1) //132
console.log(res5);//3
}
函数在使用时分为两步:声明函数和调用函数
声明函数本身并不会执行代码,只有调用函数时才会执行函数体代码
function 函数名(){
函数体
}
(1)声明函数的关键字全部小写
(2)函数是做某件事情,函数名一般是动词
(3)函数不调用自己不执行
函数名();
function 函数名 (形参1,形参2){//在声明函数的小括号里面的是形参(形式上的参数)} 形参是接受实参的
函数名(实参1,实参2) //在函数调用的小括号里面是实参(实际的参数)
1.如果实参的个数和形参的个数一致,则正常输出结果
2.如果实参的个数多于形参的个数,则会取形参的个数
3.如果实参的个数小于形参的个数,形参可以看做是不用声明的变量,num2是一个变量但是没有接受值,结果就是undefined
建议:让形参和实参的个数相匹配
函数返回值的格式
function函数名() {
return 需要返回的结果
}
1我们函数只是实现某种功能,最终的结果需要返回给函数的调用者函数名()通过return实现的
2只要函数遇到return酒吧后面的结果,返回给函数的调用者,函数名()=return后面的结果
3return后面的代码不会被执行
4return只能返回一个值(返回的结果是最后一个值)
5函数如果有return则返回return后面的值
如果没有则返回undefined
break:结束当前的循环体(如for、while)
continue:跳出本次循环,继续执行下次循环(如for、while)
return:不仅可以退出循环,还能够返回return语句中的值,同时还可以结束当前的函数体内的代码
不确定有多少个参数传递的时候,可以用arguments来获取。arguments对象中存储了传递的所有实参。
只有函数才有arguments对象,而且是每个函数都内置好了这个arguments。
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
1.具有length属性
2.按索引方式储存数据
3.不具有数组的push,pop等方法
1.if的语法结构 如果的意思
if(条件表达式){
`}
2.执行思路
如果if里面的调价表达式结果为真true 则执行大括号里面的 执行语句
如果if 条件表达式结果为假 则不执行大括号里面的语句 则执行if 语句后面的代码
3 if else
if(){
}else {
}
执行思路 如果表达式结果为真 那么执行语句1 否则 执行语句2
for/in语句用于循环对象属性
循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作
不要使用 for/in 语句来循环数组的索引,你可以使用 for 语句替代。
语法
for (var in object) {
执行的代码块
}
<!DOCTYPE html>
<html>
<body>
<p>点击按钮循环对象属性。</p>
<button onclick="myFunction()">点我</button>
<p id="demo"></p>
<script>
function myFunction() {
var person = {fname:"John", lname:"Doe", age:25};
var text = "";
var x;
for (x in person) {
text += person[x] + " ";
}
document.getElementById("demo").innerHTML = text;
}
</script>
</body>
</html>
for 循环除了使用 in 方式来循环数组,还提供了一个方式: of , 遍历数组时更加方便。
for…of 是 ES6 新引入的特性。它既比传统的for循环简洁,同时弥补了forEach和for-in循环的短板。
for-of的语法:
for (var value of myArray) {
console.log(value);
}
for-of 的语法看起来跟 for-in 很相似,但它的功能却丰富的多,它能循环很多东西。
for-of 循环使用例子:
循环一个数组(Array):
let iterable = [10, 20, 30];
for (let value of iterable) {
console.log(value);
}
// 10
// 20
// 30
我们可以使用const来替代let,这样它就变成了在循环里的不可修改的静态变量。
循环一个字符串:
let iterable = "boo";
for (let value of iterable) {
console.log(value);
}
// "b"
// "o"
// "o"
文档对象模型,是w3c组织推荐的处理可扩展标记语言(html或者xml)的标准编程接口
w3c已经定义了一系列的dom接口,通过这些dom接口可以改版网页的内容、结构和样式。
1对于JavaScript,为了能够使JavaScrip操作HTML , JavaScript就有了一套自己dom编程接口。
2.对于HTML, dom使得html形成一棵dom树.包含文档、元素、节点
关于dom操作,我们主要针对与元素的操作。主要有创建、增、删、改、查、属性操作、事件操作
给元素添加事件,称为注册事件或者绑定事件。
注册事件有两种方式:传统方式和方法监听注册方式
利用on开头的事件onclick
特点:注册事件的唯一性
同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数—
方法监听注册方式
w3c标准 推荐方法
addEventListener()它是一个方法
ie9之前的ie 不支持此方法, 可使用attachEvent()代替
特点:同一个元素同一个事件可以注册多个监听器
按注册顺序依次执行
eventTarget.addEventLIstener('type',listener[, useCapture])
`eventTarget.addEventLIstener()发给发将指定的监听器注册到eventarget(目标对象)上,当该对象出发指定的事件时,就会执行事件处理函数
该方法接收三个参数:
type:事件类型字符串,比如click、mouseover,注意不要带on,属于字符串要加引号
listener:事件处理函数,事件发生时,会调用该监听函数
useCapture:可选参数,是一个布尔值,默认是false
1传统注册方式
evenTarget.onclick = null;
2方法监听注册方式
1 eventTarget.remoeEvenListener(type, listener[, useCapture]);
2 eventTarget.detachEvent(eventNaeWithOn, callback);
evenTarget.onclick = function(event){}
evenTarget.addEventListener(‘click’ , function(event{}))
这个event就是事件对象,还可以写成e或者evt
event对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。
简单理解:事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象event,他有很多属性和方法
event是个形参,系统帮我们设定为事件对象,不需要传递实参过去。
但我们注册事件时,event对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
e.target 返回触发事件的对象 标准
e.srcEl^nent 返回触发事件的对象 非标准ie6・8使用
e.type 返回事件的类型比如click mouseover不带on
e.cancelBubble 该属性阻止冒泡非标准ie6・8使用
e.returnValue 该属性阻止默认事件(默认行为)非标准ie6-8使用比如不让链接跳转
e.preventDefault() 该方法阻止默认事件(默认行为)标准比如不让链接跳转
e.stopPropagation( ) 阻止圈泡标准
事件委托的原理
不是每个子节点单多设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理设置每个子节点。
onkeyup 某个键盘被松开时触发
onkeydown 某个键盘按键按下时触发 能识别功能键
onkeypress 某个键盘按键被按下时触发 但是它不识别功能键 比如 ctrl shift 箭头等
三个事件的执行顺序 keydown - - keypress – keyup
keyCode 返回该键的ASCII值
注意:onkeydown 和 onkeyup 不区分字母大小写,onkeypress区分字母大小写。在实际开发中,我们更多是哦那个keydown和keyup,他能识别所有的键(包括功能键)Keypress不识别功能键,但是keyCode属性能区分大小写,返回不同的ASCII值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQVtzUKt-1653612972527)(G:\typora图片\js\键盘事件对象.PNG)]
bom是什么?
bom即游览器对象模型,它提供了独立于内容二与游览器窗口进行交互的对象,其核心对象是window。
bom由一系列相关的对象构成,并且每个对象都提供了很多方法与属性
ie9以上才支持
load 要等内容全部加载完毕,包含页面dom元素 图片 flash css 等
DOMContentLoaded 是 DOM 加载完毕,不包含图片falsh css 等就可以执行 加载速度笔 load更快一些
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WofmZikk-1653612972528)(G:\typora图片\js\bom调整窗口大小.PNG)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TTq7mBvm-1653612972529)(G:\typora图片\js\bom定时器.PNG)]
clearTimeout () 方法取消了先前通过调用setTimeout()建立的定时器
setTimeout 延时时间到了,就去调用这个回调函数,只调用一次 就结束了这个定时器
setInterval 每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数
统一资源定位符(uniform resource locator;URL)时互联网上标准资源地址。互联网上的每个文件都有一个唯一的url,它包含的信息指出文件的位置以及游览器应该怎么处理他
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DNKBGt50-1653612972529)(G:\typora图片\js\navigator对象.PNG)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-omAhYQKq-1653612972530)(G:\typora图片\js\history对象.PNG)]
match,test,replace
//match
//返回匹配的值的数组或null。
var regex = /hello/;
var array="hello";
array.match(regex);//true
//test
//返回true或false
var regex = /hello
regex.test("hello");//true
//replace
//正则替换
var str = 'cqyzsC012QzAabcd';
// 全局匹配 忽略大小写
var str1 = str.replace(/a|c|q/gi, '你好')
console.log(str1);//["你好你好yzs你好012你好z你好你好b你好d"]
两种模糊匹配(横向,纵向)
例:
var regex = /hello/;
console.log( regex.test("hello")) =>true //只允许匹配hello
横向模糊匹配
例:
一个正则可匹配的字符串的长度不是固定的,可以是多种情况的。
var regex = /ab{2,5}c/g;
var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc";
console.log( string.match(regex) ); // => ["abbc", "abbbc","abbbbc","abbbbbc"]
案例中用的正则是/ab{2,5}c/g,后面多了g,它是正则的一个修饰符。表示全局匹配,即在目标字符串中按顺序找到满足匹配模式的所有子串,强调的是“所有”,而不只是“第一个”。g是单词global的首字母。
纵向模糊匹配
一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种可能。
var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log( string.match(regex) ); // => ["a1b", "a2b", "a3b"]
[abc],表示该字符是可以字符“a”、“b”、“c”中的任何一个
{},[],()
例:
//大括号 量词符 里面表示重复次数
var reg = /^abc{3}$/;
//只是让c重复三次 abccc
//中括号 字符集合,匹配方括号中的任意字符
var reg = /^[abc]$/;
//a也可以 b也可以 c也可以 a||b||c
//小括号 表示优先级
var reg = /^(abc){3}$/;
//让abc重复三次
范围表示,排除字符组,常用的简写
例:
//范围表示
[123456abcdefGHIJKLM] => [1-6a-fG-M]
匹配“a”、“-”、“z”这三者中任意一个字符
[-az]或[az-]或[a\-z]
//排除字符组
[^abc]
表示是一个除"a"、"b"、"c"之外的任意一个字符。字符组的第一位放^(脱字符),表示求反的概念
//常用的简写
\d就是[0-9]。表示是一位数字。记忆方式:其英文是digit(数字)。
\D就是[^0-9]。表示除数字外的任意字符。
\w就是[0-9a-zA-Z_]。表示数字、大小写字母和下划线。记忆方式:w是word的简写,也称单词字符。
\W是[^0-9a-zA-Z_]。非单词字符。
\s是[ \t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。记忆方式:s是space character的首字母。
\S是[^ \t\v\n\r\f]。 非空白符。
简写形式,贪婪匹配和惰性匹配
例:
//量词符:用来设定某个模式出现的次数
//简单理解:就是人下面的a这个字符重复多少次
{m,} 表示至少出现m次。
{m} 等价于{m,m},表示出现m次。
? 等价于{0,1},表示出现或者不出现。记忆方式:问号的意思表示,有吗?
+ 等价于{1,},表示出现至少一次。记忆方式:加号是追加的意思,得先有一个,然后才考虑追加。
* 等价于{0,},表示出现任意次,有可能不出现。记忆方式:看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。
//贪婪匹配
var regex = /\d{2,5}/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex)); // => ["123", "1234", "12345", "12345"]
//其中正则/\d{2,5}/,表示数字连续出现2到5次。会匹配2位、3位、4位、5位连续数字。只要在能力范围内,越多越好
//惰性匹配
var regex = /\d{2,5}?/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) ); // => ["12", "12", "34", "12", "34", "12", "34", "56"]
//其中/\d{2,5}?/表示,虽然2到5次都行,当2个就够的时候,就不在往下尝试了
例:
//用| =>表示其中任何之一
var regex = /good|nice/g;
var string = "good idea, nice try.";
console.log( string.match(regex) ); // => ["good", "nice"]
//要匹配"good"和"nice"可以使用/good|nice/
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) ); // => ["good"]
//用/good|goodbye/,去匹配"goodbye"字符串时,结果是"good":
var regex = /goodbye|good/g;
var string = "goodbye";
console.log( string.match(regex) ); // => ["goodbye"]
//修改过后会优先匹配|前面的字符,也就是说,分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了
创建object有两种方式,
//第一种new操作符和object构造函数
let person = new Object();
person.name = "Nicholas";
person.age = 29;
//第二种对象字面量方法
let person = {
name: "Nicholas",
age: 29
};
//在对象字面量表示法中,属性名可以是字符串或数值,比如:
let person = {
"name": "Nicholas",
"age": 29,
5: true
};
let person = {}; // 与 new Object()相同
person.name = "Nicholas";
person.age = 29;
//有几种基本的方式可以创建数组。一种是使用 Array 构造函数,比如:
let colors = new Array();
//如果知道数组中元素的数量,那么可以给构造函数传入一个数值,然后 length 属性就会被自动创
//建并设置为这个值。比如,下面的代码会创建一个初始 length 为 20 的数组:
let colors = new Array(20);
//也可以给 Array 构造函数传入要保存的元素。比如,下面的代码会创建一个包含 3 个字符串值的
//数组:
let colors = new Array("red", "blue", "green");
//创建数组时可以给构造函数传一个值。这时候就有点问题了,因为如果这个值是数值,则会创建一
//个长度为指定数值的数组;而如果这个值是其他类型的,则会创建一个只包含该特定值的数组。下面看
//一个例子:
let colors = new Array(3); // 创建一个包含 3 个元素的数组
let names = new Array("Greg"); // 创建一个只包含一个元素,即字符串"Greg"的数组
//在使用 Array 构造函数时,也可以省略 new 操作符。结果是一样的,比如:
let colors = Array(3); // 创建一个包含 3 个元素的数组
let names = Array("Greg"); // 创建一个只包含一个元素,即字符串"Greg"的数组
//另一种创建数组的方式是使用数组字面量(array literal)表示法。数组字面量是在中括号中包含以
//逗号分隔的元素列表,如下面的例子所示:
let colors = ["red", "blue", "green"]; // 创建一个包含 3 个元素的数组
let names = []; // 创建一个空数组
let values = [1,2,]; // 创建一个包含 2 个元素的数组
注意事项
1、再es6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
2、类里面的共有的属性和方法一定要加this使用
3、constructor里面this指向实例对象,方法里面的this指向这个方法的调用者
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,总与new一起使用,我们可以把对象中一些公共的熟悉感和方法抽取出来,然后封装到这个函数里面。
fillter()
用于创建一个新的数组,新数组中的元素是通过检查指定数组中复合条件的所有元素,主要用于筛选数组,并且返回一个新数组
some()方法
用于检测数组中的元素是否满足指定条件,查找数组中是否有满座条件的数组
注意返回的是布尔值,查到返回true,没查到返回false。如果找到第一个满足条件的元素,则终止循环,不查找
//1普通函数
function fn() {
console.log('普通函数的this'+this)
}
//指向的是window
fn();
//2对象的方法
var o = {
sayhi: function () {
console.log(this)
}
}
//指向o对象
o.sayhi();
//3构造函数
//this指向ldh这个实例对象 原型对象里面的this 指向的也是ldh 这个实例对象
function Star() {
console.log(this)
}
Star.prototype.sing = function (){
}
var ldh = new Star();
//4绑定事件函数
btn.onclick = function (){
console.log(this)} //this指向调用者
//5定时器函数
nsetInterval(function (){console.log(this)},1000);//this指向window
//6立即执行函数
(function (){
console.log(this)
})() //this指向window
call()方法
call方法会调用函数,并且改变函数内部this指向
bind()方法
bind方法不会调用函数,但是能改变函数内部this指向
返回有指定的this值和初始化参数改造的原函数拷贝
如果有的函数我们不需要立即调用,又想改变这个函数内部的this指向此时使用bind
总结
相同点:都可以改变函数内部this的指向
区别点:
1、call和apply回调用函数,并且改变函数内部this指向
2、call和apply传递的参数不一样,call传递参数aru1,aru2…形式 apply必须数组形式
3、bing不会调用函数,剋改变函数内部this指向
主要应用场景
1、call经常做继承
2、apply经常跟数组有关系,比如借助与数学对象实现数组最大值最小值
3、bing不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。
高阶函数是对其他函数进行操作的函数,它接受函数作为参数火将函数作为返回值输出
function fn(a, b, callback) {
console.log(a + b)
callback && callback();
}
fn(1, 2, function () {
console.log('我是最后调用的')
}
);
//此时fn就是一个高阶函数
//函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用,最典型的就是作为回调函数
filter(function or None, iterable) 筛选可迭代对象iterable 中的数据,返回一个可迭代对象,此可迭代对象将对iterable生成的数据进行筛选
filter中的回到函数必须返回一个boolean值
function 数将对iterable中每个元素进行求值,返回Flase则将此数据丢充,
返回True则保留此数据
const nums = [10,20,30,404,90,100,234,99];
let newNums = nums.filter(function (n){
return n < 100;
})
console.log(newNums);
好处:
1、消除js语法的不合理、不严谨、减少怪异行为
2、消除掉吗运行的一些不安全指出,保证代码运行的安全
3、提高编译器效率,增加运行速度
4、警用ecmascript的未来版本中可能会定义的一些语法,为未来版本的js做好铺垫。
变化
1.变量先声明在使用
2.不能删除已经定义的变量
3.严格模式下全局作用域中的函数this是undefined
4.严格模式下,如果构造函数不加new调用,this会报错
5.函数不能有重名的参数
6.函数必须声明在顶层
闭包指有权访问另一个函数作用域中的变量的函数。
作用:延申了变量的作用范围
变量作用域
1.函数内部可以使用全局变量
2.函数外部不可以使用局部变量
3.当函数执行完毕,本作用域内的局部遍历会销毁
如果一个函数在内部调用其本身,那么这个函数就是递归函数
递归函数的作用和循环效果一样
由于递归容易发生‘栈溢出’, 所有必须要加退出条件return
1、浅拷贝只是拷贝一层,更深层对象级别的只拷贝引用
2、深拷贝拷贝多层,每一集别的数据都会拷贝
3、Object.assign(target,…sources) es6新增方法可以浅拷贝 浅拷贝的语法糖
用test方法来检测字符串是否符合要求
量词符
*相当于可以出现零次或者很多次
+相当于要出现 >=1 次或者很多次
?相当于只能出现1次或者不出现
{ 3 } 只能重复3次
{3,}相当于出现 大于等于3次
{3,16} 大于3次 并且 小于等于16
let声明的变量具有块级作用域
1.在一个大括号中let关键字才具有块级作用域,var关键字是不具备这个特点的
2.let要先声明在使用,因为let不存在变量提升
3.暂时性死区
var num = 10;
if(true){
console.log(num);
let num = 20; //num id not defind 此时let里的num和log绑定一个块级形成了暂时性死区
}
作用:声明常量,常量就是值(内存地址)不能变化的量
1.具有块级作用域
2.声明时必须赋值
3.赋值后不能更改,除了复杂数据类型
1、使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
2、使用let声明的变量,起作用域为噶语句所在的代码块内,不存在变量提升问题
3、使用const声明的时常量,在后面出现的代码不能在修改常量的值
假如函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
function fn(num2,num3){
return num2+num3;
}
console.log(fn(1, 5));;
const sum = (num1,num2) => num1 + num2;
console.log(sum(1, 4));
//如果形参只有一个,可以省略小括号
function fn(v){
return v;
}
const fn = v => v;
箭头函数不绑定this关键字,箭头函数中的this,指向的时函数定义位置的上下文this
promise解决了回调地狱
promise是一个构造函数,通过new关键字实例化对象
语法
new Promise((resolve,reject)=>{})
Promise接受一个函数作为参数
在函数中接受两个参数
resolve 成功函数
reject 失败函数
promise实例
promise实例有两个属性
state:状态
result:结果
第一种状态:pending(准备,待解决,进行中)
第二种状态:fulfilled(已完成,成功)
第三种状态:rejected(已拒绝,失败)
通过resolve和reject改变当前状态的改变
实例
const p = new Promise((resolve, reject) => {
//resolve():调用函数,可以使promise对象的状态改成fulfilled
//reject():调用函数,使当前promise对象的状态改成rejected
resolve();
})
console.dir(p) //fulfilled
promise状态的改变是一次性的
实例
const p = new Promise((resolve, reject) => {
//通过调用resolve,传递参数,改变当前promise对象的结果
// resolve('成功的结果');
reject('失败的结果');
})
console.dir(p)
const p = new Promise((resolve, reject) => {
//通过调用resolve,传递参数,改变当前promise对象的结果
// resolve('成功的结果');
reject('失败的结果');
})
/*
then方法函数
参数
1、是一个函数
2、还是一个函数
返回值:是一个promise对象
*/
p.then((value)=>{
//当promise的状态是fulfilled时,执行
console.log(value)
},(reason)=>{
//当promise的状态是rejected时,执行
console.log(reason)
})
console.log(p)
then方法返回一个新的promise实例,状态是pending
promise的状态不改变,不会执行then里的方法
在then方法中,可以通过return将返回的promise实例改为fulfilled状态
//如果promise的状态不改变,then里的方法不会执行
const p = new Promise((resolve, reject) => {
resolve(1231)
})
const t = p.then((value) => {
console.log('成功',value)
return 132131;
//使用return可以将t实例的状态改为fulfilled
}, (reason) => {
console.log('失败');
})
t.then((value) => {
console.log('成功2',value);
}, (reason) => {
console.log('失败');
})
如果在then方法中,出现代码错误,会将返回的promise实例改为rejected状态
//如果promise的状态不改变,then里的方法不会执行
const p = new Promise((resolve, reject) => {
resolve(1231)
})
const t = p.then((value) => {
console.log('成功',value)
//使用return可以将t实例的状态改为fulfilled
// return 132131;
//如果这里的代码出错,会将t实例的状态改成rejected
console.log(a)
}, (reason) => {
console.log('失败');
})
t.then((value) => {
console.log('成功2',value);
}, (reason) => {
console.log('失败',reason);
})
实例
const p = new Promise((resolve, reject) => {
// reject();
// console.log(a)
throw new Error('出错了')
})
//思考:catch中的参数哈数在什么时候被执行?
//1.当promise的状态改为rejected时,被执行
//2.当promise执行体中出现代码错误时,被执行
p.catch((res) => {
console.log('失败',res);
})
console.log(p)
实例
function getData(url, data = {}) {
return new Promise((resolve, reject) => {
$.ajax({
type: 'GET',
url: url,
data: data,
success: function (res) {
//promise的状态为成功,修改promise的结果为res
resolve(res);
},
error: function (res) {
//promise的状态为失败,修改promise的结果为res
reject(res)
}
})
})
}
getData('resource/content.json')
.then((data) => {
const {id} = data //对象解构
return getData('resource/content2.json', {id})
})
.then((data) => {
const {username} = data
return getData('resource/content3.json', {username})
})
.then((data) => {
console.log(data.title)
})
ES2016 新增了一个指数运算符(**
)。
2 ** 2 // 4
2 ** 3 // 8
这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
上面代码中,首先计算的是第二个指数运算符,而不是第一个。
指数运算符可以与等号结合,形成一个新的赋值运算符(**=
)。
let a = 1.5;
a **= 2;
// 等同于 a = a * a;
let b = 4;
b **= 3;
// 等同于 b = b * b * b;
编程实务中,如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。比如,读取message.body.user.firstName
这个属性,安全的写法是写成下面这样。
// 错误的写法
const firstName = message.body.user.firstName || 'default';
// 正确的写法
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
上面例子中,firstName
属性在对象的第四层,所以需要判断四次,每一层是否有值。
三元运算符?:
也常用于判断对象是否存在。
const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined
上面例子中,必须先判断fooInput
是否存在,才能读取fooInput.value
。
这样的层层判断非常麻烦,因此 ES2020 引入了“链判断运算符”(optional chaining operator)?.
,简化上面的写法。
const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value
上面代码使用了?.
运算符,直接在链式调用的时候判断,左侧的对象是否为null
或undefined
。如果是的,就不再往下运算,而是返回undefined
。
下面是判断对象方法是否存在,如果存在就立即执行的例子。
iterator.return?.()
上面代码中,iterator.return
如果有定义,就会调用该方法,否则iterator.return
直接返回undefined
,不再执行?.
后面的部分。
对于那些可能没有实现的方法,这个运算符尤其有用。
if (myForm.checkValidity?.() === false) {
// 表单校验失败
return;
}
上面代码中,老式浏览器的表单对象可能没有checkValidity()
这个方法,这时?.
运算符就会返回undefined
,判断语句就变成了undefined === false
,所以就会跳过下面的代码。
链判断运算符?.
有三种写法。
obj?.prop
// 对象属性是否存在obj?.[expr]
// 同上func?.(...args)
// 函数或对象方法是否存在下面是obj?.[expr]
用法的一个例子。
let hex = "#C0FFEE".match(/#([A-Z]+)/i)?.[1];
上面例子中,字符串的match()
方法,如果没有发现匹配会返回null
,如果发现匹配会返回一个数组,?.
运算符起到了判断作用。
下面是?.
运算符常见形式,以及不使用该运算符时的等价形式。
a?.b
// 等同于
a == null ? undefined : a.b
a?.[x]
// 等同于
a == null ? undefined : a[x]
a?.b()
// 等同于
a == null ? undefined : a.b()
a?.()
// 等同于
a == null ? undefined : a()
上面代码中,特别注意后两种形式,如果a?.b()
和a?.()
。如果a?.b()
里面的a.b
有值,但不是函数,不可调用,那么a?.b()
是会报错的。a?.()
也是如此,如果a
不是null
或undefined
,但也不是函数,那么a?.()
会报错。
使用这个运算符,有几个注意点。
(1)短路机制
本质上,?.
运算符相当于一种短路机制,只要不满足条件,就不再往下执行。
a?.[++x]
// 等同于
a == null ? undefined : a[++x]
上面代码中,如果a
是undefined
或null
,那么x
不会进行递增运算。也就是说,链判断运算符一旦为真,右侧的表达式就不再求值。
(2)括号的影响
如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。
(a?.b).c
// 等价于
(a == null ? undefined : a.b).c
上面代码中,?.
对圆括号外部没有影响,不管a
对象是否存在,圆括号后面的.c
总是会执行。
一般来说,使用?.
运算符的场合,不应该使用圆括号。
(3)报错场合
以下写法是禁止的,会报错。
// 构造函数
new a?.()
new a?.b()
// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`
// 链判断运算符的左侧是 super
super?.()
super?.foo
// 链运算符用于赋值运算符左侧
a?.b = c
(4)右侧不得为十进制数值
为了保证兼容以前的代码,允许foo?.3:0
被解析成foo ? .3 : 0
,因此规定如果?.
后面紧跟一个十进制数字,那么?.
不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数。
读取对象属性的时候,如果某个属性的值是null
或undefined
,有时候需要为它们指定默认值。常见做法是通过||
运算符指定默认值。
const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;
上面的三行代码都通过||
运算符指定默认值,但是这样写是错的。开发者的原意是,只要属性的值为null
或undefined
,默认值就会生效,但是属性的值如果为空字符串或false
或0
,默认值也会生效。
为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符??
。它的行为类似||
,但是只有运算符左侧的值为null
或undefined
时,才会返回右侧的值。
const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;
上面代码中,默认值只有在左侧属性值为null
或undefined
时,才会生效。
这个运算符的一个目的,就是跟链判断运算符?.
配合使用,为null
或undefined
的值设置默认值。
const animationDuration = response.settings?.animationDuration ?? 300;
上面代码中,如果response.settings
是null
或undefined
,或者response.settings.animationDuration
是null
或undefined
,就会返回默认值300。也就是说,这一行代码包括了两级属性的判断。
这个运算符很适合判断函数参数是否赋值。
function Component(props) {
const enable = props.enabled ?? true;
// …
}
上面代码判断props
参数的enabled
属性是否赋值,基本等同于下面的写法。
function Component(props) {
const {
enabled: enable = true,
} = props;
// …
}
??
本质上是逻辑运算,它与其他两个逻辑运算符&&
和||
有一个优先级问题,它们之间的优先级到底孰高孰低。优先级的不同,往往会导致逻辑运算的结果不同。
现在的规则是,如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。
// 报错
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs
上面四个表达式都会报错,必须加入表明优先级的括号。
(lhs && middle) ?? rhs;
lhs && (middle ?? rhs);
(lhs ?? middle) && rhs;
lhs ?? (middle && rhs);
(lhs || middle) ?? rhs;
lhs || (middle ?? rhs);
(lhs ?? middle) || rhs;
lhs ?? (middle || rhs);
ES2021 引入了三个新的逻辑赋值运算符(logical assignment operators),将逻辑运算符与赋值运算符进行结合。
// 或赋值运算符
x ||= y
// 等同于
x || (x = y)
// 与赋值运算符
x &&= y
// 等同于
x && (x = y)
// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)
这三个运算符||=
、&&=
、??=
相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。
它们的一个用途是,为变量或属性设置默认值。
// 老的写法
user.id = user.id || 1;
// 新的写法
user.id ||= 1;
上面示例中,user.id
属性如果不存在,则设为1
,新的写法比老的写法更紧凑一些。
下面是另一个例子。
function example(opts) {
opts.foo = opts.foo ?? 'bar';
opts.baz ?? (opts.baz = 'qux');
}
上面示例中,参数对象opts
如果不存在属性foo
和属性baz
,则为这两个属性设置默认值。有了“Null 赋值运算符”以后,就可以统一写成下面这样。
function example(opts) {
opts.foo ??= 'bar';
opts.baz ??= 'qux';
}
`或`undefined`时,才会返回右侧的值。
```javascript
const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;
上面代码中,默认值只有在左侧属性值为null
或undefined
时,才会生效。
这个运算符的一个目的,就是跟链判断运算符?.
配合使用,为null
或undefined
的值设置默认值。
const animationDuration = response.settings?.animationDuration ?? 300;
上面代码中,如果response.settings
是null
或undefined
,或者response.settings.animationDuration
是null
或undefined
,就会返回默认值300。也就是说,这一行代码包括了两级属性的判断。
这个运算符很适合判断函数参数是否赋值。
function Component(props) {
const enable = props.enabled ?? true;
// …
}
上面代码判断props
参数的enabled
属性是否赋值,基本等同于下面的写法。
function Component(props) {
const {
enabled: enable = true,
} = props;
// …
}
??
本质上是逻辑运算,它与其他两个逻辑运算符&&
和||
有一个优先级问题,它们之间的优先级到底孰高孰低。优先级的不同,往往会导致逻辑运算的结果不同。
现在的规则是,如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。
// 报错
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs
上面四个表达式都会报错,必须加入表明优先级的括号。
(lhs && middle) ?? rhs;
lhs && (middle ?? rhs);
(lhs ?? middle) && rhs;
lhs ?? (middle && rhs);
(lhs || middle) ?? rhs;
lhs || (middle ?? rhs);
(lhs ?? middle) || rhs;
lhs ?? (middle || rhs);