前端八股文JS基础篇上

前端八股文JS基础篇上

一、JS的数据类型+区别

1. 基本数据类型

  • Number
  • String
  • Boolean
  • Null
  • Undefined
  • Symbol(ES6新增,代表唯一标识符)
  • BigInt(ES10新增,用于处理高精度整数)

特性

  • 直接存储在栈内存中,占用固定大小的内存空间。
  • 赋值时是值的复制,修改一个变量不会影响另一个变量。

2. 引用数据类型

  • Object(包括普通对象、数组、函数、日期等)
  • Array(本质是Object的子类型)
  • Date
  • Function(特殊的对象,可被调用)
  • RegExp(正则表达式对象)

特性

  • 栈内存存储对象的引用地址,堆内存存储对象的实际数据。
  • 赋值时是引用的复制,多个变量指向同一对象,修改会相互影响。

3. 核心区别

维度基本数据类型引用数据类型
存储位置栈内存栈内存存地址,堆内存存数据
赋值/传递行为值复制引用复制(指向同一堆内存数据)
比较方式值比较(===判断值是否相等)引用比较(===判断地址是否相同)
内存占用固定大小动态大小(由对象属性数量决定)

二、判断数据类型的6种方法

1. typeof 操作符

返回值'number''string''boolean''object''function''undefined''symbol''bigint'
局限性

  • typeof null 返回 'object'(是历史遗留问题)。
  • 无法区分具体的引用类型(如数组、普通对象均返回'object')。
  • 对基本类型的包装对象(如new String('a'))返回'object'
console.log(typeof NaN); // 'number'(特殊数值)
console.log(typeof []); // 'object'

2. instanceof 运算符

原理:检查对象的原型链是否包含构造函数的prototype
注意

  • 基本数据类型需转换为包装对象才能使用(如(123).instanceof Number)。
  • 跨框架/窗口环境可能失效(不同窗口的构造函数不同)。
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true

3. Array.isArray() 方法

优势:跨环境可靠,专门用于判断数组。

console.log(Array.isArray([1, 2])); // true

4. Object.prototype.toString.call()

