
一、变量与类型
1、let/const/var 区别(作用域、提升、重复声明)

举个例子,
// 作用域差异
if (true) { var a = 1;
// 函数作用域,外部可访问
let b = 2;
// 块级作用域,外部不可访问
const c = 3;
// 块级作用域,外部不可访问
}
console.log(a);
// 1(正常)
console.log(b);
// ReferenceError(b未定义)
// 变量提升差异
console.log(x);
// undefined(var提升并初始化)
var x = 10;
console.log(y);
// ReferenceError(let的暂时性死区)
let y = 20;
// 重复声明差异
var m = 1;
var m = 2;
// 允许,m变为2
let n = 1;let n = 2;
// 报错:Identifier 'n' has already been declared(1)分类与区别
2、数据类型(基本类型 vs 引用类型 + 判断方法)
js中的数据类型分为基础类型和应用类型,具体如下:
String、Number、Boolean、Null、Undefined、Symbol(唯一且不可变)、BigInt(大整数,如123n)。Object(对象)、Array(数组)、Function(函数)、Date等3、类型转换(显式 / 隐式转换规则,如 == 与 === 差异);
这里主要涉及到的就是类型转换
(1)显式转换(需要手动触发)
Number(value)、parseInt(value)、parseFloat(value)
(空字符串""转0,"123a"用Number转NaN,parseInt转123)String(value)、value.toString()(null和undefined不能用.toString())Boolean(value)( falsy 值:0、""、null、undefined、NaN转false,其余转true)(2)隐式转换(一般是自动触发)
+、-等):非数字会转为数字("3" + 4 → "34"(+ 遇字符串优先拼接);"3" - 4 → -1)if、&&、||):自动转为布尔值===);null == undefined → true(特殊规则);valueOf()或toString())。(3)== 与 === 的区别
===(严格相等):不转换类型,类型不同直接返回false;==(宽松相等):会进行类型转换,转换后值相等则返回true。
二、函数与作用域
4、闭包(定义 + 应用场景 + 内存泄漏问题)
1)、什么是闭包
所谓闭包,就是在嵌套函数中,内部函数引用了外部函数的变量,且内部函数被外部作用域访问时形成的环境。此时外部函数的变量不会被垃圾回收,始终被内部函数持有。
来看下面这段代码:
function outer()
{
let count = 0;
// 外部函数变量
return function inner()
{
// 内部函数引用count
count++;
return count;
}
;
}
const fn = outer();
console.log(fn());
// 1(count被保留)
console.log(fn());
// 2(count未被回收)2)、闭包会导致什么问题?
主要是内存泄漏。
因为外部函数不知道内部函数什么时候结束对这个变量的引用,导致这些变量不再使用但是空间并没有释放,就会导致内存无法回收,这里具体的解决办法是使用完变量后手动将其设为null。
3)、闭包有哪些应用场景?
主要有四个:
a.数据私有化:隐藏变量,仅通过暴露的方法操作(如计数器、缓存)
b.防抖/节流:保存定时器变量,控制函数执行频率;
c.柯里化:将多参数函数转为单参数函数序列(如add(1)(2)(3));
d.模块化:早期无 ES6 模块时,用闭包隔离作用域(如 IIFE)。
5、this 指向(普通函数 / 箭头函数 / 对象方法 / 构造函数中 this 的区别)
请看下面的表格:

简单总结一下就是:除了箭头函数外,谁调用这个函数,它的this就指向谁。
箭头函数的this是由定义时的外层作用域来决定的。
6、作用域链与变量提升的作用;
js中的作用域分为:全局作用域,函数作用域和块级作用域。
作用域链:当访问变量时,JS 引擎会先在当前作用域查找,若找不到则向上级作用域查找,直到全局作用域(若仍找不到则为undefined)
可以看一下下面这段代码:

2)、变量提升
var声明的变量和function声明的函数 “提升” 到作用域顶部(仅声明,赋值留在原地)。var:提升后初始化为undefined;function:提升后可直接调用(整个函数体被提升);let/const:存在 “提升” 但未初始化,访问会触发 “暂时性死区”(报错)。基础语法:
1、原型与原型链(构造函数、prototype、__proto__关系)
(1)核心概念
function Person() {});prototype(即实例.__proto__ === 构造函数.prototype);Person.prototype.constructor === Person)。
(2)原型链
__proto__查找原型对象;若仍找不到,继续通过原型对象的__proto__查找(即原型的原型),直到null(原型链终点)。2、继承方式
在js中,常见的继承方式有三种,分别是:原型链继承、构造函数继承、组合继承优缺点。
这三种继承方式都有各自的优缺点:下面我们来一个一个看。
(1)原型链继承
Child.prototype = new Parent()),使子类实例能访问父类原型的方法。(2)构造函数继承
Parent.call(this, 参数)调用父类构造函数,复制父类实例属性到子类。(3)组合继承(最常用)

new Parent()和Parent.call()),造成冗余。(4)寄生组合继承(优化组合继承)
new Parent()调用父类构造函数)。好啦,本期文章就到这里,感谢大家的阅读,我们下期再见~