一.函数定义的方式
-
函数声明
特点:定义前后都能执行
为什么前后都能执行?
因为函数声明的方式:函数提升
栗子:
function fn(){ //代码块 }
-
函数表达式
特点:只能在定义后调用才能执行
var fn = function(){ //代码块 } fn()
二.new函数,访问原型方法
我们通过一道经典的面试题来了解原型:
function Foo() {
getName = function () { alert(1) };
return this;
}
Foo.getName = function () { alert(2) };
Foo.prototype.getName = function () { alert(3) };
//函数表达式
var getName = function () { alert(4) }
//函数声明
function getName() { alert(5) }
//请写出输出结果
Foo.getName()
getName()
Foo().getName()
getName()
new Foo.getName()
new Foo().getName()
new new Foo().getName()
不要偷看下面的答案哦
===========================================================
===========================================================
下面是答案
===========================================================
===========================================================
答案:2,4,1,1,2,3,3
解析
-
第一个就没什么难度吧,直接调用Foo.getName,所以直接弹出 2
-
第二个其实难度也不是很大的,可能有些小伙伴会往下面看
比如:可能打印2,4或者5?
但是实际我们仔细看看:
下面那个函数表达式=》var getName = function()
这里的var有一个变量提升,会提升一级。
所以打印 4
-
第三个,重点来了,小伙伴们会不会猜到2或者3?
其实我刚开始也是猜到2的
但是呢,第一行就定义了Foo()的函数,所以直接打印的是1
-
第四个,其实我们仔细观察一下:
第四个和第三个其实是一样的:
第一个声明的Foo()函数里这样声明getName = function()
函数直接声明表示全局设置,所以这里的this直接指向window
所以实际上第三个和第四个可以写成这样:window.getName()
所以打印的是1
-
第五个答错的小伙伴们要注意了,
这里new 了一下Foo.getName()
但是仍然调用的是Foo.getName,不要被迷惑了
所以打印 2
-
第六个答错的小伙伴们要注意了
好好复习一下原型链的知识吧?
在这里实例化了Foo().getName()
实例化的方法会被分享=>去它的原型上找
所以这里打印3
-
最后一个总不会错的吧?这明明和上一个一样哦?
举个栗子:
new new new new new Foo().getName()
它无论new多少次都是new Foo().getName(),
当然就近原则,他会先new 最近的,从最后一个new 往后解析
但这不影响它的答案仍然是打印 3
三.JavaScript异步
-
同步和异步
-
同步:在执行一个请求或者调用功能的时候,没有得到结果是不会继续往下执行,
直到返回结果才会往下执行
缺点:会造成代码阻塞
-
异步:在发送一个请求的时候,不会按照顺序等待,可以继续执行其他代码,之后有信息返回在进行处理,可以提高效率
-
-
异步的应用场景
- 数据请求(ajax)
- DOM操作:例如:点击事件
- 定时器操作:setTimeout,setInterval
- and so on…
-
管理异步:
-
回调函数
栗子:
$.ajax({ //代码块 success:function(res) { $.ajax({ //代码块 success:function(res) { ..... } }) } })
缺点:容易导致地狱回调
-
时隔半个月才想起发布的Promise
-
为了解决地狱回调问题,提出Promise管理异步代码
栗子:
function request() { return new Promise((resolve,reject)=>{ if(判断条件) { resolve(返值) //用.then()接收 }else { reject(返值) //用.catch接收 } }) } //调用 request().then(res=>{ console.log(res) }).catch(error=>{ console.log(error) })
误区:
这里提醒小伙伴们,Promise不是异步,而是异步处理的一种管理方式,它更趋向同步
-
-
async/await
async/await:用同步方式管理异步代码
这两个关键词写到function关键词前面
栗子:
async function fn() { }
使用await必须配合async使用
栗子:
async function request() { await getList() }
new Promise 代码是同步的
await后面可以跟任意类型的值
.then是同步的,但是.then中的回调函数是异步的
-
下面我们试着做接下来一道异步面试题
async function async1(){ console.log('1') await async2() console.log('2') } async function async2() { console.log('3') } console.log('4') setTimeout(() => { console.log('5') }); async1() new Promise((resolve=>{ console.log('6') resolve() })).then(function () { console.log('7') }) console.log('8')
============================================
============================================
不要偷看答案!自己做
============================================
============================================
做完了再看答案!
============================================
============================================
-
运行结果:
4,1,3,6,8,7,2,5
解析:
-
首先打印4,这个毫无疑问吧?
-
其次async是微任务,宏任务和微任务
微任务先执行,所以打印1
-
再者,async await2(),这里的代码会推迟到后面执行,
因为这里有个await,它要等async2执行完之后推迟执行
async2开始执行,因为它是微任务所以执行打印3
-
在这里有些小伙伴说是不是应该打印2了?
在这里小伙伴们就走入了误区了,
我们仔细想想await这个任务并不是同步任务但是为什么会一直推迟呢?
await并不是同步,它只是使得代码能够继续按照顺序执行
所以,这里会一直推迟到宏任务开始之前
所以,根据宏任务和微任务,先打印 6
-
这里通过Promise打印resole里面的8之后
-
同时打印7
-
这时候我们仔细想想还有没有微任务执行,不要操之过急
这么一想,微任务还有一个 2 没有打印,宏任务在后面打印
所以先打印 2
-
最后执行宏任务打印 5,
到这里,有些小伙伴就有疑惑了:
这里延迟是不是等于0 啊那岂不是没有延迟就该打印?
其实不是的,就算写成:
setTimeout(() => { console.log('5') },0);
它也是最后打印的,表面上我们强制延迟0秒,
但是在运行的时候,会被强制改成至少延迟4ms,再者setTimeout是个宏任务,
微任务和宏任务运行时,先执行微任务
好啦,今天就分享到这里了,各位,晚安~~~