# JS 基础和核心问题

# 判断数据类型

  1. typeof 用于判断基本数据类型,引用数据类型和 null 都判为 object
  2. instanceof 判断实例对象的 prototype 如 a instanceof A 判断 a 是否由 A new 产生
  3. constructor 检测不出 undefined 和 null; 使用方法 str.constructor == String ; true
  4. toString.call (待测值) toString 是 Object 原型对象的一个方法,根据原型链往上找 如函数 fn --> fn._proto_(Fn.prototype) --> Fn.prototype._proto_ (Object.prototype)
  5. jQuery 内置的方法

image-20221113214240160

# 浅拷贝 vs 深拷贝

浅拷贝只拷贝一层,实际上指向同一个地址;

1
let newArr = [...oldArr]

深拷贝拷贝全部,将数组内的对象也拷贝,开辟了新地址;

// 四种深拷贝
1. 递归
2.Object.create()
3.jQuery 的 $.extend
4. 导入 lodash 使用 cloneDeep

# 递归实现深拷贝

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
//递归实现深拷贝

const oldObj={
name: 'jilin',
hobby: ['game','sleep','eat'],
skill:{
name: 'LOL',
rank: 'Gold II'
}
}
//let newObj = oldObj;
//oldObj.name = '123' 浅拷贝 修改原对象的值,新对象也变了
//console.log(newObj);
//********以下为深拷贝***********
function deepCopy(object) {
let newObj = null;
if (object instanceof Object) {
newObj = {}; //检测到为Object类型
for (let key in object) {
newObj[key] = deepCopy(object[key]);
}
} else if (object instanceof Array) {
newObj = []; //检测到为Array类型
object.forEach((item) => {
newObj = deepCopy(item);
});
} else {
newObj = object;
}
return newObj;
}

let myNewObj = deepCopy(oldObj);
console.log(myNewObj);
/*
{
name: 'jilin',
hobby: { '0': 'game', '1': 'sleep', '2': 'eat' },
skill: { name: 'LOL', rank: 'Gold II' }
}
*/

# 闭包

在函数内调用函数外部的变量,创建私有变量

  • 手写一个闭包,能记录函数调用的次数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function Fn(){
    let i = 0;
    return ()=>{
    i++;
    console.log(`调用了${i}次`)
    }
    }
    const f = new Fn()

    f(); //> "调用了1次"
    f(); //> "调用了2次"
    f(); //> "调用了3次"

    f = null //利用标记清除 回收内存

原理: 函数在作用找不到变量,顺着父级作用域往上找。 函数作用域只与创建时的位置有关,和调用时无关,如:

1
2
3
4
5
6
7
8
9
10
let a = "全局变量a"
fn1=()=>{
console.log(a)
}
fn2=()=>{
let a = "fn2中的a"
fn1() //虽然调用位置在这里 但是创建的作用域
}

fn2(); //"全局变量a"

# 完全理解 call (),apply (),bind ()

三者都用于改变函数传参和 this 指向,注意 箭头函数 无法改变 this

fn.call('新的this对象','参数1','参数2','参数3'..)
fn.apply(''新的this对象',['参数1','参数2','参数3'])
fn.bind('新的this对象','参数1','参数2','参数3'..)() //bind返回的是一个新的函数,传参成为固定参数

1
2
3
4
5
6
7
8
9
10
function oldFn(a,b){
let sum = a+b
console.log(sum,this)
}
const obj={ name:'对象' }
oldFn(2,3)
oldFn.call(obj,2,2) //this指向obj 传参2,2
oldFn.apply(obj,[1,3]) //this指向obj 传参1,3
const bindFn=oldFn.bind(obj,0,4) //this指向obj 绑定参数为0和4
bindFn() //相当于执行了o

# 暂时性死区

一句话:一进入当前作用域,变量就已经存在了,但不可获取,只有等到声明后才能获取和使用,作用是防止在变量声明前就使用,减少运行时的错误

var 存在变量提升,所以声明前访问,返回 undefined 而不报错

1
2
3
4
5
6
7
8
9
10
function fn1(){
console.log(a)
var a = 1 //变量提升
}
function fn2(){
console.log(b) //
let b = 1
}
fn1() //undefined
fn2() //ReferenceError

# JavaScript 执行机制 —— 事件循环

# 异步?

JavaScript 是一门单线程语言,没有异步,一切异步都是以同步的方法模拟的!

# 事件循环 Event Loop

​   event loop 它最主要是分三部分:主线程、宏队列(macrotask)、微队列(microtask)

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
console.log('1');

setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})

setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
// 1 7 6 8 | 2 4 3 5 | 9 11 10 12
第一轮事件循环 第二轮 第三轮

** 宏任务(Macrotasks):**js 同步执行的代码块,setTimeout、setInterval、XMLHttprequest、setImmediate、I/O、UI rendering 等。

** 微任务(Microtasks):**promise、process.nextTick(node 环境)、Object.observe, MutationObserver 等。

浏览器在执行 js 代码过程中会维护一个执行栈,每个方法都会进栈执行之后然后出栈(FIFO)。与此同时,浏览器又维护了一个消息队列,所有的异步方法,在执行结束后都会将回调方法塞入消息队列中,当所有执行栈中的任务全部执行完毕后,浏览器开始往消息队列寻找任务,先进入消息队列的任务先执行。

执行完一次宏任务后,检查任务队列微任务,执行完后进入下一次宏任务执行

宏任务和微任务的总结: 宏任务 Macrotasks 就是参与了事件循环的异步任务。 微任务 Microtasks 就是没有参与事件循环的 “异步” 任务。

事件循环

# Node 环境的 JS 执行机制

​ node 环境下 js 事件循环机制与 浏览器环境下有出入,不过随着 node 版本的更新,正在不断规范。

nod 执行栈 -> 宏任务 -> 宏任务 -> 微任务 -> 微任务

浏览器环境:执行栈 -> 宏任务 -> 微任务 -> 宏任务 -> 微任务