天津理工大学前端组大一末期考核随记(2)

随便记点考到的东西

null 和 undefined 的区别

nullundefined 是 JavaScript 中表示"空值"的两种类型,但它们在语义和使用上有本质区别。

对比维度 undefined null
类型 "undefined" "object"(JS 历史遗留 Bug)
语义(最主要) "未定义"/"未赋值" "空值"/"主动清空"
来源 JS 引擎自动产生 开发者手动赋值
转为数值 NaN 0

instanceof 和 typeof 判断数据类型的原理

typeofinstanceof 是 JavaScript 中两种常用的类型判断方法,但它们的判断原理完全不同

一、typeof 的原理

1. 基本原理

typeof 通过检查 JavaScript 内部存储的"类型标记" 来判断数据类型。

在 JavaScript 引擎中,每个值都有一个内部类型标记(type tag):

  • 在底层(C++ 实现),每个 JS 值都会存储一个表示类型的标志位

javascript

arduino 复制代码
// JavaScript 值在引擎内部的简化表示
typedef union {
  struct {
    uint64_t typeTag;  // 类型标记(1 = 对象,2 = 字符串,3 = 数字...)
    uint64_t value;    // 实际值
  };
  double numberValue;  // 数字直接存储
} JSValue;

2. null 的特殊情况(历史 Bug)

javascript

csharp 复制代码
typeof null; // "object"(这是 JS 的著名 Bug)

为什么 :在 JS 早期实现中,null 被表示为 空对象指针 ,类型标记被设为 0(代表对象),这个 Bug 一直保留至今。

javascript

javascript 复制代码
// 准确判断 null 的方法
Object.prototype.toString.call(null); // "[object Null]"

3. typeof 的局限性

javascript

csharp 复制代码
// ❌ 无法区分对象的具体类型
typeof {};        // "object"
typeof [];        // "object"(数组也是对象)
typeof new Date(); // "object"
typeof /regex/;   // "object"

// ✅ 只有函数能特殊识别
typeof function(){}; // "function"

二、instanceof 的原理

1. 基本原理

instanceof 通过检查构造函数的 prototype 属性是否出现在对象的原型链上

javascript

javascript 复制代码
// 基本语法
object instanceof Constructor

// 判断逻辑(简化版的手写实现)
function instanceOf(obj, Constructor) {
  // 1. 获取构造函数的 prototype 对象
  const prototype = Constructor.prototype;
  
  // 2. 获取对象的原型(__proto__)
  let proto = Object.getPrototypeOf(obj);
  
  // 3. 沿着原型链向上查找
  while (proto !== null) {
    if (proto === prototype) {
      return true;  // 找到了,返回 true
    }
    proto = Object.getPrototypeOf(proto);
  }
  
  return false;  // 到 null 都没找到,返回 false
}

2. 原型链查找示例

javascript

javascript 复制代码
function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype);

const child = new Child();

// 判断过程
child instanceof Child;   // true
child instanceof Parent;  // true
child instanceof Object;  // true

// 原型链:
// child → Child.prototype → Parent.prototype → Object.prototype → null
//         ↑                ↑                 ↑
//         Child.prototype   Parent.prototype  Object.prototype

3. instanceof 的特性

特性1:只能判断对象

javascript

javascript 复制代码
// 基本类型返回 false
1 instanceof Number;     // false
'string' instanceof String; // false
true instanceof Boolean;    // false

// 需要用包装对象才能判断!!!
new Number(1) instanceof Number;  // true

特性2:instanceof 可被修改

javascript

javascript 复制代码
function MyClass() {}

const obj = new MyClass();
obj instanceof MyClass;  // true

// 修改原型链后失效
MyClass.prototype = {};
obj instanceof MyClass;  // false(原型链断了)

三、两者的对比总结

维度 typeof instanceof
判断原理 检查内部类型标记 检查原型链
适用类型 基本类型 + 函数 对象类型
能否判断 null ❌ 返回 "object"(Bug) ❌ 始终 false
能否判断数组 ❌ 返回 "object" arr instanceof Array
能否判断自定义类 ❌ 返回 "object" obj instanceof MyClass
可被修改 ❌ 不可修改 prototype 可被修改

四、更准确的类型判断方法

1. Object.prototype.toString.call()

这是最准确的类型判断方法,能区分所有内置类型。

javascript

scss 复制代码
function getType(value) {
  return Object.prototype.toString.call(value);
}

