js考核重点内容

我把考核中的重点知识点总结如下

1.JavaScript中的数据类型有哪几种?并写出判断数据类型的方法

其中基本数据类型包括 Undefined、Null、Number、Boolean、String、Symbol (ES6 新增,表示独一无二的值),而引用数据类型统称为 Object 对象,主要包括对象、数组和函数

特殊:SymbolES6才出现的,它是一种基本数据类型。Symbol()函数会返回symbol类型的值。该类型具有静态属性和静态方法 。每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符。

例子:

	const level1 = Symbol("level");
	const level2 = Symbol("level");
	console.log(level1 === level2);
	const student = {
		name:"小明",
		age:12,
		[level1]:"优秀",
		[level2]:"有钱",
	}
	console.log(student);

即使输入相同的symbol值,所代表的变量也是不一样的

判断数据类型的方法:

(1).typeof

注意:null和数组进行typeof操作符后,结果是object,原因在于,null和数组被当做一个空对象引用。这也是typeof的缺线,不能够正确判断null和数组的类型

(2).instanceof

说到这个方法,那就不得不提到原型和原型链,可以用下面一张图来表示

在这里插入图片描述

在js中,函数也可以看做是一个对象,而在创建数组和null时都调用了其相对应的函数方法,所以上面的typeof出来的是一个对象,而这时的instanceof访问的是对象中的prototype,所以就能判断出来具体数据类型了

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,换句话说就是:instanceof用来测试一个实例对象在其原型链中是否存在一个构造函数的prototype属性,存在即为真true,不存在即为false。

原理是instanceof 是通过原型链判断的,A instanceof B, 在A的原型链中层层查找,是否有原型和类(构造函数)的一个属性即B.prototype相等,如果一直找到A的原型链的顶端(null即Object.prototype.proto),仍然不等于B.prototype,那么返回false,否则返回true。
(3).constructor方法:

constructor是prototype对象上的属性,指向构造函数

注意:除了undefined和null之外,其他类型都可以通过constructor属性来判断类型

(4).Object.prototype.toString 方法:
用来检测对象类型

所有数据类型的父类都是Object, toString为Object的原型prototype的方法,返回格式为[object xxx],其中Object对象返回的是[Object object],其他类型需通过call/apply来调用
Object.prototype.tostring.call();

使用Object.prototype.toString.call()的方式来判断一个变量的类型是最准确的方法。

(5).无敌万能的方法:jquery.type()
如果对象是undefined或null,则返回相应的“undefined”或“null”。

2.简述一下let,var,const的区别

(1)块级作用域 块作用域由 { }包括,let和const具有块级作用域,var不存在块级作用域。块级作用域解决了ES5中的两个问题:

  • 内层变量可能覆盖外层变量
  • 用来计数的循环变量泄露为全局变量

(2)变量提升: var存在变量提升,let和const不存在变量提升,即在变量只能在声明之后使用,否在会报错。

(3)给全局添加属性: 浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。

(4)重复声明: var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const和let不允许重复声明变量。

(5)暂时性死区: 在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区。

(6)初始值设置: 在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。

(7)指针指向: let和const都是ES6新增的用于创建变量的语法。 let创建的变量是可以更改指针指向(可以重新赋值)。但const声明的变量是不允许改变指针的指向。

var

全局作用域,假如如果if中设置了一个变量a,而在if外也有一个变量a,这时会引起冲突和问题,所以大多数时候不要用

let 和 const 的区别
1、let声明的变量可以重新赋值
2、const声明的变量不能重新赋值,而且声明变量时必须有初始值

3.闭包的概念

如果一个函数访问了此函数的父级及父级以上的作用域变量,那么这个函数就是一个闭包

闭包
1定义:在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。

function show(){
var name = “lisi”;
return function(){
return name;
}; //闭包 这个函数是个引用类型的对象,所以可以用一个变量接收一下 如①
}
var fun = show(); ①
fun(); //就是在调用紫色部分那个函数

或者:show()(); 这也是在调用紫色部分函数

这也是js中的匿名闭包