最通用的方法,返回格式为[object Type]的字符串,可精确区分:

  • 基本类型(包括NullUndefined
  • 引用类型(数组、函数、日期、正则等)
  • 包装对象(如new String与普通字符串均返回[object String],需额外逻辑区分)
console.log(Object.prototype.toString.call(null)); // '[object Null]'
console.log(Object.prototype.toString.call(/\d/)); // '[object RegExp]'

缺点补充

  • 自定义类实例:若未定义Symbol.toStringTag,统一返回[object Object]
  • 宿主对象:浏览器环境的windowdocument等可能返回非标准标签(如[object Window])。
  • 包装对象混淆new String('a')与普通字符串'a'的标签相同,需结合typeof进一步判断。

5. constructor 属性

原理:对象的constructor指向其构造函数(如[].constructor === Array)。
风险

  • 构造函数的原型可能被修改(如Array.prototype = {}会导致constructor指向错误)。
  • 基本数据类型的包装对象(如new Number(1))的constructor可被篡改。

6. Symbol.toStringTag 自定义标签

用法:通过在对象/类中定义Symbol.toStringTag属性,自定义toString.call()的返回值。

class MyClass {
  [Symbol.toStringTag] = 'MyClass';
}
console.log(Object.prototype.toString.call(new MyClass())); // '[object MyClass]'

三、作用域与作用域链

1. 作用域分类

  • 全局作用域:代码最外层的作用域,全局变量/函数在此定义。
  • 函数作用域(ES5及之前):函数内部形成独立作用域,var声明的变量在此作用域内有效。
  • 块级作用域(ES6+):let/const{}块内形成作用域(如iffor块)。

2. 作用域链

  • 查找规则:当访问变量/函数时,从当前作用域逐级向上查找,直到全局作用域(若未找到则报错ReferenceError)。
  • 示例
    const a = 1;
    function fn() {
      const b = 2;
      function inner() {
        const c = 3;
        console.log(a); // 查找顺序:inner作用域 → fn作用域 → 全局作用域
      }
    }
    

四、闭包

1. 核心概念

  • 定义:函数内部返回一个函数,使得内部变量能被外部访问,形成“私有作用域”。
  • 形成条件
    1. 函数嵌套;
    2. 内部函数引用外部函数的变量;
    3. 外部函数返回内部函数。

2. 核心作用

  • 数据保护:避免全局变量污染(如模块模式(function(){})())。
  • 状态保存
    function counter() {
      let count = 0;
      return function() { count++; console.log(count); };
    }
    const add = counter();
    add(); // 1,闭包保存了count的状态
    
  • 解决循环变量问题
    for (let i = 0; i < 3; i++) { // 块级作用域,每次循环创建独立的i
      setTimeout(() => console.log(i), 0); // 输出 0, 1, 2
    }
    // 若用var,需通过闭包包裹:
    for (var i = 0; i < 3; i++) {
      (function(j) {
        setTimeout(() => console.log(j), 0); // 闭包保存j的值
      })(i);
    }
    

3. 缺点与内存管理

  • 内存泄露风险:闭包长期持有变量引用,若不再使用需手动释放(如设置为null)。
  • 性能影响:过度使用闭包会增加内存开销,需合理设计。

五、JS获取DOM元素

1. 传统方法

  • getElementById(id):返回单个元素(唯一,效率最高)。
  • getElementsByClassName(className):返回HTMLCollection(动态集合,实时更新)。
  • getElementsByTagName(tagName):返回HTMLCollection(匹配所有子元素)。

2. CSS选择器方法

  • querySelector(selector):返回第一个匹配的元素(静态,返回Element)。
  • querySelectorAll(selector):返回所有匹配的元素(静态,返回NodeList,可遍历)。

注意

  • 传统方法返回的HTMLCollection是动态的,修改DOM会实时更新;
  • querySelectorAll返回静态NodeList,性能更优(尤其在多次遍历时)。

六、原型与原型链

1. 核心概念

  • 显式原型(prototype:每个函数都有prototype属性,指向原型对象(默认包含constructor属性)。
  • 隐式原型(__proto__:每个对象(除了null)都有__proto__,指向其构造函数的prototype
  • 原型链:对象查找属性时,若自身不存在,则沿__proto__向上查找,直到Object.prototype(其__proto__null)。

2. 关键关系

const obj = {};
obj.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true

const arr = [];
arr.__proto__ === Array.prototype; // true
Array.prototype.__proto__ === Object.prototype; // true

七、callapplybind 的区别

方法参数形式执行时机返回值特性
call逐个参数(thisArg, arg1, arg2...立即执行函数执行结果适合动态传参
apply数组/类数组(thisArg, [argsArray]立即执行函数执行结果适合处理数组参数
bind逐个参数(同call返回新函数,需手动调用新函数(绑定this用于预先绑定上下文,延迟执行

示例

function fn(a, b) { console.log(this.name, a, b); }
const obj = { name: 'obj' };
fn.call(obj, 1, 2); // 立即执行,输出 'obj 1 2'
fn.apply(obj, [1, 2]); // 同上
const boundFn = fn.bind(obj, 1, 2); // 返回新函数
boundFn(); // 手动调用,输出 'obj 1 2'

八、继承方式对比

1. 常见继承方案

方式核心实现优点缺点
原型继承Sub.prototype = new Super()简单,共享父类方法无法向父类构造函数传参,引用属性共享
构造函数继承Super.call(this, args)可传参,避免引用属性共享无法继承父类原型方法
组合继承结合前两者兼顾传参与原型方法继承父类构造函数被调用两次,性能损耗
寄生组合继承优化组合继承,避免重复调用父类高效,接近完美代码稍复杂
ES6 extendsclass Sub extends Super语法简洁,内置super机制依赖ES6环境

2. ES6继承关键点

  • 子类constructor中必须调用super()才能使用this
  • super可用于调用父类方法(如super.method())。

九、内存泄露与垃圾回收

1. 内存泄露场景

  • 意外的全局变量:未声明直接赋值(如a = 1,隐式创建全局变量)。
  • 未清除的定时器/回调setInterval未调用clearInterval
  • DOM引用未释放:内存中保存DOM元素引用,但DOM已从页面移除。
  • 闭包过度使用:闭包长期持有不必要的变量引用。

2. 垃圾回收机制

  • 标记清除算法(现代浏览器主流):
    1. 标记活动对象(被引用的对象);
    2. 清除未标记的对象(释放内存)。
  • 引用计数算法(早期,存在循环引用问题):
    变量引用计数为0时释放内存,但a.b = b.a = {}会导致循环引用无法回收(需手动置空a.b = b.a = null)。

十、变量提升与暂时性死区

1. 提升规则

声明方式提升内容初始化时机能否重复声明
var声明+初始化(undefined进入作用域时允许
let/const仅声明(无初始化)代码执行到声明处不允许(同一作用域内)

2. 暂时性死区(TDZ)

  • 在块级作用域中,let/const声明的变量在声明前处于TDZ,访问会报错ReferenceError
if (true) {
  console.log(x); // ReferenceError(x在TDZ中)
  let x = 1;
}

十一、箭头函数 vs 普通函数

特性箭头函数普通函数
this指向继承外层作用域的this(静态绑定)动态绑定(调用时由上下文决定)
prototype属性有(指向原型对象)
arguments对象无(需用...rest参数)有(类数组对象)
作为构造函数不可(调用报错)可(需用new
super关键字继承外层作用域的super指向父类原型(需配合extends
new.target指向构造函数(new调用时)

典型场景

  • 箭头函数适合定义回调函数(避免this指向混乱,如setTimeout、数组方法map/filter)。
  • 普通函数适合需要动态this或作为构造函数的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值