getType(123);           // "[object Number]"
getType("hello");       // "[object String]"
getType(true);          // "[object Boolean]"
getType(null);          // "[object Null]"
getType(undefined);     // "[object Undefined]"
getType({});            // "[object Object]"
getType([]);            // "[object Array]"
getType(function(){});  // "[object Function]"
getType(new Date());    // "[object Date]"
getType(/regex/);       // "[object RegExp]"
getType(Symbol());      // "[object Symbol]"
getType(123n);          // "[object BigInt]"

forEachfor...in 的区别

forEachfor...in 都是用于遍历的语法,但它们的设计目的、适用场景、遍历范围完全不同。


一、核心区别速览

维度 forEach for...in
适用类型 数组(及类数组) 对象(可枚举属性)
遍历内容 数组索引对应的值 对象可枚举属性名(包括原型链)
遍历原型链 ❌ 只遍历自身属性 ✅ 会遍历原型链上的属性
返回结果 undefined 遍历出的结果是 key(属性名)
能否中断break ❌ 不能(除非抛异常) ✅ 可以用 break/return 中断
使用场景 数组遍历 对象属性遍历

二、详细对比

1. 适用类型不同

javascript 复制代码
// forEach:专门为数组设计
const arr = [1, 2, 3];
arr.forEach((item, index) => {
  console.log(item); // 1, 2, 3
});

// for...in:为对象设计
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  console.log(key); // 'a', 'b', 'c'
  console.log(obj[key]); // 1, 2, 3
}

// ❌ forEach 不能直接用于普通对象
// obj.forEach is not a function

2. 原型链遍历问题(重要!)

javascript 复制代码
// 给数组原型添加属性
Array.prototype.customProp = 'hello';

const arr = [1, 2, 3];

// forEach:只遍历数组自身的元素
arr.forEach(item => {
  console.log(item); // 1, 2, 3(不会输出 'hello')
});

// for...in:会遍历原型链上的属性!
for (let key in arr) {
  console.log(key); // '0', '1', '2', 'customProp'(多了原型属性)
}

// 解决方案:使用 hasOwnProperty 过滤
for (let key in arr) {
  if (arr.hasOwnProperty(key)) {
    console.log(key); // 只输出 '0', '1', '2'
  }
}

4. 能否中断

javascript 复制代码
// forEach:不能中途中断
const arr = [1, 2, 3, 4, 5];
arr.forEach(item => {
  if (item === 3) {
    // ❌ 这里不能 break 或 return 终止循环
    // return 只会退出当前回调,不影响下一次迭代
  }
  console.log(item); // 1, 2, 3, 4, 5(全部输出)
});

// for...in:可以用 break 中断
for (let key in arr) {
  if (Number(key) === 2) {
    break; // ✅ 退出循环
  }
  console.log(arr[key]); // 只输出 1, 2
}

三、替代方案对比

1. for...of(ES6 推荐)

for...of 结合了两者的优点:遍历数组值、可中断、不遍历原型链。

javascript 复制代码
const arr = [1, 2, 3];

// for...of:直接遍历值
for (let value of arr) {
  console.log(value); // 1, 2, 3
  if (value === 2) break; // ✅ 可以中断
}

// 不会遍历原型链属性
Array.prototype.custom = 'test';
for (let value of arr) {
  console.log(value); // 1, 2, 3(不输出 'test')
}


相关推荐
暗不需求1 小时前
深入理解 React 受控组件与非受控组件:从源码到面试
前端·react.js·面试
冰凌时空1 小时前
Swift 类型系统入门:从 Int、String 到自定义类型
前端·ios·ai编程
hexu_blog1 小时前
前端vue后端java+springboot如何实现pdf,word,excel之间的相互转换
java·前端·vue.js·spring boot·文档转换
w_t_y_y2 小时前
vue父子组件通信(二)祖先调用inject
前端·javascript·vue.js
哆哆啦002 小时前
URL 重写规则和静态资源解析逻辑
前端·浏览器·url
wkj0012 小时前
JavaScript模块化技术进程详解
开发语言·javascript·ecmascript
IT_陈寒2 小时前
Java的Stream.peek()千万别乱用,血泪教训
前端·人工智能·后端
w_t_y_y2 小时前
VUE组件配置项(二)data和props
前端·javascript·vue.js
Martin -Tang2 小时前
uniapp+vue3+ts自定义表格
javascript·vue.js·uni-app