闭包的特点
1.被闭包函数访问的父级及以上的函数的局部变量(如范例中的局部变量 i )会一直存在于内存中,不会被JS的垃圾回收机制回收。

2.闭包函数实现了对其他函数内部变量的访问。(函数内部的变量对外是无法访问的,闭包通过这种变通的方法,实现了访问。)

Javascript的垃圾回收机制

如果一个对象不再被引用,那么这个对象就会被GC回收。
如果两个对象互相引用,而不再被第三者所引用,那么这两个对象都会被回收。
闭包的用途
访问函数内部的变量
让变量始终保持在内存中
闭包的应用场景
模拟面向对象的代码风格
比如模拟两人对话

function person(name) {
    function say(content) {
        console.log(name + ':' + content)
    }
    return say
}

a = person(“张三”)
b = person(“李四”)
a(“在干啥?”)
b(“没干啥。”)
a(“出去玩吗?”)
b(“去哪啊?”)
1
2
3
4
5
6
7
8
9
10
11
12
13
控制台打印结果为:

张三:在干啥?
李四:没干啥。
张三:出去玩吗?
李四:去哪啊?
1
2
3
4
使setTimeout支持传参
通过闭包实现setTimeout第一个函数传参(默认不支持传参)

function func(param){
    return function(){
        alert(param)
    }
}
var f1 = func(1);
setTimeout(f1,1000);

js中call,apply,bind三者的区别

1,共同点:

(1)可以改变函数体内this的指向。

2,区别:

(1)call,apply可以立即执;bind不会立即执行,因为bind返回的是一个函数,执行需要加括号()。

(2)参数不同,apply第二个参数是数组,call和bind有多个参数需要用逗号分开挨个写。

let callStr = “这是window的str”;

let callObj = {callStr: “这是obj对象的str”};

function callFun(name, age) {
this.name = name;
this.age = age;
console.log(this, this.callStr);
}

callFun.call(callObj);
callFun.apply(callObj);
callFun.bind(callObj)();
// 以上三条都输出: {callStr: “这是obj对象的str”, name: undefined, age: undefined} “这是obj对象的str”

callFun.call(callObj, “张三”, “23”);
callFun.apply(callObj, [“张三”, “23”]);
callFun.bind(callObj, “张三”, “23”)();
// 以上三条都输出 {callStr: “这是obj对象的str”, name: “张三”, age: 23} “这是obj对象的str”

垃圾回收机制

什么是垃圾?
(1)没有被引用的对象或变量

(2)无法访问到的对象(几个对象引用形成一个环,互相引用)

可达性
可达性是指那些以某种方式可以访问到或可以用到的值,它们被保证存储在内存中。

有一组基本的固有可达值,由于显而易见而无法删除:
(1)本地函数的局部变量和参数
(2)嵌套调用链上的其他函数的变量与参数
(3)全局变量
(4)还有一些其他的,内部的
这些值成为根。

如果引用或引用链可以从根访问任何其他值,则认为该值是可访问的。
垃圾回收机制
垃圾回收机制(GC:Garbage Collection):执行环境负责管理代码执行过程中使用的内存。JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,没有被释放,导致该内存无法被使用,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。

垃圾回收的必要性
字符串、对象和数组没有固定的大小,所以只有当它们大小已知时才能对它们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都要分配内存才存储这个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便它们能够被再次利用;否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
JavaScript有自己的垃圾回收机制(Garbage Collection),JavaScript的解释器可用检测到何时程序不再使用一个对象,当确定了一个对象无用的时候,就说明不再需要这个对象了,就可用把这个对象所占用的内存释放掉。

例如:

var a=‘use’
var b=‘over a’
var a=b //将a重写
1
2
3
代码执行以后,‘use’这个字符串就失去了引用(刚开始是被a引用的),系统检测到之后,就会启动垃圾回收机制,释放use字符串的存储空间,以便这些空间可用再被利用。

垃圾回收方式
JavaScript执行环境中的垃圾回收器怎样才能检测到哪块内存可以被回收呢?

通常有两种方式:标记清除(mark and sweep)、引用计数(reference counting)

