原型
原型的出现,就是为了解决 构造函数的缺点
也就是给我们提供了一个给对象添加函数的方法
prototype 显示原型
每个函数天生自带的一个属性,叫 prototype,是一个对象空间
既然每个函数都有,构造函数也是函数,构造函数也有这个对象空间
这个prototype
的对象空间可以由函数名来访问
1 2 function Person ( ) {}console .log (Person .prototype );
1 2 3 4 5 6 7 8 9 10 11 function Person ( ) {}Person .prototype .name = 'prototype' ;Person .prototype .sayHi = function ( ) {};console .log (Person .prototype ); console .log (Person .sayHi ); let p1 = new Person ();console .log (p1.name , p1.sayHi );
我们发现了一个叫做prototype
的空间和函数有关
并且我们可以向里面放一些东西
重点:在函数的 prototype 里面存储的内容,不是给函数本身使用的,是给函数的每一个实例化对象使用的
__proto__
隐式原型
每个对象天生自带的一个属性,叫做__proto__
,也是一个对象空间
既然每个对象都有,实例化对象也是对象,每一个实例化的对象都有这个属性
这个__proto__
对象空间是给对象使用的
当你访问对象中的一个属性的时候
如果这个对象本身有这个属性,那么直接给你结果
如果没有,就会去__proto__
这个对象空间里面找,有的话就给你结果
未完待续
那么这个__proto__
指向哪里? 这个对象由那个构造函数 new 出来 那么这个对象的__proto__
就指向该构造函数的prototype
1 2 3 4 function Person ( ) {}let p = new Person ();console .log (p.__proto__ === Person .prototype );
实例化对象的隐式原型和构造函数的显示原型是同一个空间
我们就可以通过构造函数的prototype
来给实例化对象添加属性
对象访问属性的时候,可以去自己的__proto__
中查找
之前的构造函数不合理的地方就解决了
我们把对象的方法放在构造函数的prototype
上
实例化的对象访问的时候就去自己的__proto__
也可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function Person ( ) {}Person .prototype .sayHi = function ( ) { console .log ('你好' ); }; let p = new Person ();let p1 = new Person ();p.sayHi (); p1.sayHi ();
p 和 p1 使用的实际上就是同一个函数
prototype 和 __proto__
有什么区别
名字不一样
有可能指向同一个对象空间 => prototype 前面主语一定是函数 => proto 前面的主语一定是对象
小结 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function Person ( ) { this .name = 'alex' ; } Person .prototype .a = 100 ;let p = new Person ();console .log (p.a );
面向对象的开发思想 核心:高内聚低耦合
意义:
在开发的时候
首先写一个构造函数
这个构造函数可以生成能够完成对应的功能对象
原型和原型链
万物皆对象
问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 function Person (nam, age, gender ) { this .name = name; this .age = age; this .gender = gender; } Person .prototype .sayHi = function ( ) { console .log ('hahah' ); }; const p1 = new Person ('alex' , 18 , '男' );console .log (p1);console .log (p1.__proto__ === Person .prototype ); console .log (Person .__proto__ === Function .prototype );console .log (Person .prototype .__proto__ === Object .prototype );console .log (Function .__proto__ === Function .prototype );console .log (Function .prototype .__proto__ === Object .prototype );console .log (Object .__proto__ === Function .prototype );console .log (Object .prototype .__proto__ );
对象访问机制
当你访问一个对象的属性的时候 首先在自己身上查找,如果有,给你 停止查找 如果没有,__proto__
自己去上找 如果还没有,再去__proto__
上 找 依此类推,直到 顶级原型上(Object.prototype)都没有,返回 undefined
结论:Object.prototype 添加的内容,所有的对象数据类型都可以使用Function.prototype 添加的内容,所有的函数数据类型都可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function Person ( ) { this .name = 'alex' ; this .age = 18 ; this .gender = '男' ; } Person .prototype .a = function ( ) { console .log ('Person.prototype' ); }; Object .prototype .b = function ( ) { console .log ('Object.prototype' ); }; Function .prototype .c = function ( ) { console .log ('Function.prototype' ); }; const p = new Person ();p.a (); p.b (); Person .b ();Person .c ();
例题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function getName ( ) { console .log (5 ); } var getName;function Father ( ) { getName = function ( ) { console .log (1 ); }; return this ; } Father .getName = function ( ) { console .log (2 ); }; Father .prototype .getName = function ( ) { console .log (3 ); }; getName = function ( ) { console .log (4 ); }; Father .getName (); getName (); new Father .getName (); new Father ().getName (); new new Father ().getName ();
继承 1. 认识继承 关于 构造函数 的高阶应用 继承是出现在两个构造函数之间的关系
当构造函数 B 的实例,使用了构造函数 A 内书写的属性和方法 此时我们就说 构造函数 B 继承自 构造函数 A 构造函数 A 是构造函数 B 的父类 构造函数 B 是构造函数 A 的子类
原型继承
call 继承
组合继承
es6 的继承(完美)
2. 原型继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function Person (name, age ) { this .name = name; this .age = age; } Person .prototype .sayHi = function ( ) { console .log ('hello world' ); }; function Student (gender ) { this .gender = gender; } Student .prototype = new Person ('alex' , 18 );Student .prototype .play = function ( ) { console .log ('没日没夜的玩' ); }; const s = new Student ('男' );console .log (s);
核心:子类原型指向父类的 实例
子类.prototype = new 父类
优缺点优点 :父类的 构造函数体内 和 原型上的所有内容都能继承
缺点 :继承下来的属性不在自己身上,子类的实例的所有属性分开了两部分书写,s 自身有一部份,原型上面有一部份。同样是给 子类的实例 使用的属性,在两个位置传递参数 new Person()传递一部份,new Student() 传递一部分
不是不可以,只是不够友好
3. call 继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 function Person (name, age ) { this .name = name; this .age = age; } Person .prototype .sayHi = function ( ) { console .log ('hello world' ); }; function Student (gender, ...arg ) { this .gender = gender; Person .call (this , ...arg); } Student .prototype .play = function ( ) { console .log ('没日没夜的玩' ); }; const s = new Student ('男' , 'alex' , 18 );console .log (s);
核心:利用 call 调用父类的构造函数
优缺点: 优点:可以把继承来的属性直接出现在 子类的实例 身上,一个实例需要用的属性可以在同一个位置传递参数 缺点:只能继承 构造函数体内书写的内容, 构造函数 原型上的 不能继承
4. 组合继承 原型继承和 call/apply 继承 优缺点刚好互补 一种新的相对完美的继承方式就出来了,利用好这两种方式的优缺点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function Person (name, age ) { this .name = name; this .age = age; } Person .prototype .sayHi = function ( ) { console .log ('hello world' ); }; function Student (gender, ...arg ) { this .gender = gender; Person .call (this , ...arg); } Student .prototype = new Person ();Student .prototype .play = function ( ) { console .log ('没日没夜的玩' ); }; let s = new Student ('男' , 'alex' , 88 );console .log (s);console .log (s.name , s.age );
5. es6 继承
使用 extends 关键字 class 子类 extends 父类{…}
在子类的 constructor 内书写 super()
注意:必须两个条件同时书写,super 必须写在所有的 this 之前,父类如果是一个 es5 的构造函数,那么也可以正常继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 function Person (name, age ) { this .name = name; this .age = age; } Person .prototype .sayHi = function ( ) { console .log ('hello world' ); }; class Student extends Person { constructor (gender, name, age ) { super (name, age); this .gender = gender; } play ( ) { console .log ('没日没夜的玩' ); } } let s = new Student ('男' , 'alex' , 18 );console .log (s);
js的运行原理 V8如何把js运行起来 V8引擎原理 js代码的执行 高级语言写的代码最终都是通过计算机硬件(CPU)运行的,
但计算机不能直接识别js的代码,需要通过一定的转换,变成计算机能够识别的二进制机器码,计算机执行后才能看到实现的效果。
什么是V8 V8是Google使用C++编写的开源高性能JS和WebAssmbly引擎,广泛应用于chrome和Node.js,能够跨平台,独立运行,还能嵌入任何C++应用程序中
V8架构 Parse 解析 (https://v8.dev/blog/scanner )
将js代码转化成AST(抽象语法树),如果函数没有调用,不会将函数转化成抽象树。
Ignition 解释器 (https://v8.dev/blog/ignition-interpreter )
将AST转换成bytecode(字节码)
帮助Turbo Fan收集信息
如果函数只调用一次,Ignition会执行bytecode
Turbo Fan 编译器 (https://v8.dev/blog/turbofan-jit )
收集信息,优化代码的执行速度
反向优化,重新将机器码转化成字节码
多次调用的函数在Turbo Fan 中也叫热点函数
Garbage Collection 垃圾回收机制
js执行上下文GO AO VO 2.1 初始化全局对象
在执行代码之前,js引擎会在堆内存中创建一个全局对象:Global Object(GO)
在浏览器中全局对象就是window,所以在执行所有代码之前window已经创建好了,该对象所有的作用域都能访问,里面会包含Date、Math、String
2.2 执行上下文 js引擎内部有一个执行上下文,它是用于执行代码的调用栈
GEC(全局执行上下文)被放入ESC(执行上下文)中包含两部分内容
在代码执行之前,在parser转成AST的过程中,它会将全局定义的变量,函数放入到GO中,但是不会赋值,这个过程也被称为变量作用域的提升
在代码执行过程中,对变量赋值或者执行其他的函数
只要执行代码js就会创建一个执行上下文
2.3 VO对象
补充:Function.length属性 length
属性指明函数的形参个数 。
length
是函数对象的一个属性值 ,指该函数期望传入的参数数量,即形参的个数。
形参的数量不包括剩余参数(…args) 个数,仅包括第一个具有默认值之前 的参数个数。
与之对比的是,arguments.length
是函数被调用时实际传参的个数。
3.函数代码执行
总结 :
当我们去执行一段代码,不管是全局代码还是函数内部的代码,都会创建一个对应的执行上下文,如果执行的是全局代码,创建的VO就是GO,如果执行的是函数内部的代码,创建的VO就是AO
全局代码如何执行 函数的 length 属性
形参个数
函数全部的形参没有默认值,则function 的length就是形参的个数
1 2 3 4 5 6 function fn1 ( ) {}function fn2 (name ) {}function fn3 (name, age ) {}console .log (fn1.length ); console .log (fn2.length ); console .log (fn3.length );
默认参数
如果函数的形参有默认值,则function 的 length 是第一个具有默认值之前 的形参个数
1 2 3 4 5 6 7 8 9 10 11 12 13 function fn1 (name ) {}function fn2 (name = 'alex' ) {}function fn3 (name, age = 22 ) {}function fn4 (name, age = 22 , gender ) {}function fn5 (name, age, gender = '男' ) {}function fn6 (name = 'alex' , age, gender ) {}console .log (fn1.length ); console .log (fn2.length ); console .log (fn3.length ); console .log (fn4.length ); console .log (fn5.length ); console .log (fn6.length );
剩余参数
1 2 function fn1 (name, ...args ) {}console .log (fn1.length );
面试题: 123['toString'].length+123=124
3.函数代码执行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var message = 'Global Message' ;function foo (num ) { var message = 'Foo Message' ; var age = 18 ; var height = 1.88 ; console .log ('foo function' ); } foo (123 );var num1 = 10 ;var num2 = 20 ;var result = num1 + num2;console .log (result);
因为每个执行上下文都会关联一个 VO ,那么函数执行的时候也要关联对应的 VO,函数 的 VO 叫做 AO 对象 (Activation Object),这个 AO 对象会作为执行上下文的 VO 来存放函数中的私有变量
易错点 :创建函数的时候就会创建一个 AO 对象,完全不对,这两者没有任何关系,AO 是函数调用的时候才有的
4.函数代码多次执行 重新创建对应的 EC,关联对应的 VO,创建对应的 AO,执行完以后弹出栈,如果我们不做任何其他的操作,销毁 AO。
5. 函数代码互相调用 一样的操作,只不过是创建对应的 EC,关联对应的 VO,创建对应的 AO,执行完以后弹出栈,如果我们不做任何其他的操作,销毁 AO
函数执行过程 作用域和作用域链 6.1 全局变量的查找顺序 去自己的 VO 的查找
1 2 3 console .log (message);var message = 'Global message' ;console .log (message);
作用域提升:对后面定义的变量做一个提前的访问。
6.2 函数变量的查找顺序 1 2 3 4 5 6 7 8 9 console .log (message);var message = 'Global message' ;function foo ( ) { var message = 'foo message' ; console .log (message); } foo ();
作用域链 作用域链它是一个对象列表,用于变量标识符的求值 当进入一个执行上下文的时候,作用域链被创建,并且根据代码类型,添加一系列的对象。
如果是全局,作用域链就只有一个 GO;如果是函数,当函数被创建的时候,而不是调用,它的作用域链就被确定了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 console .log (message);var message = 'Global message' ;function foo ( ) { console .log (message); } foo ();var obj = { name : 'obj' , messsage : '12312' , bar : function ( ) { var message = 'obj内的bar' ; foo (); }, }; obj.bar (); obj['bar' ]();
6.3 多层嵌套的查找顺序 1 2 3 4 5 6 7 8 9 10 11 12 13 console .log (message);var message = 'Global message' ;function foo ( ) { var name = 'foo' ; function bar ( ) { console .log (name); } return bar; } var bar = foo ();bar ();
6.4 几道经典的面试题 1 2 3 4 5 6 var n = 100 ;function foo ( ) { n = 200 ; } foo ();console .log (n);
foo 自身的 AO 里面没有 n,去找[[Scopes]]
,只有一个 GO,修改成 200
1 2 3 4 5 6 7 var n = 100 ;function foo ( ) { console .log (n); var n = 200 ; console .log (n); } foo ();
1 2 3 4 5 6 7 8 9 10 var n = 100 ;function foo1 ( ) { console .log (n); } function foo2 ( ) { var n = 200 ; console .log (n); foo1 (); } foo2 ();
1 2 3 4 5 6 7 var n = 100 ;function foo ( ) { console .log (n); return ; var n = 200 ; } foo ();
1 2 3 4 5 6 function foo ( ) { msg = 'hello world' ; } foo ();console .log (msg);
1 2 3 4 5 6 function foo ( ) { var a = (b = 200 ); } foo ();console .log (a); console .log (b);
javascript 的内存管理 1.1 认识内存管理 不管是什么样的编程语言,在代码的执行过程中都是需要给他分配内存 的,不同的是有些编程语言需要我们自己手动管理内存,有些编程语言可以自动管理内存。
不管是什么方式来管理内存,内存的管理都会有如下的生命周期 :
分配申请你需要的内存(申请)
使用分配的内存
不需要使用时,对其进行释放
1.2 js 的内存管理
2. 垃圾回收机制算法 2.1 js 的垃圾回收(Garbage Collection) 原因: 因为内存的大小是有限的,所以当内存里面的对象不再需要时,我们就要对他进行释放,以便腾出空间。 Garbage Collection,简称 GC。
2.2引用计数(Reference counting)
当一个对象有一个引用 指向他时,这个对象的引用就+1 ;
当一个对象的引用为 0 时,这个对象就可以被销毁 掉
1 2 3 4 5 let user = { username : 'alex chen' }; let user2 = user; user = null ; user2 = null ;
这个方法虽然看起来合理,但是存在明显的漏洞,那就是循环引用
1 2 3 4 5 6 7 8 9 let boy = {};let girl = {};boy.girlfriend = girl; girl.boyfriend = boy; boy = null ; girl = null ; boy.girlfriend = null ; girl.boyfriend = null ;
2.3 标记-清除(mark-Sweep) 核心思路是可达性,这个算法是设置一个根对象(Root Object),垃圾回收器会定期从根开始,找到所有从根开始引用的对象,对于那些没有的对象,认为是后续不再用的,就给他销毁,这个算法可以很好的解决循环引用的问题。
2.4 常见的其他 GC 算法
标记整理(Mark-Compact)
分代收集(Generational collection) 对象被划分成两组:’新的’和’旧的’ 减少对老旧对象的扫描频率
增量收集(Incremental collection) 大的->很多个小的
闲时收集(idle-time collection)
2.5 V8 引擎详细内存图
3. 闭包 3.1 js 的函数式编程 js 中函数非常重要,而且是一等公民。函数可以作为另一个函数的参数,也可以作为函数的返回值。 vue:optionsAPI->compositionAPI->函数 hook react:class->function->hooks
3.2 闭包的概念 在计算机科学:Closure,又叫词法闭包(Lexical Closure)或者函数闭包,是在头等函数 的编程语言中,实现词法绑定的一种技术,闭包在实现上是一个结构体,他存储了一个函数和一个关联的环境。闭包跟函数最大的区别在于,当捕获闭包的时候,他的自由变量 会在被捕捉时确定。这样即使脱离了上下文,他也能照常运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var name = 'alex' ;var age = 18 ;var info = '唱跳rap' ;function foo ( ) { var msg = 'hello world' ; console .log (msg, name, age, info); } function bar (name, age, info, address, hobby, height ) { var msg = 'hello world' ; console .log (msg, name, age, info); function baz ( ) { console .log (msg, name, age, info); } } bar (name, age, info, address, hobby, height);foo ();
私人: 广义上来说,js 中的函数都是闭包。 狭义说:js 中的函数,访问了外层作用域的变量,才叫做闭包。
4. 闭包的形成过程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function createAdder (count ) { return function (num ) { return count + num; }; } let adder5 = createAdder (5 );adder5 (15 ); adder5 (25 );adder5 (35 );let adder8 = createAdder (8 );adder8 (2 );adder8 (12 );adder8 (22 );
看图
5. 闭包的内存泄漏 我们图里 adder5 adder8 以及对应的 AO 对象,存在引用而且也是根可达的所以即使后续不再使用也不会释放。我们经常说的闭包的内存泄漏,其实就是这些引用链上的对象无法释放。
5.1 如何释放 比如说我们要释放 adder5,adder5=null
5.2 内存泄漏的测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <button class ="create" > 创建很多对象</button > <button class ="destory" > 销毁很多对象</button > <script > function createArray ( ) { var arr = new Array (1024 * 1024 ).fill (1 ); function test ( ) { console .log (arr); } return test; } var totalArr = []; let createBtn = document .querySelector ('.create' ); let destoryBtn = document .querySelector ('.destory' ); createBtn.onclick = function ( ) { for (let i = 0 ; i < 100 ; i++) { totalArr.push (createArray ()); } console .log (totalArr.length ); }; destoryBtn.onclick = function ( ) { totalArr = []; }; </script >
5.3 AO 不使用的属性优化 不被销毁的 AO 对象,里面所有的属性都一直保留吗?结论 :不使用的不会保留–浏览器自己的优化
1 2 3 4 5 6 7 8 9 10 11 12 function foo ( ) { var name = 'foo' ; var age = 18 ; var height = 1.88 ; function bar ( ) { debugger ; console .log (name); } return bar; } var fn = foo ();fn ();
函数的增强知识 1.1 函数的属性和 arguments 1.1.1 函数的属性 1 2 3 4 5 function foo (a, b, c ) {}let obj = {};obj.address = '天河' ; foo.msg = 'hello foo' ;
除了自定义属性以外,函数对象中已经有一些自己的属性。
name :函数的名字
length :第一个具有默认值参数之前的参数的个数
1.1.2 Arguments类数组对象 概念:**arguments
** 是一个对应于传递给函数的参数的类数组对象。
array-like 对象不是一个真正的数组,它类似于Array
,但除了 length 属性和索引元素之外没有任何Array
属性。如pop、filter、map等方法
在开发中,如果需要对 arguments
进行操作的时候要用到数组的一些特性,那我们就需要把他转成数组。
转成数组的三种方法
遍历添加
1 2 3 4 5 6 7 8 9 function foo ( ) { console .log (arguments ); let newArr = []; for (const arg of arguments ) { newArr.push (arg); } console .log (newArr); } foo (10 , 20 , 12 , 11 , 333 );
es6 的方法
1 2 let newArr = [...arguments ];let newArr1 = Array .from (arguments );
slice+apply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let arr = ['a' , 'b' , 'c' ];let bar = arr.slice (); console .log (arr, bar);function foo ( ) { console .log (arguments ); let newArr = Array .prototype .slice .apply (arguments ); let newArr1 = [].slice .apply (arguments ); } foo (10 , 20 , 12 , 11 , 333 );
箭头函数的 arguments
1 2 3 4 5 6 7 8 9 function foo ( ) { let bar = ( ) => { console .log (arguments ); }; bar (); } foo (11 , 22 );
ES6的Rest parameters(剩余参数) 概念 :将不定量的参数放入一个数组中
1 2 3 4 function foo (num1, num2, ...otherNums ) { console .log (otherNums); } foo (1 , 2 , 3 , 4 , 5 , 6 , 7 );
剩余参数和 arguments
区别
剩余参数是一个真正的数组,arguments
是一个类数组对象
剩余参数只包括没有对应形参的实参,arguments
包括所有的实参。
arguments
对象还有一些附加的属性(如callee属性)
注意 :剩余参数只能写在最后面
1.2 纯函数的理解和应用 1.2.1 纯函数的理解 在程序设计中,若一个函数符合以下条件,那么这个函数被称为纯函数 。
此函数在相同的输入 时,需产生相同的输出 。
函数的输出和输入 值和其他的隐藏信息 或者外部设备状态无关 。
该函数不能有语义 上可观察的函数副作用 。
私人:
确定的输入,一定产生确定的输出
函数执行的过程中,不能产生副作用
计算机中的副作用
概念:在执行一个函数的时候,除了返回对应的值。还对调用函数产生了附加的影响,比如修改了全局变量,修改了参数,修改了外部的存储。
判断以下函数是不是纯函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function sum (a, b ) { return a + b; } function printInfo (info ) { console .log (info.name , info.age , info.msg ); } let obj = { name : 'alex' , age : 99 , msg : '哈哈哈哈' , }; printInfo (obj);
例子
slice:截取数组,不会对原数组进行任何操作,而是返回一个新数组给你。是纯函数
splice:截取数组,返回一个新数组给你,就是已经改变的原数组。不是纯函数
1.2.2 纯函数的优势
1 2 3 4 5 6 7 8 let counter = 0 ;function add (num ) { return num; }
1.3 柯里化的理解和应用 1.3.1 柯里化的理解 概念: 在计算机科学中,柯里化(Currying)是把接收多个参数的函数 变换成接受一个单一参数(最初函数的第一个参数)的函数 ,并且返回接收余下的参数且返回结果的新函数 的技术。
f(a,b,c)
转成f(a)(b)(c)
注意: 柯里化不会调用函数 ,只是对函数进行转化 。
1.3.2 柯里化函数变换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function foo1 (x, y, z ) { console .log (x + y + z); } foo1 (10 , 20 , 30 );function foo2 (x ) { return function (y ) { return function (z ) { console .log (x + y + z); }; }; } foo2 (10 )(20 )(30 );let foo3 = (x ) => (y ) => (z ) => { console .log (x + y + z); }; foo3 (10 )(20 )(30 );
1.3.3 柯里化函数优势
函数的职责单一
函数的参数复用
1.3.4 柯里化函数练习 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function logInfo (date ) { return function (type ) { return function (msg ) { console .log (`时间${date} -类型${type} -内容${msg} ` ); }; }; } var logToday = logInfo ('2023-05-16' ); var logTodayDebug = logToday ('debug' ); var logTodayFeature = logToday ('feature' ); var logTodayInfo = logToday ('info' ); logTodayDebug ('修复按钮点击' );logTodayDebug ('修复接口' );logTodayDebug ('修复排序' );logTodayFeature ('新增过滤' );logTodayFeature ('新增统计功能' );
1.3.5 自动柯里化函数 如何定义一个函数可以将多个参数的普通函数转成柯里化的函数
1.4 组合函数的理解和应用 1.4.1 组合函数概念的理解 把依次调用的多个函数组合起来,让他们自动的依次调用,这个过程就是组合函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var num = 100 ;function double (num ) { return num * 2 ; } function pow (num ) { return num ** 2 ; } console .log (pow (double (123 )));function composeFn (num ) { return pow (double (num)); } console .log (composeFn (123 ));
1.4.2 自动组合函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 function double (num ) { return num * 2 ; } function pow (num ) { return num ** 3 ; } function composeFn (...fns ) { let length = fns.length ; if (length <= 0 ) return ; for (let i = 0 ; i < length; i++) { let fn = fns[i]; if (typeof fn !== 'function' ) { throw new Error (`第${i} 个参数必须是一个函数` ); } } return function (...args ) { let res = fns[0 ].apply (this , args); for (let i = 1 ; i < length; i++) { let fn = fns[i]; res = fn (res); } return res; }; } let newFn = composeFn (double, pow, console .log );newFn (100 );let doubleRes = double (100 );let powRes = pow (doubleRes);console .log (powRes);
1.5 with、eval with:扩展一个语句的作用域链 eval:执行字符串格式的 js 代码
1.6 严格模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 'use strict' ;function foo ( ) { console .log (this ); } foo (); foo.apply ('abc' );
2.对象 2.1 Object.defineProperty 对对象的属性做精准的控制,属性描述符Object.defineProperty(obj,prop,desciptor)
obj 要定义属性的对象 prop 属性名 desciptor 对该属性的描述
desciptor 有两种类型
数据属性 Data
存取属性 Accessor
详细的说明 :https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 var obj = { name : 'alex' , address : '天河' , }; let _name;Object .defineProperty (obj, 'name' , { configurable : true , enumerable : false , set : function (value ) { console .log ('set被调用' , value); _name = value; }, get : function ( ) { console .log ('get被调用了' ); return _name; }, }); obj.name = '123' ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let i = 1 ;Object .defineProperty (window , 'a' , { get : function ( ) { return i++; }, }); console .log (a == 1 && a == 2 && a == 3 );let obj = {};Object .defineProperties (obj, { name : { configurable : true , enumerable : true , writable : true , value : 'alex' , }, age : {}, height : {}, });
2.2 一些新方法
禁止对象扩展属性
密封对象 不允许你配置和删除属性
冻结对象 不允许修改现有属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var obj = { name : 'alex' , age : 18 , }; Object .freeze (obj);obj.name = 'bbb' ; console .log (obj);
1. 对象方法的补充 1.1 hasOwnProperty 对象是否有某个属于自己的属性(不是原型上的)
1 2 3 4 5 6 7 let obj = { name : 'alex' , age : 18 , }; obj.__proto__ .address = '天河' ; console .log (obj.hasOwnProperty ('name' )); console .log (obj.hasOwnProperty ('address' ));
1.2 in 操作符 判断某个属性是否在某个对象或者对象的原型上
1 2 3 4 5 6 7 8 9 10 11 let obj = { name : 'alex' , age : 18 , }; obj.__proto__ .address = '天河' ; console .log ('name' in obj); console .log ('address' in obj); for (const key in object) { console .log (key); }
1.3 instanceof 用于检测构造函数的 prototype 是否出现在某个实例对象的原型链上 就是用于判断对象和(类)构造函数之间的关系
1 2 3 4 5 6 7 8 9 10 11 12 13 function Person ( ) {}function Student ( ) { Person .call (this ); } Student .prototype = new Person ();var stu = new Student ();console .log (stu instanceof Student ); console .log (stu instanceof Person ); console .log (stu instanceof Function ); console .log (stu instanceof Object ); console .log (stu instanceof Array );
1.4 isPrototypeOf 用于检测某个对象,是否出现在某个实例对象的原型链上 可以判断对象之间的继承关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function Person ( ) {}function Student ( ) { Person .call (this ); } Student .prototype = Person ();var stu = new Student ();console .log (Student .prototype .isPrototypeOf (stu)); console .log (Person .prototype .isPrototypeOf (stu)); var obj = { name : 'alex' , age : 28 , }; function createObject (o ) { function F ( ) {} F.prototype = o; return new F (); } let info = createObject (obj);console .log (obj.isPrototypeOf (info));
2. class 方式定义类 2.1 认识 class 定义类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person {}let alex = new Person ();let jack = new Person ();console .log (alex, jack);const Student = class {};const foo = function ( ) {};let aa = new Student ();console .log (aa);
2.2 class 类中的内容 new 的时候发生了什么事:会调用构造函数的 constructor,并执行如下操作,在内存中创建一个新的空对象;这个对象内部的__proto__
属性会被赋值该构造函数的 prototype 属性;构造函数内的 this 会指向创建出来的新对象;执行构造函数体内的代码;如果构造函数没有返回非空对象,则返回创建出来的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person { constructor (name, age ) { this .name = name; this .age = age; } running ( ) { console .log (this .name + '疯狂的跑' ); } eating ( ) { console .log (this .name + '嘎嘎炫' ); } } let obj = new Person ('alex' , 18 );console .log (obj);console .log (Person .prototype === obj.__proto__ ); console .log (Person .running );console .log (Person .prototype .running );
2.3 类和构造函数的区别 class 定义的类不能作为普通函数去调用,必须和 new 连用
2.4 类的访问器方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 let obj = { _name : 'alex' , }; Object .defineProperty (obj, 'name' , { configurable : true , enumerable : true , set ( ) {}, get ( ) {}, }); let obj2 = { _name : 'alex' , set name (value ) { this ._name = value; }, get name () { return this ._name ; }, }; class Person { constructor (name, age ) { this ._name = name; } set name (value ) { console .log ('设置name' ); this ._name = value; } get name () { console .log ('获取name' ); return this ._name ; } } let p1 = new Person ('alex' , 18 );class Rectangle { constructor (x, y, width, height ) { this .x = x; this .y = y; this .width = width; this .height = height; } get position () { return { x : this .x , y : this .y }; } get size () { return { width : this .width , height : this .height }; } } let rect1 = new Rectangle (10 , 20 , 100 , 200 );console .log (rect1.position );console .log (rect1.size );
3. extends 实现继承 3.1 继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Person { constructor (name, age ) { this .name = name; this .age = age; } running ( ) { console .log ('明天🏃' ); } eating ( ) { console .log ('明天吃KFC' ); } } class Student extends Person { constructor (name, age, sno, score ) { super (name, age); this .sno = sno; this .score = score; } studying ( ) { console .log ('不开灯的卷' ); } } let stu = new Student ('alex' , 18 , 111 , 59 );stu.eating (); stu.running (); stu.studying ();
3.2 super 关键字 super 使用的位置有三个,子类的构造函数,实例方法,静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Animal { running ( ) { console .log ('🏃' ); } eating ( ) { console .log ('🍽️' ); } static sleep ( ) { console .log ('没日没夜的睡' ); } } class Dog extends Animal { running ( ) { console .log ('四条腿跑' ); super .running (); } static sleep ( ) { console .log ('趴着' ); super .sleep (); } } let dog = new Dog ();dog.running (); dog.eating (); Dog .sleep ();
3.3 继承内置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Array .prototype .lastItem = function ( ) { return this [this .length - 1 ]; }; let arr = new Array (10 , 20 , 30 );console .log (arr.__proto__ === Array .prototype );console .log (arr.lastItem ());
4. 多态概念的理解 面向对象的三大特性:封装、继承、多态 私人:不同数据类型,同一个操作,表现出不同的行为,就是多态的体现
1 2 3 4 5 function sum (a, b ) { console .log (a + b); } sum (1 , 2 ); sum ('1' , 2 );
5. 补充 5.1 对象字面量的增强写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let name = 'alex' ;let age = 18 ;let key = 'address' + 'city' ;let obj = { name, age, eating ( ) {}, [key]: '广州' , }; console .log (obj);
5.2 回顾函数的原型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function foo (name, age ) { console .log (this , name, age); } function test ( ) {}foo.apply ('abc' , ['alex' , 18 ]); console .log (foo.apply === Function .prototype .apply );Function .prototype .info = 'hello alex' ;foo.info ; test.info ; Function .prototype .bar = function ( ) { console .log ('bar~~~~~~' ); }; foo.bar (); test.bar ();
5.3 手写 apply 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function foo (name, age ) { console .log (this , name, age); } Function .prototype .myapply = function (thisArg, otherArgs ) { thisArg = thisArg === null || thisArg === undefined ? window : Object (thisArg); Object .defineProperty (thisArg, 'fn' , { enumerable : false , configurable : true , value : this , }); thisArg.fn (...otherArgs); delete thisArg.fn ; }; foo.myapply ('aaa' , ['alex' , 18 ]); foo.myapply ('aaa' , ['alex' , 18 ]); foo.myapply (null , ['alex' , 18 ]);
5.4 手写 call 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function foo (name, age ) { console .log (this , name, age); } Function .prototype .mycall = function (thisArg, ...otherArgs ) { thisArg = thisArg === null || thisArg === undefined ? window : Object (thisArg); Object .defineProperty (thisArg, 'fn' , { enumerable : false , configurable : true , value : this , }); thisArg.fn (...otherArgs); delete thisArg.fn ; }; foo.mycall ('aaa' , 'alex' , 18 ); foo.mycall ('aaa' , 'alex' , 18 ); foo.mycall (null , 'alex' , 18 );
5.5 call-apply 抽取封装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function foo (name, age ) { console .log (this , name, age); } Function .prototype .myexec = function (thisArg, otherArgs ) { thisArg = thisArg === null || thisArg === undefined ? window : Object (thisArg); Object .defineProperty (thisArg, 'fn' , { enumerable : false , configurable : true , value : this , }); thisArg.fn (...otherArgs); delete thisArg.fn ; }; Function .prototype .myapply = function (thisArg, otherArgs ) { this .myexec (thisArg, otherArgs); }; Function .prototype .mycall = function (thisArg, ...otherArgs ) { this .myexec (thisArg, otherArgs); }; foo.mycall ('aaa' , 'alex' , 18 ); foo.myapply ('aaa' , ['alex' , 18 ]);
手写 bind 相比上面,不用调用而是返回这个 fn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function foo (name, age, height, address ) { console .log (this , name, age, height, address); } Function .prototype .mybind = function (thisArg, ...otherArgs ) { thisArg = thisArg === null || thisArg === undefined ? window : Object (thisArg); Object .defineProperty (thisArg, 'fn' , { enumerable : false , configurable : true , value : this , }); return (...newArgs ) => { let allArgs = [...otherArgs, ...newArgs]; thisArg.fn (...allArgs); }; }; let newFoo = foo.bind ('abc' , 'alex' , 18 );newFoo (1.88 , 18888 );
ES6 1. 函数 1.1 默认参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function foo (arg1, arg2 ) { arg1 = arg1 ?? '我是默认值' ; console .log (arg1); } foo (123 );foo ('123' );foo ('' );foo (false );foo (null );foo (0 );foo (undefined );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function foo (age, name, height = 1.88 , ...args ) { console .log (name, age, height); } foo (18 , 'alex' );let obj = { name : 'alex' , age : 18 , }; function bar ({ name, age } = obj ) { console .log (name, age); } bar ();
1.2 箭头函数补充 1 2 3 4 5 6 7 8 9 10 function foo ( ) {}let f = new foo ();console .log (foo.__proto__ ); console .log (foo.prototype === f.__proto__ ); let bar = ( ) => {};console .log (bar.__proto__ === Function .prototype ); console .log (bar.prototype ); let b = new bar ();
2. 展开运算符 2.1 展开的基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 let str = 'helloworld' ;console .log (...str);const names = ['abc' , 'cba' , 'nba' , 'mba' ];const newNames = [...names, 'aa' , 'bb' ];function foo (name1, name2, ...args ) { console .log (name1, name2, args); } foo (...names);foo (...str);const obj = { name : 'alex' , age : 18 , }; const info = { ...obj, height : 1.88 , }; foo (...obj);
2.2 引用赋值和深浅拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 const obj = { name : 'alex' , friend : { name : 'autoCurry' , }, }; let info2 = JSON .parse (JSON .stringify (obj));info2.friend .name = '我是info2哈哈哈' ; console .log (obj, info2);
3. Symbol 生成独一无二的值
3.1 初识 symbol 1 2 3 4 5 6 7 8 9 const s1 = Symbol ();const obj = { name : 'aaaaaaalex' , [s1]: 'aaa' , }; const s2 = Symbol ();console .log (s1 == s2); obj[s2] = 'bbb' ; console .log (obj);
3.2 symbol 注意和 api 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 const s1 = Symbol ();const s2 = Symbol ();const obj = { name : 'alex' , [s1]: 'aa' , [s2]: 'bb' , }; const obj1 = {};obj1[s1] = 'aa' ; obj1[s2] = 'bb' ; const obj2 = {};Object .defineProperty (obj2, s1, { value : 'aaa' , }); console .log (Object .keys (obj)); console .log (Object .getOwnPropertySymbols (obj)); const symbolKeys = Object .getOwnPropertySymbols (obj);for (const key of symbolKeys) { console .log (obj[key]); } const s3 = Symbol ('ccc' );console .log (s3);console .log (s3.description );const s4 = Symbol (s3.description );console .log (s3 === s4); const s5 = Symbol .for ('ddd' );const s6 = Symbol .for ('ddd' );console .log (s5 === s6); console .log (Symbol .keyFor (s5));
4. Set 集合 类似数组,但是最大区别是天生不允许重复。只能通过构造函数来创建
4.1 set 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 const set = new Set ();set.add (10 ); set.add (20 ); set.add (22 ); set.add (22 ); console .log (set);const obj = {};const names = ['abc' , 'abc' , 'cba' , 'cba' , 'nba' , 'mba' ];const newNamesSet = new Set (names);const arr = Array .from (newNamesSet);set.add (100 ); console .log (set);set.delete (100 ); console .log (set);console .log (set.has (obj)); set.forEach ((item ) => console .log (item)); for (const item of set) { console .log (item); }
4.2 WeakSet weak Reference 弱引用:可以通过引用获取到对应的数据,但是他引用的那个对象不保证一定不被销毁. strong Reference 强引用:我保证你引用的对象永远存在
1 2 3 4 5 6 7 8 9 10 11 let obj1 = { name : 'alex' };let obj2 = { name : 'jack' };let obj3 = { name : 'kern' };let arr = [obj1, obj2, obj3];obj1 = null ; obj2 = null ; obj3 = null ; const set = new Set (arr);arr = null ; console .log (set);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 let obj1 = { name : 'alex' };let obj2 = { name : 'jack' };let obj3 = { name : 'kern' };let arr = [obj1, obj2, obj3];const weakset = new WeakSet ();weakset.add (obj1); weakset.add (obj2); weakset.add (obj3); console .log (obj1);const pWeakSet = new WeakSet ();class Person { constructor ( ) { pWeakSet.add (this ); } running ( ) { if (!pWeakSet.has (this )) { console .log ('type error 调用方式不对' ); } else { console .log ('跑~' ); } } } let p = new Person ();p.running (); const fn = p.running ;fn ();const obj = { run : p.running , }; obj.run ();
WeakSet 只能存放对象类型,不能 add 基本类型 WeakSet 是弱引用。 WeakSet 不能遍历,原因是:WeakSet 是弱引用,如果遍历获取其中的元素,有可能导致对象不能正常的销毁。
5. Map 映射 5.1 Map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 const info = { name : 'alex' };const info2 = { name : 'jack' };const map = new Map ();map.set (info, 'aaaa' ); map.set (info2, 'bbb' ); console .log (map);console .log (map.get (info));console .log (map.has (info));map.forEach ((item ) => console .log (item)); for (const item of map) { console .log (item); const [key, value] = item; console .log ('解构后' , key, value); }
5.2 WeakMap 区别一:WeakMap 只能用对象做 key,不接受其他类型 区别二:WeakMap 弱引用。
Proxy-Reflect 监听对象的操作(ES5)
首先Object.defineProperty
设计的初衷,不是为了数据劫持和数据代理,也就是不是为了监听对象中所有的属性,详细地定义对象的属性,只不过我们的操作强行让它能够监听而已。
其次我们想监听一些更加丰富的操作的时候,没办法。所以 ES6 给我们提供了 proxy
监听对象的操作(ES6) Proxy 类,用于帮助我们创建一个代理,监听对象的时候,我们可以先创建一个代理对象(proxy),之后所有对该对象的操作都通过代理对象来完成。const p = new Proxy(target,handler)
target:你要代理的目标对象 hanlder:你需要做那些处理
set: 四个参数 target 目标对象 property 属性 value 值 receiver 调用的代理对象
get: 三个参数 target 目标对象 property 属性 receiver 调用的代理对象
Reflect 和 Object Reflect(反射)也是 ES6 新增的 API,它是一个对象。提供了操作对象的方法
1 2 3 4 5 6 7 8 const obj = {};console .log (obj.__proto__ );console .log (Object .getPrototypeOf (obj));console .log (Reflect .getPrototypeOf (obj));
明明已经有 Object 可以做这些操作,为什么还要有 Reflect?这是因为早期 ECMA 规范没有考虑到对对象本身的操作如何设计 更加好。Object 在 js 内属于顶级的类,Array,Function,Number,String…都继承于他,从语言设计层面来说,作为所有类的父类,你本身不应该包含太多东西。
ES6 新增的 Reflect 就把一些操作集中到了 Reflect 对象上。Reflect 就是专门来做对象的操作用的,Proxy 也可以不操作原对象
Promise Promise 的概念 Promise 是一个类,承诺,许诺 当我们需要的时候,给予调用者一个承诺,待会给你回调的数据,就可以来创建一个 promise 的实例 通过 new 创建 Promise 对象的时候,传入了一个回调函数,称为
Promise是一个用来处理不会立即得到数据或结果(异步操作)的一个构造函数
executor(执行函数)
这个回调函数会被立即执行,并且接受另外的两个回调函数 resolve,reject
当我们调用 resolve 时,会执行 promise 对象的 then 方法传入的回调函数
当我们调用 reject 时,会执行 promise 对象的 catch 方法传入的回调函数
promise 的三个状态
待定(pending):初始状态,即没有被兑现,也没有被拒绝;当执行 executor 中的代码时,处于该状态。
已兑现(fulfilled):意味着操作完成,执行了 resolve,处于该状态的话 promise 已经被兑现了
已拒绝(rejected):意味着操作失败,执行了 reject,处于该状态的话 promise 已经被拒绝了
executor(执行函数) 我们往往会在 executor 中确定 promise 的状态-叫已决议 注意:一旦状态被确定下来,Promise 状态就会被锁死 🔒,该 Promise 的状态就不可变 ,在我们调用 resolve 的时候,如果 resolve 的值不是一个 Promise,那么会将该 Promise 的状态变成成功。resolve 调用之后再 reject 调用,reject 的代码会执行,但是不会改变 Promise 的状态,并不是不执行代码.
resolve 参数的三种情况 then then 方法是返回一个全新的 promise,这个 promise 他的状态是等到 then 传入的回调函数有返回值时,再进行决议。
finally ES9(ES2018)中新增的一个特性,无论你的 promise 对象是 fulfilled 还是 rejected 状态,最终都要执行的代码。finally 不接收参数。
Promise的类方法 Promise.resolve 最大的作用,就是把一个现成的内容转成 Promise 来使用,就可以使用 Promise.resolve。 作为 resolve 来说,他的参数是有三种情况 04.html 讲过
Promise.reject reject 方法类似于 resolve注意:不管传入 Promise.reject 的参数是何种形式,都会直接作为 rejected 状态给到 catch 的回调
其他类方法… 都是处理多个 promise 的类方法 all allSettled any race
async async
关键字定义的函数叫做异步函数 ,返回一个Promise对象
语法:
1 2 3 4 5 6 7 8 9 10 async function foo ( ) { }let bar =async ( )=>{}class foo { async bar ( ) { } }
await await
关键字必须和async
一起使用才有效果。如果有多个await,则会等上一个await的promise对象的状态为兑现状态时,才会执行下一个await后面的表达式
特点:await
会阻塞async函数后面代码的执行,直到await后面的表达式结果出来后才会执行后面的代码
await返回的值就是await后面表达式的值
如果await后面表达式是一个普通值,则返回值就是这个普通值
如果await后面表达式是一个兑现状态(fulfilled )的promise对象,则返回值就是兑现状态的值
1 2 3 4 5 6 7 8 9 10 async function foo ( ) { let res1 = await 123 console .log (res1); let res2 = await new Promise ((reslove, reject ) => { reslove ("success" ) }) console .log (res2); } foo ()