1.闭包(Closure)
我的理解闭包只是一个叫法,其实闭包就是个比较特殊的函数(老师说是高阶函数,不明觉厉),符合以下特征的就叫闭包:
- 就是函数套函数
- 内部函数调用外部函数的变量
例:
// 闭包
// 定义英雄函数
let Hero = function () {
// 初始血量
let Blood = 999;
// 加血包
let addBlood = function (addNum) {
Blood = Blood + addNum;
return Blood;
};
return addBlood;
};
// 大血包
const big_blood_bag = 300;
// 建立英雄龙
let dragon = Hero();
console.log(`获得大血包,加血${big_blood_bag},现在血量${dragon(big_blood_bag)}`);
// 输出:获得大血包, 加血300, 现在血量1299;
程序运行过程:
- 为什么要函数套函数呢?
我的理解是隐藏变量,函数套函数就是要造出1个局部变量。例如上面代码,英雄的血量如果不用闭包,而用全局变量,万一不小心把变量改成负数,或与其他变量名重复,就完了。所以不能让别人直接访问。但是用局部变量别人又无法直接访问,并且局部函数运行完变量就消失了,因此就用闭包来间接访问,并且变量不会消失。闭包内部用return来与外部联系。
有点难理解,上课老师经常提到优雅,什么时刻能到那个境界。
- 闭包的应用
当一个函数需要多个参数的时候,不一定一次性全部传入,可以分批传入// 闭包应用
// 分批传入参数
// 例:游戏连击
let doubleKill = function (oneKill) {
let reward = "黄金甲";
return function (towKill) {
reward += "、束神环";
return function (threeKill) {
reward += "、焚天雷";
return `牛批!三连击!!!掉血${
oneKill + towKill * 2 + threeKill * 3
}!奖励${reward}`;
};
};
};
let oneKill = doubleKill(180);
let towKill = oneKill(260);
let threeKill = towKill(480);
console.log(threeKill);
// 显示: 牛批!三连击!!!掉血2140!奖励黄金甲、束神环、焚天雷
- “柯里化”函数:就像上面这个例子,分批次获取数据。
2. 访问器属性
- JS中对象就是一组变量名和对应的值集合,值可以是数据(对象中叫属性)也可以是函数(对象中叫方法)。如下:
// 访问器属性
// 定义坦克对象
let tank = {
// 穿甲弹
ArmorPie: 50,
// 装甲
Armor: 10,
// 开炮
fire(Num) {
if (!isNaN(Num) && Num > 0) {
this.ArmorPie = this.ArmorPie - Num;
return `开了${Num}炮,还乘${this.ArmorPie}颗穿甲弹。`;
} else {
return "没上弹!";
}
},
// 防御
defend(Whether) {
if (Whether === "中弹") {
this.Armor--;
return `完了,中弹了,装甲还乘${this.Armor}片了。`;
} else {
return "幸运之神眷顾,没被打中!";
}
},
};
// 一般正常调用对象方法是这样的:
console.log(tank.fire(6));
// 显示: 开了6炮,还乘44颗穿甲弹。
console.log(tank.defend());
// 显示: 幸运之神眷顾,没被打中!
console.log(tank.defend("中弹"));
// 显示: 完了,中弹了,装甲还乘9片了。
// 使用访问器属性是这样的
// 把对象的方法改为如下:
tank = {
// 穿甲弹
ArmorPie: 50,
// 装甲
Armor: 10,
// 中弹
ShotIn: "",
// 上弹数
FireNum: 0,
// 开炮
// 此处语法改为 get 方法名,用于读取数据
get shot() {
if (this.FireNum > 0) {
return `开了${this.FireNum}炮,还乘${this.ArmorPie}颗穿甲弹。`;
} else {
return "没开炮!";
}
},
// 上炮弹
// 此处语法改为 set 方法名,用于写数据
set shot(FireNum) {
if (FireNum > 0) {
this.FireNum = FireNum;
this.ArmorPie = this.ArmorPie - FireNum;
return `开了${this.FireNum}炮,还乘${this.ArmorPie}颗穿甲弹。`;
} else {
return "没上弹!";
}
},
};
console.log(tank.shot);
// 显示:没开炮
tank.shot = 3;
console.log(tank.shot);
// 显示:开了3炮,还乘47颗穿甲弹。
3. 类与对象的创建和成员引用
// 1. 构造函数
// 先创建类
let phone = function (cpu, px) {
this.cpu = cpu;
this.px = px;
};
// 用构造函数创建对象,通过构造函数可以创建多个对象;
const p30 = new phone("麒麟990", "4k");
const mi9 = new phone("高通980", "2k");
console.log(p30);
// 显示:phone { cpu: '麒麟990', px: '4k' }
console.log(mi9);
// 显示:phone { cpu: '高通980', px: '2k' }
// 对象的方法一般是公共的、可调用的,通过方法操作当前对象的属性
// 任何一个函数都有一个属性, 叫原型 prototype,普通函数的原型没用
// 只有将原型当成构造函数创建对象时,才用到原型
console.log(phone.prototype);
// 显示:phone {} 这时是没有意义的
// 用来创建对象的方法
phone.prototype.call = function (num) {
return `正在拨打${num}。`;
};
// 用原型创建方法后,可被所有实例使用
console.log(p30.call("13877888877"));
console.log(mi9.call("13311112222"));
// 创建静态成员,将属性直接挂载到构造函数
p30.screen = "6寸";
console.log(p30.screen);
// 创建私有成员:在对象内声明变量
phone = function (cpu, px, rom) {
let romnum = "8G";
this.cpu = cpu;
this.px = px;
};
const p50 = new phone("麒麟990", "4k", "12G");
console.log(p50);
// 传统的基于构造函数的类与对象, 语法上非常的复杂;
// ES6中 用 class 可以包装以上操作
// ES6 用class创建对象
class TV {
// 公共字段
logo = "logo";
scr_size = "scr_size";
// 私有成员
#core_cpu = "";
// 构造方法
constructor(logo, scr_size, cpu) {
this.logo = logo;
this.scr_size = scr_size;
this.#core_cpu = cpu;
}
// 公共方法,即原型
getInfo() {
return `电视品牌为${this.logo},屏幕尺寸${this.scr_size},配置cpu${
this.#core_cpu
}`;
}
// 静态成员
static on_off = "off";
}
const HuaWei_TV = new TV("华为智慧屏", "65寸", "麒麟520");
console.log(HuaWei_TV.getInfo());
// 显示:电视品牌为华为智慧屏,屏幕尺寸65寸,配置cpu麒麟520
// 继承:可以扩展对象功能,即增加属性、方法
class intelligence_TV extends TV {
#packa = "";
// 构造器必须将父类的复制过来
constructor(logo, scr_size, cpu, AI, packaging) {
// 用super 来引用原属性赋值
super(logo, scr_size, cpu);
// 新增属性
this.AI = AI;
this.#packa = packaging;
}
// 重写父类方法
getInfo() {
// 用super 引用原方法
return `${super.getInfo()},采用最新智能AI-${this.AI},封装工艺${
this.#packa
}`;
}
也可以用属性访问器;
set packa(packaging) {
this.#packa = packaging;
console.log(this.#packa);
}
get packa() {
console.log(this.#packa);
return this.#packa;
}
}
const HuaWei_ES55 = new intelligence_TV(
"华为智慧屏",
"55寸",
"麒麟520",
"小艾",
"2D封装"
);
console.log(HuaWei_ES55.getInfo());
// 显示:电视品牌为华为智慧屏,屏幕尺寸55寸,配置cpu麒麟520,采用最新智能AI-小艾,封装工艺2D封装
// 用属性访问器访问
HuaWei_ES55.packa = "5D立体封装";
console.log(HuaWei_ES55.packa);
4. 数组与对象的解构
// 数组解构
// 语法:模板 = 数组
let [hero , skill]=["超人","飞天、神力、神速"];
console.log(hero , skill);
// 更新
[hero , skill]=["闪电侠" , "神速"];
console.log(hero , skill);
// 防止参数不足情况,使用默认值
[hero , skill , sex = "男"]=["闪电侠" , "神速"];
console.log(hero,skill,sex);
// 参数过多情况如下用 ... 多余的放在数组n中
let [a , b , ...n]=[1, 2, 3, 4, 5, 6, 7];
console.log(a, b, n);
// 二数交换
let a1 = "超人";
let b1 = "闪电侠";
[a1 , b1] = [b1 , a1];
console.log(a1 , b1);
// 对象解构
// 对象模板 = 对象字面量;
let { hero, skill, sex } = { hero: "蚁人", skill: "变小", sex: "男" };
console.log(hero, skill, sex);
// 更新操作。注意:大括号不能出现在等号左边,使用括号转为表达式
({ hero, skill, sex }) = { hero: "蚁人", skill: "变小", sex: "男" };
// 注意:当左边模板中的变量与前面命名冲突,使用别名
let { hero:hero_name, skill:hero_skill, sex:hero_sex } = { hero: "琴", skill: "操控元素", sex: "女" };
console.log(hero_name, hero_skill, hero_sex);
// 克隆对象 类似参数过多用 ... 压到1个变量
let {...super_hero} = {
hero: "蚁人",
skill: "变小",
change(){
return `${this.hero}现在${this.skill}`
} };
console.log(super_hero);
// 对象解构应用:用来传递参数,即参数用对象模板,调用时用模板字面量赋值
function show({ hero, skill, sex }){
return hero+skill+sex
}
console.log(show({ hero: "蚁人", skill: "变小", sex: "男" }));
5. JS引入到浏览器中
- JS是在浏览器(前端)中运行的
- node.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>JS引入到浏览器</title>
</head>
<body>
<!-- 1. 写到元素的事件属性中 -->
<button onclick="alert('开始')">弹窗</button>
<!-- 2. 用script引入js脚本 -->
<button onclick="alt(this)">保存</button>
<script>
function alt(ev) {
ev.textContent = "保存成功";
}
</script>
<!-- 3. 引入外部脚本 -->
<button onclick="show()">显示</button>
<script src="show.js"></script>
</body>
</html>
6. 获取DOM元素
<!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>获取dom元素</title>
</head>
<body>
<ul class="list">
<li class="item">项目1</li>
<li class="item">项目2</li>
<li class="item">项目3</li>
<li class="item">项目4</li>
<li class="item">项目5</li>
</ul>
<script>
// 最常用的两个API,基本可以满足使用
// 1. 选择一组元素,语法 querySelectorAll("css选择器")
// 2. 选择一个元素,语法 querySelector("css选择器")
// 例:将所有li字体颜色设置为红色
const Li = document.querySelectorAll(".item");
// console.log(Li);
for (let i = 0, length = Li.length; i < length; i++) {
Li[i].style.color = "red";
}
// 将第一、三个字号改为2rem
// console.log(document.querySelector(".item"));
document.querySelector(".item").style.fontSize = "2rem";
document.querySelector(".item:nth-of-type(3)").style.fontSize = "2rem";
</script>
</body>
</html>