标记清除
这是JavaScript中最常用的垃圾回收方式。

(1)当变量进入执行环境时(函数中声明变量),就标记这个变量为“进入环境”,当变量离开环境时(函数执行结束),则将其标记为“离开环境”,离开环境之后还有的变量则是需要被删除的变量。

(2)垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记。

(3)去掉环境中的变量以及被环境中变量引用的变量的标记。

(4)之后再被加上标记的变量即是需要回收的变量(因为环境中的变量已经无法访问到这些变量)

(5)最后,垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

引用计数
这种方式常常会引起内存泄漏,低版本的IE使用这种方式。机制就是跟踪一个值的引用次数,当声明一个变量并将一个引用类型赋值给该变量时该值引用次数加1,当这个变量指向其他一个时该值的引用次数便减一。当该值引用次数为0时,则说明没有办法再访问这个值了,被视为准备回收的对象,每当过一段时间开始垃圾回收的时候,就把被引用数为0的变量回收。引用计数方法可能导致循环引用,类似死锁,导致内存泄露。

例如:

function problem() {
    var objA = new Object();
    var objB = new Object();

    objA.someOtherObject = objB;
    objB.anotherObject = objA;

}
1
2
3
4
5
6
7

objA和objB相互引用,两个对象的引用次数都是2。函数执行完成之后,objA和objB还将会继续存在,因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。

补充:

常见内存泄漏的原因:

(1)全局变量引起的内存泄露
(2)闭包引起的内存泄露:慎用闭包
(3)dom清空或删除时,事件未清除导致的内存泄漏
(4)循环引用带来的内存泄露

4.作用域和this指向

1.3.全局作用域:

函数以外的作用域:所有在script标签里面的代码,都处在全局作用域中,全局作用域在页面打开时会创建全局对象GO对象,页面关闭时会销毁GO对象,找变量与函数

1.4.函数作用域:

函数以内的作用域:所有在函数里面的代码,都处在函数作用域中,函数作用域在函数执行时会创建AO对象,在函数结束时会销毁AO对象,当下一次执行函数时,会创建全新的AO对象

1.5.!注意:函数内不加var声明的变量就是全局变量、函数的参数:

也就是说未经声明的变量被赋值了,就归全局所有:

函数的参数,会默认定义为这个函数的局部变量:

 function fn(){
        a=1;
    }
    fn();
    console.log(a);  //1  这个a第一次赋值的时候,没有被var过,所以就自动的在全局的范围帮你var了一次

函数作用域

1、全局函数:

在全局作用域下创建的函数,可以在任意的作用域下访问到

2、局部函数:

在函数作用域下创建的函数,只能在当前函数作用域下访问到

3作用域链:

多个作用域嵌套构成的作用域结构,在寻找变量的时候,先到当前的作用域下寻找,如果当前的作用域没有就会不断往上一级作用域寻找

4、函数声明的提升:

程序执行前,会将函数提升到所在作用域的最前边,是整体提升:JS在执行前,会有一个预解析的过程,把所有的函数声明提升到了最前面,然后在执行第一行语句。

//先调用
fun();  //函数,因为函数有函数声明头提升的特性
//然后定义
function fun(){
    console.log("函数");
}
123456

函数声明会被提升,但是函数表达式不会被提升:函数表达式提升的是变量,变量提升后面并不是一个函数,所以在表达式之前执行,会报错,为类型错误,因为不是函数

fn();
    var fn=function fn(){
        console.log('函数');  //报错,函数表达式不会有提升
    }
1234

函数最先提升,然后变量提升,如果函数名称和变量名称相同,函数最先提升

 /*
    GO{
        c:undefind,函数声明提升赋值f c(){},2再赋值给c
    }
    */
    var c=2;
    function c(){
        console.log(c);
    }
    c(); //报错 c is not a function,c是2

JS中this的指向

this永远指向一个对象

this的指向完全取决于函数调用的位置

JavaScript支持运行环境动态切换,this的指向是动态的

全局上下文(Global Context)
在全局执行环境中(在任何函数体外部),this都是指向全局对象,在浏览器中,window对象即是全局对象。

