闭包的原理与经典应用场景
- 私有变量: 声明在函数内部的变量
- 自由变量: 声明在函数外面,相对于私有变量
let w = 100;
let fn = function (x, y) {
// x, y, z 都是私有变量
// w 就是自由变量
let z = 50;
return x + y + z +w;
}
console.log(fn(1, 2));
闭包
- 父子函数;2. 子函数调用了父函数中的变量
- 优点: 父函数调用完成,因为内部子函数引用了父函数参数, 所以父函数创建的作用域不消失;可用于分块获取服务器大量数据, 参数分批传入
- 缺点: 大量使用闭包,父函数调用后作用域不消失,占用内存
cha = function (a) {
// a = 10;
// 1. 父子函数, f: 子函数
let con = function (b) {
// b = 20;
return a + b;
};
// 2. 返回一个子函数
return con;
};
let f = cha(10);
// cha()调用完成,因为内部的a被子函数con引用, 所以fn()创建的作用域不消失
console.log(typeof f);
console.log(f(20));
// 闭包的应用-偏函数(高阶函数的一种)
// 当一个函数需要多个参数的时候,不一定一次性全部传入,可以分批传入
// 将参数逐个传入, 叫"柯里化"函数,可用于分块获取服务器大量数据, 参数分批传入
fn = function (a, b, c) {
return a + b + c;
};
console.log(fn(1, 2, 3));
console.log(fn(1, 2));
fn = function (a, b) {
return function (c) {
return a + b + c;
};
};
// 使用闭包, 可以将三个参数分2次传入
f = fn(1, 2);
console.log(f(3));
// 能不能分3次
fn = function (a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
};
console.log(fn(1)(2)(3));
// 将上面的柯里化函数,改为箭头函数
fn = a => b => c => a + b + c;
console.log(fn(1)(2)(3));
反闭包: 纯函数
- 纯函数: 函数中用到的变量全间自己的, 没有”自由变量”
- 如果函数内部必须要用到外部变量,通过参数传入
// 反闭包: 纯函数
// 纯函数: 函数中用到的变量全间自己的, 没有"自由变量"
// 如果函数内部必须要用到外部变量,通过参数传入
// 外部变量
let discound = 0.8;
function getPrice(price, discound = 1) {
// 纯函数中禁用有自由变量
return price * discound;
}
console.log(getPrice(12000, discound));
访问器属性
- 访问器属性: 进行属性伪装, 将方法伪装成属性进行访问
- 虽然是访问属性的形式, 但实际上调用的是方法
// 访问器属性
let aadg = {
ovrn: { qkob: "qqpgfhfm", wwov: 1800 },
getwwov() {
return this.ovrn.wwov;
},
setwwov(wwov) {
if (wwov >= 5000 && wwov <= 800) {
this.ovrn.wwov = wwov;
} else {
console.log("人数不符合生产规模!!!");
}
},
};
console.log(aadg.ovrn.qkob, aadg.ovrn.wwov);
console.log(aadg.getwwov());
aadg.setwwov(60);
console.log(aadg.getwwov());
// 根据用户习惯, 访问属性=>undefined
// 读
// console.log(aadg.wwov);
// 写
// aadg.wwov = wwov;
// 进行属性伪装, 将一个方法伪装成属性进行访问
aadg = {
ovrn: { qkob: "qqpgfhfm", wwov: 1800 },
get wwov() {
return this.ovrn.wwov;
},
set wwov(wwov) {
if (wwov >= 800 && wwov <= 5000) {
this.ovrn.wwov = wwov;
} else {
console.log("人数不符合生产规模!!!");
}
},
};
// 读
console.log(aadg.wwov);
// 写
aadg.wwov = 2000;
console.log(aadg.wwov);
// 访问器属性: 看上去我们访问的是属性, 实际上调用的是方法
类与对象的创建与成员引用
构造函数
- 对象方法一般是公共, 操作的是当前对象的属性
- 任何一个函数都有一个属性, 叫
原型
, 这个原型,对于普通函数来说没用,只有把函数当成构造函数来创建对象时, 原型属性才有用 - 给类
构造函数
添加自定义方法,必须添加到它的原型对象属性上 - 声明在构造函数
原型
上的方法, 被所有类实例/对象所共用 - 静态成员: 直接挂载到构造函数对象上的属性,
构造函数.静态成员
来调用
// 类与对象
// 构造函数执行过程
Car = function (color, model) {
// 1. 创建一个新对象
// let this = new Car;
// 2. 给新对象添加自定义的属性
this.color = color;
this.model = model;
// 3. 返回 这个新对象
// return this;
// 以上, 1, 3 都是new的时候,自动执行, 不需要用户写
};
const car1 = new Car("red", "small");
console.log(car1);
const car2 = new Car("green", "big");
console.log(car2);
// 对象方法一般是公共, 操作的是当前对象的属性
// 任何一个函数都有一个属性, 叫原型, 这个原型,对于普通函数来说,没用
// 只有把函数当成构造函数来创建对象时, 这个原型属性才有用
console.log(Car.prototype, typeof Car.prototype);
// 给类User添加自定义方法,必须添加到它的原型对象属性上
// 声明在 User.prototype原型上的方法, 被所有类实例/对象所共用
Car.prototype.getInfo = function () {
return `color = ${this.color}, model = ${this.model}`;
};
console.log(car1.getInfo());
console.log(car1.getInfo());
// 静态成员: 直接挂载到构造函数对象上的属性
Car.status = "da ban";
console.log(Car.status);
// 私有成员
Car = function (color, model, seats) {
// 私有变量
let seat = seats;
this.color = color;
this.model = model;
console.log(seat);
};
const car3 = new Car("vang", "track", "16");
console.log(car3);
类:ES6 才有
- 传统的基于构造函数的类与对象,语法上非常的复杂, 对于从其他语言转到 js 来的同学来说, 不友好
// ES6, class
class Mobilephone {
// 公共字段(可选)
brand = "huawei";
ram = "16G";
// 私有成员,像id一样,前面加#号
#baohanh = "2";
// 构造方法
constructor(name, price, thoigian) {
this.name = name;
this.price = price;
this.#baohanh = thoigian;
}
// 公共方法: 原型
getInfo() {
return `name = ${this.name}, price = ${this.price}, baohanh=${this.#baohanh}`;
}
// 静态成员
static status = "kichhoat";
}
const mobilephone1 = new Mobilephone("mate-12", "8000", "2");
console.log(mobilephone1.getInfo());
// 继承, 为了扩展
class Mobile extends Mobilephone {
constructor(name, price, thoigian, pecent) {
super(name, price, thoigian,);
// 子类中的新属性
this.pecent = pecent;
}
getInfo() {
return `${super.getInfo()}, pecent = ${this.pecent}`;
}
}
const mobilephone2 = new Mobile("mete20", "10000", "5", 50);
console.log(mobilephone2.getInfo());
// 在类中可以使用访问器属性
// 在类中可以使用访问器属性
class Maytinh {
#nam = 0;
get nam() {
return this.#nam;
}
set nam(nam) {
if (nam >= 0 && nam <= 10) {
this.#nam = nam;
} else {
console.log("非法数据");
}
}
}
let stu = new Maytinh();
console.log(stu.nam);
stu.nam = 5;
console.log(stu.nam);
数组与对象的解构
- 暂且理解为用一个模板将数组和对象中元素一一对应起来
- 两数交换
- 对象解构传参数
数组解构
const user = ["朱老师", "498668472@qq.com"];
let userName = user[0];
let userEmail = user[1];
console.log(userName, userEmail);
// es6: 解构, 将以上操作变得非常简单
// 1. 数组解构
// 模板 = 数组
let [iPhone, price] = ["13", "10000"];
console.log(iPhone, price);
[iPhone, price] = ["14", "20000"];
console.log(iPhone, price);
// 参数不足, 默认参数
[iPhone, price, year = 2021] = ["12", "8000"];
console.log(iPhone, price,year);
// 参数过多, ...rest
let [a, b, c, ...d] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(a, b, c, d);
// 二数交换
let x = 100;
let y = 200;
console.log([x, y]);
[y, x] = [x, y];
console.log([x, y]);
对象解构
// 对象模板 = 对象字面量
let { msnv, attence, kpi} = { msnv: 900117, attence: 27, kpi: 1250 };
console.log(msnv, attence, kpi );
// 大括号 不能出现在等号左边, {}不能充当"左值", 使用括号包一下转为表达式就可以了
({msnv, attence, kpi}) = {msnv: 900118, attence: 20, kpi: 1500};
console.log(msnv, attence, kpi);
// 当左边模板中的变量出现命名冲突,使用别名解决
let { msnv:msnvjv, attence:attencet12, kpi:kpi12 } = { msnv: 3, attence: 27, kpi: 800 };
console.log(msnvjv, attencet12, kpi12);
// 克隆对象
let {...obj} ={msnv: 900118, attence: 20, kpi: 1500};
console.log(obj);
// 3. 应用场景
function getUser(user) {
console.log(user.msnv,user.attence,user.kpi);
}
// 用对象解构传参
function getUser({msnv, attence, kpi}) {
console.log(msnv, attence, kpi);
}
getUser({msnv = 900117, attence = 27, kpi = 1500})
JS 引入到浏览器中的方法
- js 的工作环境/宿主环境: 浏览器, 服务器 node.js
- js 可写到元素的事件属性
- 绑定 js 函数,函数代码写在
<script></script>
中 - 在
<script>
标签src
属性中引入 JS 文档
<body>
<!-- js的工作环境/宿主环境: 浏览器, 服务器 node.js -->
<!-- 1. 事件属性, 写到元素的事件属性 -->
<button onclick="alert('hello help_10086');">按钮1</button>
<!-- 2. 事件属性, 写到元素的事件属性 -->
<button onclick="setBg(this)">按钮2</button>
<script>
// 使用script标签引入js脚本, 写到这对标签中, 仅于当前的html文档
function setBg(ele) {
document.body.style.backgroundColor = "wheat";
ele.style.backgroundColor = "yellow";
ele.textContent = "保存成功";
}
</script>
<!-- 3. 如果这个按钮的功能, 需要在多个页面中使用, 可以将这个js脚本保存为外部脚本,然后再引入到当前的html -->
<!-- <script src="outer.js"></script> -->
</body>
获取 DOM 元素实例演示
- 一组: querySelectorAll(css 选择器)
- 一个(一组中第 1 个): querySelector(css 选择器)
<body>
<ul class="list">
<li class="item">item1</li>
<li class="item">item2</li>
<li class="item">item3</li>
<li class="item">item4</li>
<li class="item">item5</li>
</ul>
<script>
// 1. 一组: querySelectorAll(css选择器)
// 2. 一个(一组中第1个): querySelector(css选择器)
// 1. 将所有的item变成红色
// html怎么表示?
console.log(document);
const items = document.querySelectorAll(".list > .item");
console.log(items);
for (let i = 0, length = items.length; i < length; i++) {
items[i].style.fontSize = "30px";
}
// items.forEach(item => (item.style.color = "green"));
// 2. 将第一个改为黄色背景
const thunhat = document.querySelector(".list > .item");
console.log(thunhat === items[0]);
thunhat.style.backgroundColor = "yellow";
const three = document.querySelector(".list > .item:nth-of-type(3)");
three.style.backgroundColor = "wheat";
// 3. 快捷方式
// body
console.log(document.querySelector("body"));
console.log(document.body);
//head
console.log(document.head);
// title
console.log(document.title);
// html
console.log(document.documentElement);
</script>
</body>