目录
🆙【前文回顾】👉 ES6_箭头函数、for of的简谈与应用_13
一. 模板字符串
二. let的应用
三. 箭头函数
四. for of
五. 参数增强:3项技能
1. 参数默认值(default):
(1). 什么是:即使调用函数时,没有传入实参值,也希望形参变量有默认的值作为备胎使用。
(2). 如何: 定义函数时:
function 函数名(形参变量1=默认值){
... ...
}
(3). 结果: 将来调用时:
a. 如果传入了实参值,则形参变量采用传入的实参值
b. 如果没有传入实参值,则形参变量将默认值作为备胎使用
(4). 示例: 为函数参数指定默认值:
1_default.html ⏬
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//定义一个函数做自我介绍:
function intr(str="主人很懒,什么也没有留下"){
console.log(`我的自我介绍是:${str}`)
}
intr("you can you up!");
intr();
</script>
</body>
</html>
运行结果:
我的自我介绍是:you can you up!
我的自我介绍是:主人很懒,什么也没有留下
(5). 问题: 虽然所有形参变量都可设置默认值,但是只靠默认值只能解决结尾几个参数没给的情况。无法解决开头几个参数不给的情况!
(6). 解决: 今后如果想实现无论哪个实参值不给都可使用默认值时,应该使用默认值+参数解构方式(参看解构之参数解构,稍后讲...)
(7). 示例: 尝试用默认值解决多个参数值不确定的情况
2_default2.html ⏬
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//定义函数,点套餐
function order(zhushi="香辣鸡腿堡", xiaochi="辣翅", yinliao="可乐"){
console.log(`您点的套餐是:
主食:${zhushi},
小吃:${xiaochi},
饮料:${yinliao}
`)
}
//第一个人不着急,每个都挑
order("香辣鸡腿堡","辣翅","可乐");
//第二个人着急,不挑,就选默认套餐搭配
order();
//第三个人只想换主食,小吃和饮料保持默认
//给了第一个实参值,没给后两个实参值——支持
order("奥尔良烤腿堡");
//第四个人只想换饮料,主食和小吃保持默认
//不给前两个实参值,只给最后一个实参值——不支持
//错误1:
// order("咖啡");
//错误2:
// order(,,"咖啡");
//不好:
// order(undefined, undefined, "咖啡");
</script>
</body>
</html>
运行结果:
您点的套餐是:
主食:香辣鸡腿堡,
小吃:辣翅,
饮料:可乐
您点的套餐是:
主食:香辣鸡腿堡,
小吃:辣翅,
饮料:可乐
您点的套餐是:
主食:奥尔良烤腿堡,
小吃:辣翅,
饮料:可乐
2. 剩余参数(rest)
(1). 问题: ES6的箭头函数中禁止使用arguments了!
(2). 解决: 今后在ES6中,只要一个函数不确定将来有多少个实参值时,都用剩余参数语法代替arguments。
(3). 如何: 定义函数时
其它实参值|收集剩余不确定个数的实参值
↓ \ ↓ /
var 函数名=(其它形参,...数组名)=>{
}
(4). 原理:
a. 调用函数时,先将开头的实参值,按顺序依次一对一传给开头确定的形参变量
b. 如果还有剩余的没人要的实参值,才会被...运算符收集到自定义的数组中保存
c. 在函数内,可通过遍历数组的方式,获取除开头几个固定的实参值之外,剩余的不确定的那些实参值列表。
(5). 示例: 使用剩余参数实现计算不确定个数的值求和
3_rest.html ⏬
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//定义一个函数,计算任意多个数字的和
// add( 1,2,3 )
var add=( ...arr )=>{
// \收集实参 /
//...arr相当于var arr=[1,2,3 ]
var total=0;
for(var num of arr){
total+=num;
}
return total;
}
console.log(add(1,2,3));//6
console.log(add(1,2,3,4,5));//15
</script>
</body>
</html>
运行结果:
6
15
(6). 示例: 使用剩余参数计算员工的薪资
4_rest2.html ⏬
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//定义一个计算薪资的函数
//要求先传入员工姓名,必须
//之后有几项工资,不确定
// "Li Lei"| 剩余->
// ↓ \↓ ↓ ↓/
var jisuan=(ename,...arr)=>{
var total=0;
for(var money of arr){
total+=money;
}
console.log(`${ename}的总工资是${total}`)
}
jisuan("Li Lei", 10000, 1000, 2000);
jisuan("Han Meimei", 2000,3000,4000,5000,1000)
</script>
</body>
</html>
运行结果:
Li Lei的总工资是13000
Han Meimei的总工资是15000
3. 打散数组(spread)
(1). 问题: apply虽然有打散数组的功能,但是本职工作是替换this。如果遇到某些与this无关,只需要打散数组的需求时,也会强迫我们必须提供第一个实参值代替this——矛盾!
(2). 解决: ES6中提供了一种单纯专门打散数组的方式:spread
(3). 如何: 调用函数时
函数名( ...数组 )
/打散数组\
(4). 原理: ...会先将数组打散为多个元素值,再分别依次一对一传给函数的形参变量
(5). 总结: 同样是...,什么时候是收集,什么时候是打散?
a. 定义函数时,形参列表中的...是收集作用
b. 调用函数时,实参值列表中的...是打散作用
(6). 示例: 使用...代替apply
5_spread.html ⏬
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//js中Math对象有两个函数
console.log(Math.max(2,7,3,5));//7
console.log(Math.min(2,7,3,5));//2
//如果多个数字是放在一个数组中的
var arr=[2,7,3,5];
console.log(Math.max(arr));//NaN
console.log(Math.min(arr));//NaN
//结论:Math.max()和Math.min()不支持获得数组中的最大值和最小值
//解决: 如果可以把数组先打散,再传给Math.max()和Math.min()就可获得最大值和最小值了
//不好的做法:
//由于跟this无关,所以apply函数替换this的对象的占位写什么都可以,不影响
console.log(Math.max.apply(null,arr));
console.log(Math.min.apply(null,arr));
//apply先把arr打散为多个值,再传入max
//但是, apply本职工作是替换this,顺便打散数组,所以必须提供第一个实参值,代替this。而本例中,需求与this无关!——矛盾
//好的做法:
console.log(Math.max(...arr));
console.log(Math.min(...arr));
</script>
</body>
</html>
运行结果:
7
2
NaN
NaN
7
2
7
2
👨🌾 扩展:关于Math.max()
1、Math.max()函数只能传入一组参数来求最大值,所以如果是要用于求一个数组中的最大值时,可以用Math.max.apply(Math,array),把this值指向Math对象,则第二个参数可以传入任意数组。2、当给Math.max()或Math.min()函数传参时,若参数中有非数值的项,则会返回NaN。
◼️ Math.max的应用
console.log(Math.max(10,333,3432,33,32,5)); // 3432 console.log(Math.max.apply(null,[10,333,3432,33,32,5])); //3432 console.log(Math.max.call(null,1,3,33434)) //33434
函数 Math.max(x);的参数是 Number 类型,可以是小数,整数,正数,负数或者是
0,如果不是上面所述类型就会返回 NaN。
(7). 语法糖:
a. 复制一个数组: var arr2=[...arr1]; // 打散数组arr1,然后放入数组arr2中
b. 拼接多个数组为一个新数组: var arr3=[...arr1, ...arr2] // 打散2个数组,放入arr3中
c. 复制一个对象: var obj2={...obj1}; // 打散对象obj1中的名值对儿,放入对象obj2
d. 合并多个对象为一个大对象: var obj3={...obj1, ...obj2}
(8). 示例: 使用语法糖实现指定功能
6_spread2.html ⏬
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//复制一个数组
var arr1=[1,2,3,4,5];
var arr2=[...arr1];
console.log(arr2);//1,2,3,4,5
console.log(arr2==arr1);//false 地址不同
//拼接多个数组为一个大的数组
var arr1=[1,2,3];
var arr2=[5,6,7];
//还可以为新数组添加额外新的元素进行拼接
var arr3=[...arr1,4,...arr2,8];
console.log(arr3);
//复制一个对象
var lilei={
sname:"Li Lei",
sage:11
};
var lilei2={...lilei};
console.log(lilei2);
console.log(lilei2==lilei);//false
//合并多个对象为一个大的对象
var obj1={x:1,y:2};
var obj2={i:4,j:5};
var obj3={
...obj1,
//还可以为大对象添加额外新的对象属性进行合并
z:3,
...obj2,
k:6
}
console.log(obj3);
</script>
</body>
</html>
运行结果:
(5) [1, 2, 3, 4, 5]
false
(8) [1, 2, 3, 4, 5, 6, 7, 8]
{sname: "Li Lei", sage: 11}
false
{x: 1, y: 2, z: 3, i: 4, j: 5, …}
👨🌾 扩展:何为语法糖?
“语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。”
六. 解构(destruct)
1. 存在问题
今后开发中有些数组或对象特别大,内容特别多!但是,我们每次可能只需要使用其中一小部分功能或内容!如果带着数组和对象使用,就很繁琐
2. 解决方案
今后如果只需要使用一个大的数组或对象中的一小部分内容时,都要先将要使用的内容从数组或对象提取出来,再单独使用!——解构
3. 如何使用解构
3种:
(1). 数组解构:
a. 什么是: 从一个大的数组中,提取出个别元素,保存在变量中,单独使用!
b. 如何:
//装扮
[变量1, 变量2, ...]=数组
0 1
c. 原理: =右边的数组,会将相同位置的元素值,自动赋值给=左边[]中相同位置的变量
d. 结果:
变量1=数组[0]
变量2=数组[1]
... ...
e. 示例: 使用数组解构,解构出数组中指定位置的元素值单独使用
7_destruct_arr.html ⏬
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var arr=[2010, 10, 12];
// 0 1 2
var [y,m,d]=arr;
// 0 1 2
console.log(`今年是${y}年`);
console.log(`本月是${m}月`);
console.log(`今天是${d}号`);
var [ , ,d]=arr;
// 2
console.log(`今天是${d}号`);
</script>
</body>
</html>
运行结果:
今年是2010年
本月是10月
今天是12号
今天是12号
(2). 对象解构:
a. 什么是: 从一个大的对象中提取出个别属性值,单独使用
b. 如何:
//装扮
var {
配对儿 接属性值
属性名: 变量名,
... : ...
}=对象
c. 原理: =右边的对象,会将相同属性名的属性值,自动赋值给=左边{}中相同属性名对应的变量。
d. 结果:
变量名=对象.属性名
e. 问题: 通常原对象中的属性名已经很好用了!无需改变的!但是,如果解构时,变量名也使用原属性名,则:前后就会重复出现相同的名字!
f. 解决: ES6中如果:前后的属性名和变量名相同!其实只需要写一个名字即可!一个名字两用!即当做属性名,又当做变量名!
var {属性名1, 属性名2, ...} = 对象
g. 示例: 使用对象解构,解构出对象中的成员单独使用:
8_destruct_obj.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var lilei={
sname:"Li Lei",
sage:11
}
// 属性名 变量名 属性名 变量名
// var {sname:sname, sage:sage }=lilei;
var { sname, sage }=lilei;
console.log(`学生姓名:${sname}`);
console.log(`学生年龄:${sage}`);
</script>
</body>
</html>
运行结果:
学生姓名:Li Lei
学生年龄:11
(3). 参数解构:
a. 问题: 如果一个函数多个实参值都不确定有没有(←如果是这样的需求可以使用剩余参数或是arguments即可,但是做不到实参值必须传给指定的形参变量),而且,还要求实参值必须传给指定的形参变量!经过试验,只靠参数默认值或剩余参数语法或arguments都无法完全满足需求!
b. 解决: 参数解构:
c. 如何: 2步:
1). 定义函数时: 将所有的形参变量都放在一个对象结构中:
function 函数名({
配对儿 接相同属性名的实参值
属性名1: 形参1,
属性名2: 形参2,
... : ...
}){
函数体
}
2). 调用函数时(如何传参): 所有实参值也必须放在一个相同的对象结构中整体传入!
函数名({
配对儿
属性名1: 实参值1,
属性名2: 实参值2,
... : ...
})
d. 结果:
形参1=实参值1
形参2=实参值2
e. 简写: 通常用于配对儿的:左边的属性名和:右边实际接收实参值的形参名都是保持一致的,所以:左右两边的属性名和形参名重复了,所以,可以简写为一个名字。但是,这一个名字两用。即当做配对的属性名,又当做形参名
function 函数名({ 属性名1=默认值1, 属性名2=默认值2, ... }){
... ...
}
f. 示例: 使用参数解构和默认值解决多个参数不确定,且要求实参必须传给指定的形参的问题。
9_destruct_param.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//定义函数,点套餐
// function order({
// //属性名 形参名
// //配对儿 接实参值
// zhushi:zhushi,
// xiaochi:xiaochi,
// yinliao:yinliao
// }){
function order({
//一个名字两用
//即配对儿,又用作形参接实参值(即简写又可设置默认值)
zhushi="香辣鸡腿堡",//默认值
xiaochi="辣翅",//默认值
yinliao="可乐"//默认值
}){
console.log(`您点的套餐是:
主食:${zhushi},
小吃:${xiaochi},
饮料:${yinliao}
`)
}
//第一个人不着急,每个都挑
order({
zhushi:"香辣鸡腿堡",
xiaochi:"辣翅",
yinliao:"可乐"
});
//第二个人着急,不挑,就选默认套餐搭配
order({});
//第三个人只想换主食,小吃和饮料保持默认
//给了第一个实参值,没给后两个实参值——支持
order({
zhushi:"奥尔良烤腿堡"
});
//第四个人只想换饮料,主食和小吃保持默认
//不给前两个实参值,只给最后一个实参值——不支持
//错误1:
// order("咖啡");
//错误2:
// order(,,"咖啡");
//不好:
// order(undefined, undefined, "咖啡");
//好的方法:
order({
yinliao:"咖啡"
});
//第五个人想换小吃和饮料
order({
yinliao:"奶茶",
xiaochi:"菠萝派"
})
</script>
</body>
</html>
运行结果:
您点的套餐是:
主食:香辣鸡腿堡,
小吃:辣翅,
饮料:可乐
分析:order函数会去order({})找,有没有一个名称为zhushi的属性,但是没找到,则就会返回undefined——因为访问强行访问一个对象中不存在的属性,返回的是undefined。此时默认值就发挥作用了
您点的套餐是:
主食:香辣鸡腿堡,
小吃:辣翅,
饮料:可乐
您点的套餐是:
主食:奥尔良烤腿堡,
小吃:辣翅,
饮料:可乐
您点的套餐是:
主食:香辣鸡腿堡,
小吃:辣翅,
饮料:咖啡
您点的套餐是:
主食:香辣鸡腿堡,
小吃:菠萝派,
饮料:奶茶
💥 扩展:this判断—8种指向
this 8种指向: 判断this,一定不要看定义在哪儿!只看调用时!
1. obj.fun() this->obj
2. fun() 或 (function(){ ... })() 或 多数回调函数 或 定时器函数 this->window
3. new Fun() this->new正在创建的新对象
4. 类型名.prototype.共有方法=function(){ ... } this->将来谁调用指谁,同第一种情况
5. DOM或jq中事件处理函数中的this->当前正在触发事件的DOM元素对象
如果需要使用简化版函数,必须$(this)
6. 箭头函数中的this->箭头函数外部作用域中的this
7. jQuery.fn.自定义函数=function(){ ... } this->将来调用这个自定义函数的.前的jQuery子对象,不用再$(this)
8. new Vue()中methods中的函数中的this->当前new Vue()对象
💥 扩展:遍历数字下标的东西,首推for of
for(var i=0; i<arr.length; i++) | forEach | for of | for in | ||
数字下标 | 索引数组 | √ | √ | √ | 不保险 |
类数组对象 | √ | × | √ | 不保险 | |
字符串 | √ | × | √ | 不保险 | |
自定义名称下标 | 关联数组 | × | × | × | √ |
对象 | × | × | × | √ |
💠 总结:知识点提炼
1. 只要验证字符串格式或查找、屏蔽敏感词时都要用正则
(1). 最简单的正则: 一个敏感词的原文
(2). 某一位字符上可能有多种备选字时用: [备选字列表]
(3). 如果[]中部分字符是连续的,可用: [x-x]
a. 一位小写字母: [a-z]
b. 一位大写字母: [A-Z]
c. 一位字母(大小写都行): [A-Za-z]
d. 一位字母或数字都行: [0-9A-Za-z]
e. 一位汉字: [\u4e00-\u9fa5]
(4). 预定义字符集:
a. \d 一位数字
b. \w 一位数字、字母或_
c. \s 空格、tab、换行等空字符
d. . 任意字符
(5). 如果规定一个字符集或子规则反复出现的次数时就用量词:
a. 有明确数量边界的量词:
1). {n} =n 必须n个,不能多也不能少
2). {n,m} n个<= <=m个
3). {n,} n个<= 多了不限
b. 没有明确数量边界的量词:
1). * 0个<= 可有可无,多了不限
2). ? 0个或1个 可有可无,最多一个
3). + 1个<= 至少一个,多个不限
(6). 两个规则中选其一匹配即可: 规则1|规则2
(7).希望将多个子规则分为一组先联合匹配,再和分组外的其他规则联合匹配:
(多个子规则)
(8). 匹配特殊位置: 3个
a. 字符串的开头位置: ^
b. 字符串的结尾位置: $
c. 英文句子中的单词的左右边界: \b
2. String家提供的正则相关函数: 3件事
(1). 查找敏感词: 4种情况
a. 查找一个固定的敏感词出现的位置:
var i=str.indexOf("敏感词")
// 如果找不到,返回-1
b. 用正则查找多种敏感词出现的位置:
var i=str.search(/正则/i)
// 如果找不到,返回-1
c. 查找敏感词的内容:
1). 查找第一个敏感词的内容和位置:
var arr=str.match(/正则/i)
// arr: [ 0:"敏感词内容", index:敏感词位置 ]
// 如果找不到返回null
2). 查找所有敏感词的内容,不关心位置:
var arr=str.match(/正则/ig)
// arr: [ 敏感词1, 敏感词2, ... ]
// 如果找不到返回null
d. 查找每个敏感词的内容和位置: reg.exec
补: js中所有数组底层本质都是关联数组(下标都为字符串) 1. 访问数组中元素值的标注写法: arr["下标"] 2. 简写: a. 如果下标为自定义字符串名称,可简写为: arr.自定义名称的下标 b. 如果下标为数字内容的字符串,可简写为: arr[数字下标] |
总结: 查找方法的返回值规律 1. 如果原函数返回的是下标位置i,如果找不到,都返回-1 2. 如果原函数返回的是一个数组arr或一个对象obj,如果找不到,都返回null 3. 如果原函数返回类数组对象,如果找不到返回空类数组对象: { length:0 } |
(2). 替换敏感词: 2种
a. 简单替换:
变量=str.replace(/正则/ig, "新值")
b. 高级替换:
变量=str.replace(/正则/ig, function(形参){
return 根据本次敏感词动态生成一个新值
})
c. 删除敏感词:
变量=str.replace(/正则/ig, "")
(3). 切割字符串:
a. 简单切割:
var arr=str.split("切割符")
b. 复杂切割:
var arr=str.split(/正则/i)
c. 打散字符串为字符数组:
var arr=str.split("")
3. RegExp对象:
(1). 创建正则表达式对象:
a. 如果正则是固定的:
var reg=/正则/ig
b. 如果正则需要动态生成:
var reg=new RegExp("正则",ig)
(2). 验证字符串格式:
var bool=reg.test(str)
reg必须同时前加^后加$
(3). 既查找每个关键词的内容又查找每个关键词的位置: (待续)
do{
var arr=reg.exec(str);
if(arr!=null){
获得本次找到的敏感词的内容(arr[0])和位置(arr.index)
}
}while(arr!=null);
4. 函数:
(1). 创建函数三种方式:
a. function 函数名(形参列表){ 函数体; return 返回值 } //会被声明提前,不好
b. var 函数名=function(形参列表){ 函数体; return 返回值 }//不会被声明提前,首选
c. var 函数名=new Function("形参1", "形参2", ... , "函数体; return 返回值")
函数本质: 1). 函数也是一个对象,对象中保存着函数的函数体代码 2). 函数名只是一个普通的变量,函数名通过函数对象地址,引用着函数对象 3). function在底层等效于new Function() function 函数名(){ ... }和var 函数名=function(){}在底层都会被翻译为 var 函数名=new Function(...) 只不过function 函数名(){}是先提前,再翻译 而var 函数名=function(){}是不提前,原地翻译 |
(2). 重载: 今后,一件事,根据传入不同的参数值,动态执行不同的逻辑时,都用重载
function 一个函数名(不写形参变量){
//arguments对象自动接住所有实参值
if(arguments.length==0){
执行一种逻辑
}else if(arguments.length==1){
执行另一种逻辑
}else{
执行其它逻辑
}
}
其中arguments是类数组对象: 和数组相比:
a. 相同点: 也有下标,length属性,也可for循环遍历
b. 不同点: 不是数组类型,无法使用数组家的函数
(3). 匿名函数:
a. 所有回调函数优先使用匿名函数——用完释放,节约内存
b. 所有js代码都应该保存在匿名函数自调中,禁止使用全局变量,避免全局污染!
(function(){
要执行的js代码
})();
结果: 匿名函数内的都是局部变量,不会产生全局变量。
局部变量随匿名函数一起释放。不会污染全局。
(4). 作用域和作用域链: (跟着视频亲自画图!!!)
a. 作用域:
1). 全局作用域:window,保存全局变量
优: 可重用,缺: 随处可用, 极易被污染
2). 函数作用域: 保存局部变量
局部变量包括2中: 函数中var出的变量和形参变量
优: 仅函数内可用,不会被污染,缺: 不可重用
3). 函数作用域对象原理:
i. 每个函数定义时都自带好友列表,好友列表里2个格子,一个是空,一个引用window
ii. 调用函数时临时创建函数作用域对象保存函数局部变量。并将函数作用域对象的地址保存到函数好友列表中离自己近的格子里。
iii. 函数执行过程中按就近原则先在自己的函数作用域对象中找局部变量使用。如果找不到,才被迫去全局window中找变量使用.
iv. 函数调用后,好友列表中离自己近的格子清空,导致函数作用域对象以及内部的局部变量被释放!——所以局部变量不可重用!
b. 作用域链: 保存一个函数所有可用的作用域对象的链式结构(好友列表)学名就叫作用域链。
1). 作用域链保存着一个函数可用的所有变量
2). 作用域链控制着变量的使用顺序。先局部后全局。
5. 闭包:
a. 只要希望给一个函数保护一个可反复使用的专属变量,又防止这个变量被外界篡改时,都用闭包。
b. 闭包三步:
1). 用外层函数妈妈包裹要保护的变量和内层函数
2). 外层函数妈妈用return把内层函数孩子返回到外部
3). 外部想使用内层函数的人,必须调用外层函数,才能获得return出来的内层函数对象。并将内层函数保存在一个变量中反复使用。
c. 闭包形成的原因: 外层函数调用后,外层函数的作用域对象被内层函数引用着无法释放,形成了闭包对象
d. 闭包的缺点: 闭包比一般的函数占用多一块内存——外层函数的函数作用域对象。所以,用完闭包后,应该尽快释放:
保存内层函数的变量=null
6. 面向对象: 封装 继承 多态
(1). 封装: 3种:
a. 用{}创建一个对象:
var 对象名={
属性名:属性值,
... : ... ,
方法名: function(){
... this.属性名 ...
}
}
b. 用new Object():
1). 2步:
i. var 对象名=new Object()
ii. 对象名.属性名=属性值;
对象名.方法名=function(){ ... }
2). 对象底层也是关联数组:
i. 都是名值对儿的集合
ii. 都可用[""]和.方式访问成员。
如果属性名来自于变量,就只能用[],不要加""
iii. 访问不存在的属性,都不报错,返回undefined
判断是否包含某个属性:
对象.属性名!==undefined
iv. 强行给不存在的属性赋值,都不报错,而是自动添加该属性
给对象添加新属性,唯一办法,强行赋值:
对象名.新属性名=新值
v. 都可用for in遍历
c. 只要反复创建多个相同结构的对象都用构造函数:
1). 2步:
i. 定义构造函数:
function 类型名(形参1,形参2, ...){
this.属性名1=形参1;
this.属性名2=形参2;
//构造函数中不要再包含方法定义定义!
}
ii. 用new 调用构造函数:
var 对象名=new 类型名(属性值1, 属性值2,...)
2). new做了4件事:
i. 创建一个新的空对象
ii. 让新对象继承(_ _proto_ _)构造函数的原型对象
iii. 调用构造函数,传入实参,并自动替换构造函数中的this为new正在创建的新对象。构造函数中,通过强行赋值的方式为新对象添加规定的属性,并保存属性值。
iv. 返回新对象的地址,保存到=左边的变量中。
3). 优点: 重用对象结构代码
4). 缺点: 如果构造函数中包含方法定义,则每次创建新对象都会重复创建相同方法的副本。 ——浪费内存!
(2). 继承:
a. 今后,只要同一类型所有子对象共用的方法和属性值,都要集中保存在构造函数的原型对象中!
构造函数.prototype.属性名/共有方法名=属性值/function(){ ... }
b. 自有属性和共有属性:
1). 获取属性值:都可用"子对象.属性名"
2). 修改属性值:
i. 自有属性: 子对象.自有属性名=新值
ii. 共有属性: 构造函数.prototype.共有属性名=新值
c. 内置类型原型对象:
1). 11种内置类型/对象: String, Number, Boolean, Array, Date, RegExp, Math(对象), Error, Function, Object, global(对象)
2). 一种类型=构造函数+原型对象
i. 构造函数: 创建子对象
ii. 原型对象: 为所有子对象保存共有成员
3). 查看该类型共有哪些API: 类型名.prototype
4). 该类型缺少想用的方法: 类型名.prototype.共有新方法=function(){ ... }
d. 原型链: 保存着一个对象可用的所有属性和方法。控制着属性和方法的使用顺序:先自有再共有——就近原则!
(3). 多态
重点讲重写:如果子对象觉得从父对象继承来的成员不好用,可以在子对象自己内部重写和父对象同名的成员,覆盖父对象的成员,优先使用自己的。
******面向对象终极总结: 封装,继承,多态******
①封装: 创建对象,2种:
如果只创建一个对象: {}
如果反复创建多个相同结构的对象: 构造函数
②继承: 所有子对象共用的属性值和方法,都要放在构造函数的原型对象中
③多态: 重写: 只要觉得从父对象继承来的成员不要用,都在子对象中重写同名成员
④如果觉得这个父对象对象都不好用,可以自定义继承: 2种:
1). 只换一个子对象的父对象: 2种:
i. 子对象.__proto__=新父对象
ii. Object.setPrototypeOf(子对象, 新父对象)
2). 更换多个子对象的原型对象: 构造函数.prototype=新对象
********************************************************************
7. 严格模式: "use strict";
(1). 禁止给未声明过的变量赋值
(2). 静默失败升级为错误
(3). 普通函数调用中的this不指window,而是指undefined
(4). 禁用arguments.callee
总结: this 判断this时,一定不要看他定义在哪儿。必须看它在哪里以何种方式调用 4种:
1. obj.fun() this->点前的obj对象2. fun() this->默认指window
3. new Fun() this->new正在创建的新对象
4. 类型名.prototype.共有方法=function(){ ... }
this->将来谁调用这个函数,就指谁
将来调用这个函数的.前的某个子对象
8. 保护对象:
(1). 保护属性:
a. 每个属性包含三个开关:
1). writable: 控制是否可修改属性值
2). enumerable: 控制着是否可被for in遍历到,但是只防for in不防.
3). configurable: 控制
i. 是否可删除当前属性
ii. 是否可修改writable和enumerable两个开关
强调: configurable一旦改为 false,不可逆!
b. 只修改一个属性的多个开关:
Object.defineProperty(对象名, "属性名",{开关: true/false})
c. 修改多个属性的多个开关:
Object.defineProperties(对象名,{
属性名:{ 开关:true/false, ... },
... : ...
})
d. 如果用自定义的规则保护属性时,只能用访问器属性: 2步:
Object.defineProperties(对象,{
//1). 先定义一个隐姓埋名且半隐藏的数据属性:
_属性名:{
value: 属性的初始值,
writable:true,
enumerable:false,
configurable:false
},
//2). 再定义访问器属性保镖冒名顶替要保护的属性
属性名:{
get:function(){
return this._属性名
},
set:function(value){ //value ← 要修改的新属性值
先验证value
如果验证通过,this._属性名=value
否则如果验证未通过,不但不保存新属性值,还会报错
},
enumerable:true,
configurable:false
}
})
外界使用访问器属性时和使用普通属性一样:
对象.属性名
外界试图获取访问器属性值时,自动调用get()
外界试图修改访问器属性值时,自动调用set()
(2). 保护结构: 3个级别
a. 防扩展: Object.preventExtensions(对象)
b. 密封: Object.seal(对象)
c. 冻结: Object.freeze(对象)
9. 如果没有构造函数,也想创建子对象,继承父对象:
var 新子对象=Object.create(父对象,{
自有属性:{
value:属性值,
开关:true或false,
... :...
},
... : { ... }
})
10. 替换this: 3种:
(1). 在一次调用函数时,临时替换this,首选:
函数.call(对象, 实参值,...)
(2). 临时替换一次this,但是需要打散数组再传参时,被迫改为:
函数.apply(对象, 数组)
(3). 创建一个一模一样的新函数并永久绑定this和部分实参值:
var 新函数名=原函数.bind(对象, 固定实参值, ...)
11. 数组函数:
(1). 判断:
a. 判断数组中是否所有元素都符合要求:
var bool=arr.every(function(value,i,arr){
return 判断条件
})
b. 判断数组中是否包含符合要求的元素:
var bool=arr.some(function(value,i,arr){
return 判断条件
})
(2). 遍历:
a. 单纯简化for循环变量原数组中每个元素:
arr.forEach(function(value,i,arr){
对当前元素执行操作
})
b. 保护原数组不变,返回遍历加工后的新数组
var 新数组=arr.map(function(value, i,arr){
return 加工后的一个新元素值
})
(3). 过滤: 复制出数组中符合要求的元素放入新数组返回
var 新数组=arr.filter(function(value,i,arr){
return 判断条件
})
(4). 汇总: 遍历数组中每个元素,经过求和或其他汇总方式,统计出一个最终结论
var 结果=arr.reduce(function(box,value,i,arr){
return box和value计算出的新临时汇总值
}, 起始值)
12. ES6: ⏬
(1). 模板字符串: 今后,只要拼接字符串,都用模板字符串代替+:
a. 整个字符串包裹在一对儿反引号`...`中
b. 反引号``中支持换行、""、''均可使用
c. 反引号中需要动态生成的内容必须放在${}里
d. ${}里:
1). 可以放一切有返回值的合法的变量或js表达式。
2). 不能放程序结构(分支和循环)以及没有返回值的js表达式
(2). let: 今后,声明变量都用let代替var
a. let的好处:
1). 阻止声明提前
2). 让代码块(分支和循环的{})也变成块级作用域,{}块内的变量出了{}无法使用,不会影响外部
b. let的小脾气:
1). 在同一作用域内禁止重复声明;
2). 禁止提前使用;
3). 在全局声明也不保存在window中
(3). 箭头函数: 今后,几乎所有的function都可用箭头函数简写:
a. 如何: 3句话:
1). 去掉function,在()和{}之间加=>
2). 如果只有一个形参,可省略()
3). 如果函数体只有一句话,可省略{}
如果仅有的一句话还是return,必须省略return
b. 今后:
1). 如果函数中没有this或者恰好希望函数内this与函数外this保持一致时,可用箭头函数简写!
2). 如果不希望内外this相同时不能使用箭头函数简写。
(4). for of: 今后只要遍历数字下标的东西,都用for of
for(var i=0; i<arr.length; i++) | forEach | for of | for in | ||
数字下标 | 索引数组 | √ | √ | √ | 不保险 |
类数组对象 | √ | × | √ | 不保险 | |
字符串 | √ | × | √ | 不保险 | |
自定义名称下标 | 关联数组 | × | × | × | √ |
对象 | × | × | × | √ |
(5). 参数增强:
a. 参数默认值: 定义函数时最后一个形参不确定有没有实参时
function 函数名(形参1, ..., 最后形参=默认值){
... ...
}
b. 剩余参数: 定义函数时: 只要多个形参不确定,都用剩余参数
function 函数名(其它形参, ...数组名){
//...收集除其它形参外多余的实参值保存到指定数组名的数组中
}
c. 打散数组: 今后调用函数时,只要单纯打散数组再传参时
1). 如何: 函数(...数组)
2). ...口诀: 定义函数时...表示收集, 调用函数时...表示打散
3). ...语法糖:
i. 复制一个数组: var arr2=[...arr1];
ii. 合并多个数组和元素值: var arr3=[...arr1,值,...arr2,值]
iii. 复制一个对象: var obj2={ ... obj1 }
iv. 合并多个对象和属性: var obj3={ ...obj1, 属性:值, ...obj2, 属性:值 }
(6). 解构:
a. 只要想提取出数组中个别元素值,单独使用时,就数组解构:
[变量1, 变量2]=arr;
b. 只要想提取出对象中个别属性值或方法,单独使用时,就对象解构:
var {属性名也是变量名, 属性名也是变量名}=对象
c. 只要多个参数不确定有没有,又要求实参值必须传给指定的形参时,就参数结构:
定义函数时:
function 函数名({
属性名也是形参名="默认值",
属性名也是形参名="默认值",
... = ...
}){
函数体
}
调用函数时:
函数名({
要修改的形参名: 实参值,
... : ... ,
})
🆕【后文传送门】👉 ES6_class、promise的简谈与应用_15
如果这篇【文章】有帮助到你,希望可以给【青春木鱼】点个赞👍,创作不易,相比官方的陈述,我更喜欢用【通俗易懂】的文笔去讲解每一个知识点,如果有对【前端技术】感兴趣的小可爱,也欢迎关注❤️❤️❤️【青春木鱼】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】💕💕!