// 在浏览器中,window 对象同时也是全局对象:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = “MDN”;
console.log(window.b) // “MDN”
console.log(b) // “MDN”
console.log(this.b) // “MDN”
1
2
3
4
5
6
7
8
9
10

函数上下文(Function Context)
在函数内部,this的值取决于函数的调用方式。这包括普通函数调用、作为对象的方法调用、构造函数调用、以及通过call、apply或bind方法调用。

普通函数调用
如果一个函数不是作为对象的方法被调用,而是作为普通函数被调用,那么 this 通常指向全局对象(在严格模式下,this 是 undefined)

var name = ‘window’;
var doSth = function(){
console.log(this.name);
}
doSth(); // ‘window’
1
2
3
4
5
let 不给顶层对象添加属性(浏览器为Window)

let name2 = ‘window2’;
let doSth2 = function(){
console.log(this === window);
console.log(this.name2);
}
doSth2() // true, undefined
1
2
3
4
5
6
严格模式下,正常函数中的 this 行为不同,如未定义输出undefined

‘use strict’
var name3 = ‘window3’;
var doSth3 = function(){
console.log(typeof this === ‘undefined’);
console.log(this.name3);
}
doSth3();

window.doSth3();
1
2
3
4
5
6
7
8
9

作为对象的方法调用
如果一个函数作为对象的方法被调用,this 就指向这个对象

var name = ‘window’;
var doSth = function(){
console.log(this.name);
}
var student = {
name: ‘dog’,
doSth: doSth,
other: {
name: ‘other’,
doSth: doSth,
}
}

student.doSth(); // ‘dog’
// call like this
student.doSth.call(student);

student.other.doSth(); // ‘other’
// call like this
student.other.doSth.call(student.other);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

将对象中的函数分配给变量,实际上又是一个普通函数,所以使用普通函数的规则(默认绑定)。

var studentDoSth = student.doSth;
studentDoSth(); // ‘window’
// call like this :
studentDoSth.call(undefined);
1
2
3
4

当 o.f() 被调用时,函数内的 this 将绑定到 o 对象

var o = {
prop: 37,
f: function() {
return this.prop;
}
};

console.log(o.f()); // 37
1
2
3
4
5
6
7
8
也可以先定义函数,然后再将其附属到o.f,这样做的结果是一样的

var o = {prop: 37};

function independent() {
return this.prop;
}

o.f = independent;

console.log(o.f()); // 37
1
2
3
4
5
6
7
8
9
原型链中的 this
对于在对象原型链上某处定义的方法,同样的概念也适用。如果该方法存在于一个对象的原型链上,那么 this 指向的是调用这个方法的对象

var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5
1
2
3
4
5
6
7
8
9
10
构造函数调用
当一个函数用作构造函数时(使用new关键字),this 指向新创建的对象。

function Foo() {
this.name = ‘Test’;
console.log(this);
}

// 使用new关键字调用
var obj = new Foo(); // this指向新创建的obj对象
console.log(obj.name); // 输出 ‘Test’

// 不使用new关键字调用
Foo(); // this指向全局对象(非严格模式)或undefined(严格模式)
console.log(window.name); // 在非严格模式下,输出 ‘Test’
1
2
3
4
5
6
7
8
9
10
11
12

如果构造函数没有返回对象(也就是没有返回值或者返回非对象),那么new表达式的结果就是新创建并且被this关键字指向的对象。
如果构造函数返回一个对象,那么new表达式的结果就是这个返回的对象。
function C(){
this.a = 37;
}

var o = new C();
console.log(o.a); // logs 37

function C2(){
this.a = 37;
return {a:38};
}

o = new C2();
console.log(o.a); // logs 38
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

箭头函数
箭头函数上下文(Arrow Function Context)
箭头函数不绑定this,它会捕获其所在(即定义的位置)的上下文的this值作为自己的this值。

this 被设置为它被创建时的环境

