10.29 JS学习13
1. Async函数(可以将异步操作变为同步操作)
异步操作不按照顺序执行,而是独立出来执行。
function print() {setTimeout(() => {//定时器是异步的console.log("定时器");}, 1000)console.log("hello");}print();//先输出hello,后输出定时器调用有定时器的函数,定时器是异步的,先输出定时器后边内容后输出定时器里的。
想要定时器的先输出,等待他完成再进行后边,即异步同步化,使用async、await搭配promise
函数使用。
// 用Async实现先打印定时器再打印hello//因为定时器是异步,尝试用promise去封装function timeout(ms) {//resolve是一个函数return new Promise((resolve, reject) => {const m = "xiaohongmao";setTimeout(function () {console.log("dahuilang");resolve();}, ms);//这里resolve不用加参数吗?参数是定时器的回调函数,定时器执行完后调用resolve函数。resolve的参数是什么?// setTimeout(resolve, ms)})// return promise;// 将传入语句延时一段时间输出 }async function asyncPrint(ms, value) {//将具有异步操作的代码前放入await,等待异步操作完成await timeout(ms);console.log(value);}asyncPrint(1000, "hello world");1.setTimeout(resolve, ms);//这里resolve不用加参数吗?参数是定时器的回调函数,定时器执行完后调用resolve函数。resolve的参数是什么?
resolve 是一个函数,用来告诉 Promise:
“我完成了,外部可以继续执行后续代码了。”
因为我们只是要在定时器到时间后通知 Promise 已完成,不需要传值。
resolve(); // 不返回值
resolve(123); // 返回数值
resolve("ok"); // 返回字符串
resolve({a: 1}); // 返回对象
2.const m = "xiaohongmao"; resolve(m)为什么不执行?
关键在于:你没接收 resolve 的返回值。
resolve(m) 执行后其实是“把值 m 传递给 Promise 的使用方”。
但你这里:
await timeout(ms);
console.log(value);
只 await 了 Promise,却没有获取 resolve(m) 返回的结果。
要接收它,应该这样写👇:
async function asyncPrint(ms, value) {
const result = await timeout(ms); console.log(result); // 打印 "xiaohongmao" console.log(value); // 打印 "hello world" }
3.
return new Promise((resolve, reject) => {
// const m = "xiaohongmao";
setTimeout(function () {
console.log("dahuilang");
}
, ms); })不加resolve为什么不执行定时器后边的hello world?因为 Promise 没有调用 resolve(),所以 await 一直在等。
2.Class基本语法
在ES5中类和函数的定义形式类似,但是不同的是,一般首字母大写认为是类,首字母大写认为是函数,面向对象的方式是基于prototype


function test(name) {const name = name;console.log(name);
}
test("hello");
这种情况无论在函数中还是类里,都会报错,因为同名冲突,要么换一个名字(不推荐,因为class本质是个模板化操作,引入太多名字不容易记录),可以用this.name和this.age代替。
| 对比点 | 类(class) | 函数(function) |
|---|---|---|
| 定义方式 | 使用 class 关键字 | 使用 function 关键字 |
| 主要用途 | 创建对象、定义类型(模板) | 封装可重复执行的逻辑 |
| 调用方式 | 通过 new 实例化(如 new Person()) | 直接调用(如 foo()) |
| 内部机制 | 有原型(prototype),方法自动添加到原型上 | 普通函数无自动原型绑定(除非用作构造函数) |
| 是否可继承 | ✅ 支持继承(extends) | ⚠️ 需要手动继承(较复杂) |
| 是否可作为构造函数使用 | ✅ 默认构造器 | ✅ 也可作为构造函数(传统方式) |
| 是否有严格模式 | 类体内 自动启用严格模式 | 函数需显式 "use strict" |

语法糖只是 语法层面的简化或包装,让我们更容易表达原本就能实现的东西。
程序执行时,它最终都会被转换(编译 / 解释)成底层等价的语法。

| 功能 | 语法糖 | 展开后的底层写法 |
|---|---|---|
| 箭头函数 | const f = x => x + 1; | const f = function(x) { return x + 1; }; |
| 模板字符串 | `Hello, ${name}` | "Hello, " + name |
| 解构赋值 | const {a, b} = obj; | const a = obj.a; const b = obj.b; |
for...of | for (let v of arr) | 用 for (let i=0; i<arr.length; i++) 实现 |
<script>class Person {//类里用constructor来传参constructor(name, age) {this.name = name;this.age = age;}getName() {//这里不能写name和ageconsole.log(this.name, this.age);}}//用new创造一个对象实例,传入参数const p = new Person("dahuilang", 100);//这个对象实例调用函数p.getName();</script>2.1 constructor方法

一个类必须有constructor方法,没有也要加上,不加会自动加上,因为class是个语法糖

与ES5区别:ES5是用函数定义类,函数存在提升,但是类是不存在提升的,所以用
Class 类名 {}形式的类必须先定义后使用。

使用class类的时候要先定义后使用,使用时用new函数创造对象类
3 Class的属性和方法
class中通常用来进行定义,制作一个模板。然后用关键字创建实例对象进行调用。



类中的所有属性和定义,在创造类的实例后都会被这个实例所继承,但如果类中的某一属性和定义前边加了static,那么这个属性和定义就是静态的,不会被实例继承,而是需要调用类.函数名或者变量名来使用。
| 成员类型 | this 指向 | 属性归属 | 访问方式 |
|---|---|---|---|
| 实例属性 / 方法 | 实例对象(如 p) | 每个实例独立 | 实例。属性 / 方法(p.say()) |
| 静态属性 / 方法 | 类本身(如 Person) | 类统一拥有 | 类。属性 / 方法(Person.maybe()) |
静态属性的this表示的是类本身,而不是实例对象。访问height得到空,所以在类里想要访问height需要先定义一个静态变量height。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>class的属性和方法</title>
</head><body><script>//Person.weight = 33;//报错——因为class类不可提升,不能在还没有对其定义的时候就赋值class Person {static height = 180;//静态属性//类的属性constructor(height) {this.height = height;//实例对象属性,对应实例对象传回的height}//类的方法,实例对象方法say() {//this指向实例对象console.log("hello");console.log(this.height);//对应实例对象传回的height}//静态方法的 this 不指向实例,而是指向「类本身」,类本身没有height属性,所以返回空static maybe() {//指向当前类而不是实例对象console.log(this.height);//对应静态属性heightconsole.log(this.weight);}//如果有一个同名的静态方法,看调用方法是类调用还是实例调用maybe() {console.log(this.height);console.log(this.weight);}}var p = new Person(20);console.log(p.height);//返回20p.say();//返回hello 20//p.maybe();//报错,说p.maybe不是一个函数。静态方法不能通过实例调用,只能通过类调用。console.log("aaa");Person.maybe();//返回180 undefinedconsole.log("bbb");p.maybe();//返回20 undefinedPerson.weight = 200;//静态属性console.log("ccc");Person.maybe();//返回180 200console.log(Person.weight);//返回200</script>
</body></html>类的属性、函数方法对应的都是对象实例的特点,静态方法对应的是类本身,类的参数定义也不会影响实例对象的属性和方法。相当于访问类的属性。
静态方法只能访问静态变量。
在继承上也有区别,静态方法不会被实例所继承。
如果有同名的函数,一个是函数一个是静态函数,根据调用方式的不同,如果是类名调用,那么返回静态的方法,如果是实例名调用,那么返回类中方法。虽然名字相同,但是函数的种类不同,也不会影响冲突。

静态方法是static加函数,静态属性是在类里static加定义,或者在类外,类名.属性进行定义。
静态属性,在类外利用类名.属性进行赋值,或者在类里static加定义。
类中重名方法,但是一个是静态的话,要看调用方式,不会报错。

4.Class的继承


<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>class的继承</title>
</head><body><script>//父类class Person {constructor(name, age) {this.name = name;this.age = age;}getName() {console.log(this.name);}//静态定义,需要调用类才行static say() {console.log("hello");}}//子类class Student extends Person {//父类没有的东西getAge() {console.log(this.age);}//子类添加的属性,必须加上父类的参数,并且必须加上super(),并且super()必须在this之前constructor(name, age, height) {super(name, age);this.height = height;}}var s = new Student("xiaohongmao", 18, 111);s.getName();//调用子类特有的方法s.getAge();//s.say();//报错,静态定义需要调用类Student.say();//返回hello 子类继承了父类的静态定义,但使用的时候需要调用类//调用子类添加的属性和所有属性console.log(s.height);//111console.log(s.name);//xiaohongmaoconsole.log(s.age);//18</script></body></html>父类的属性和方法都可以被子类继承,但是特别的, 继承父类的属性时需要在子类的constructor中加入父类的参数,并且在this前调用surper(父类参数);继承父类的静态方法后使用不能调用实例,要调用子类。
5.Moudle语法
模块,管理JS文件,文件与文件之间关系。
一个html文件引用的js文件可以相互使用,这就容易造成冲突。同时他没有像Python那种将小文件拼装的方法

测试的话因为浏览器不支持impart,所以测试用nodejs的方式测试,需要全局和根目录下进行安装。
nodejs中文件与文件之间是相互隔离的。
在需要导出的js文件中代码:
export var a = "hello";在需要导入的js文件中的代码:
import { a } from "./hello.js";
console.log(a);按照下面方式添加nodejs

在命令行进行打印得到

说明



hello导出文件
export var a = "hello";
export var b = "hhhhh";
export function c() {console.log(a + b);
}
export default function () {console.log("默认函数");
}index.js文件导入文件
import { a, b as bb, c as cc } from "./hello.js";
console.log(a);
cc();
import gaga from "./hello.js";
gaga();
var b = "ddd";
console.log(b);//报错,因为重名了
import * as hello from "./hello.js";
console.log(hello.b);