var name = ‘window’;
var student = {
name: ‘dog’,
doSth: function(){
// var self = this;
var arrowDoSth = () => {
// console.log(self.name);
console.log(this.name);
}
arrowDoSth();
},
arrowDoSth2: () => {
console.log(this.name);
}
}
student.doSth(); // ‘dog’
student.arrowDoSth2(); // ‘window’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

不能通过call、apply、bind来绑定箭头函数的this(它本身没有this)

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

// 作为对象的一个方法调用
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// 尝试使用 call 来设定 this
console.log(foo.call(obj) === globalObject); // true

// 尝试使用 bind 来设定 this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
call、apply、bind可以绑定缓存箭头函数上面的普通函数的this

var student = {
name: ‘dog’,
doSth: function(){
console.log(this.name);
return () => {
console.log(‘arrowFn:’, this.name);
}
}
}
var person = {
name: ‘person’,
}
student.doSth().call(person); // ‘dog’ ‘arrowFn:’ ‘dog’
student.doSth.call(person)(); // ‘person’ ‘arrowFn:’ ‘person’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
回调函数
回调函数中 this 的指向,决定于执行回调函数 时的执行上下文环境

(function(){
console.log(this); // window
})();

setTimeout(() => {
console.log(this); // window
}, 0);

setTimeout(function(){
console.log(this); // window
}, 0);
1
2
3
4
5
6
7
8
9
10
11
组合使用

第一个setTimeout,执行obj.getage 之后,相当于setTimeout的回调是一个匿名函数,执行的时候,函数内部未设置this的指向。相当于是普通函数调用。所以this默认指向window,所以结果是undefined。

第二个setTimeout,传给setTimeout的也是一个匿名回调函数,执行匿名函数,执行到 obj.getage() 的时候,getage函数里的this,指向的就是obj了,所以能打印出10。遵循 谁调用产生 this指针的函数,this就指向谁的规则

var obj = {
age:10,
getage:function(){
console.log(this.age)
}
}

setTimeout(obj.getage,1000) // undefined

setTimeout(function(){
obj.getage() // 10
},1000)
1
2
3
4
5
6
7
8
9
10
11
12
let obj={
a:222,
fn:function(){
setTimeout(()=>{console.log(this.a)});
}
};
obj.fn(); // 222
1
2
3
4
5
6
7
var name = ‘window’;
var A = {
name: ‘A’,
sayHello: () => {
console.log(this.name)
}
}

A.sayHello(); // 输出的是window,根据刚才讲的规则就可以判断

// 那如何改造成永远绑定A呢:

var name = ‘window’;
var A = {
name: ‘A’,
sayHello: function(){
var s = () => console.log(this.name)
return s//返回箭头函数s
}
}

var sayHello = A.sayHello();
sayHello();// 输出A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let obj = {
value: ‘Hello, World’,
print: function() {
setTimeout(function() {
console.log(this.value);
}, 1000);
}
};

obj.print(); // 输出:undefined(严格模式)或者一个全局value(非严格模式)

let obj = {
value: ‘Hello, World’,
print: function() {
setTimeout(() => {
console.log(this.value); // 输出:Hello, World
}, 1000);
}
};

// 或者

let obj = {
value: ‘Hello, World’,
print: function() {
setTimeout(function() {
console.log(this.value);
}.bind(this), 1000);
}
};

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
事件处理器上下文(Event Handler Context)
在DOM事件处理器中,this通常指向触发事件的元素。

在事件处理函数(或者说事件监听器)中,this通常指向触发事件的 DOM 元素。第一个事件监听器中,this打印出来的是按钮元素本身。

bluify函数作为事件处理函数使用,因此在这个函数中,this指向触发点击事件的按钮元素。

let btn = document.getElementById(‘btn’);

btn.addEventListener(‘click’, function () {
console.log(this); // button
});

btn.addEventListener(‘click’, bluify, false);

function bluify(e) {
console.log(this === e.currentTarget); // 总是 true

// 当 currentTarget 和 target 是同一个对象时为 true
console.log(this === e.target);
this.style.backgroundColor = ‘#A5D9F3’;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
通过 call,apply,bind 改变this指向
call(a, b, c方法接收三个参数,第一个是this指向,第二个,三个是传递给函数的实参,可以是数字,字符串,数组等类型的数据类型都可以

apply(a, [b])和call基本上一致,唯一区别在于传参方式,apply把需要传递给fn()的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn()一个个的传递

bind(a, b, c:语法和call一模一样,区别在于立即执行还是等待执行

fn.bind(第一个参数是this的指向)
fn.call(第一个参数是this的指向,parma2,param3…)
fn.apply(第一个参数是this的指向,[parma2,parma3…])
1
2
3
//call()方法:改变fn中的this,并且把fn立即执行
fn.call(obj, 1, 2);
//bind()方法:改变fn中的this,fn并不执行
fn.bind(obj, 1, 2);
1
2
3
4
var obj = {
name:‘111’,
getName:function(){
console.log(this.name)
}
};

var otherObj = {
name:‘222’,
};

var name = ‘333’;

obj.getName(); // 111
obj.getName.call(); // 333
obj.getName.call(otherObj); // 222
obj.getName.apply(); // 333
obj.getName.apply(otherObj); // 222
obj.getName.bind(this)(); // 333
obj.getName.bind(otherObj)();// 222

5.js执行机制

一. 什么是同步与异步
(1) 同步
按顺序执行

//例子:
console.log(1)
console.log(2)
console.log(3)
1 2 3
1
2
3
4
5
(2) 异步
先执行一部分,然后等待结果,等拿到结果之后,再执行另一部分
1.定时器
2.ajax
3.读取文件

1000 100 10 3 2 1
解释:同步程序完成后,后执行异步程序

//例子1:
console.log(1)
setTiemout(function(){console.log(2)},0)
setTiemout(function(){console.log(3)},0)
setTiemout(function(){console.log(4)},0)
console.log(5)

//打印出 1 5 2 3 4
1
2
3
4
5
6
7
8
//例子2:
setTiemout(function(){console.log(1)},1000)
setTiemout(function(){console.log(2)},100)
setTiemout(function(){console.log(3)},10)

//打印出 321
1
2
3
4
5
6
二. 单线程
(1) js是单线程
一个任务完成之后,才能执行另外一个任务

不管定时器写几,也是在同步的代码完成之后,再执行异步的代码

for(var i = 0;i<2000;i++){
console.log(1)
}
setTiemout(function(){console.log(2)},0)
setTiemout(function(){console.log(3)},0)
setTiemout(function(){console.log(4)},0)
console.log(5)

打印出 先输出2000个1,5 2 3 4
执行的时间很长
1
2
3
4
5
6
7
8
9
10
三. process.nextTick与setImmediate方法
(1) process.nextTick
process.nextTick方法只能在node中运行,在同步代码执行完成之后,异步代码执行之前完成的

//例子:
process.nextTict(()=>{
console.log(1)
})
console.log(2)
setTiemout(function(){console.log(3)},0)
console.log(4)

执行顺序:2 4 1 3
1
2
3
4
5
6
7
8
9
(2) setImmediate
异步代码执行之后,执行setImmediate

//例子:
setImmediate(()=>{
console.log(1)
})
process.nextTict(()=>{
console.log(2)
})
console.log(3)
setTiemout(function(){console.log(4)},0)
console.log(5)

执行顺序:3 5 2 4 1
1
2
3
4
5
6
7
8
9
10
11
12
四.事件循环
(1) 运行栈
同步的代表会放到运行栈中执行

(2) 任务队列
异步

(3) 事件循环
检测任务队列里面有没有东西,如果有一个任务,他就执行,如果有多个任务,他就会按顺序执行

注解:
定时器不是到点执行,而是到点了之后插到任务队列里头,而任务队列什么时候执行,那需要看运行栈里面是否执行完,运行栈执行完了,要看任务队列里前面有没有任务,有任务的话,需要执行完,才执行刚刚插入的定时器里头的任务,所以为什么定时器为0还没执行他的原因

//例子
setImmediate(()=>{
console.log(1)
})
process.nextTict(()=>{
console.log(2)
})
console.log(3)
setTiemout(function(){console.log(4)},0)
setTiemout(function(){console.log(5)},1000)
setTiemout(function(){console.log(6)},0)
console.log(7)
执行顺序:
1.同步
2.nextTick
3.异步
4.setImmediate(当前事件循环结束执行)
每次事件循环都看任务队列里面有没有东西,有就执行

执行顺序:3 7 2 4 6 1 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
执行顺序:
1.同步
2.process.nextTick
3.异步
4.setImmediate(当前事件循环结束执行)
每次事件循环都看任务队列里面有没有东西,有就执行

五.宏任务和微任务
(1) 宏任务
计时器,ajax,读取文件

(2) 微任务
promise.then
1
执行顺序:
1.同步程序
2.process.nextTick
3.微任务
4.宏任务
5.setImmediate

//题目:
setImmediate(()=>{
console.log(1)
})
console.log(2)
setTiemout(function(){console.log(3)},0)
setTiemout(function(){console.log(4)},100)
console.log(5)
new Promise((resolve)=>{
console.log(6)//promise.then的这个6是同步代码
resolve()
}).then(()=>{{
console.log(7)
})
process.nextTict(()=>{
console.log(8)
})

//执行顺序:2 5 6 8 7 3 1 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
六. promise和async函数
(1) promise函数
promise的基本概念:then的用法就是通过resolve传出来的 resolve传出来的值,是then里面的形参

以下案例解:就这样运行,他不会打印出2,因为new Promise函数里面会有一个resolve,只有调用了resolve才会执行then

let p = new Promise(()=>{
console.log(1)//同步
})

p是promise对象,promise对象又一个then方法

p.then(()=>{
console.log(2)//异步
})

1
2
3
4
5
6
7
8
9
10
修改上面案例

let p = new Promise((resolve)=>{
console.log(1)//同步
resolve(‘hello word’)
})

p是promise对象,promise对象又有一个then方法

p.then((data)=>{
console.log(data)//异步
})
//执行:打印出 1 hello word
1
2
3
4
5
6
7
8
9
10
11
//例子:
ajax.get(‘’).then((res)=>{
//其实get方法他的返回值就是一个promise对象
})

ajax.get(‘’)是怎么封装的,他会把一个获取到的远程数据通过resolve方法传出来,然后才能调用then()拿到这个数据
promise的基本概念:then的用法就是通过resolve传出来的
resolve传出来的值,是then里面的形参
1
2
3
4
5
6
7
8
(2) async函数
async函数调用之后,他的的返回值是promise对象, promise对象的then传过来的参数,就是return的值
可以理解为,async函数就是promise对象的简写

async function fun(){
return 1
}
let a = fun()//fun是promise对象,要想拿到这个返回值1,则需要使用then方法
console.log(a)//打印出 Promise {: 1}

a.then((data)=>{
console.log(data)//执行后,打印出1
})

async函数调用之后,他的的返回值是promise对象,promise对象的then传过来的参数,就是return的值
可以理解为,async函数就是promise对象的简写
1
2
3
4
5
6
7
8
9
10
11
12
async function fun(){
return 1
}
这个写法可以换成

function fun(){
return new Promise((resolve)=>{
resolve(1)
})
}
fun.then((data)=>{
console.log(data)//执行后打印出 1
})

方法是一样的,只是async函数看上去会顺畅很多,代码不会写的太多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
还可以将方法换成:
let p1 = new Promise((resolve)=>{
resolve(1)
})
let p2 = new Promise((resolve)=>{
resolve(2)
})

//获取到1,2的简写
async function fun(){
let a = await p1;//a相当于 await 后面加一个promise对象.await+promise对象他就可以直接拿到resolve的值了,这就是async的用法,
let b = await p2;
console.log(a)//打印出1
console.log(b)//打印出2

}

async函数里面可以加await,await后面可以加promise对象,然后就可以让异步的代码,写起来更像同步的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
题目:
async function fun1(){
let data = await fun2()
console.log(data)//异步
}
async function fun2(){
console.log(200)//同步的代码
return 100
}
fun1()//打印出 200 100

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值