Javascript个人问题收集

目录

1.splice在js中为什么可以当作数组的删除函数,它的英语本意是粘贴呀?

1. 英语原意 vs JavaScript 的功能

2. 为什么用 "splice" 表示删除?

3. 对比 slice 和 splice

4. 为什么感觉「别扭」?

5. 设计哲学

总结

2.为什么js的数组还可以被当作map集合那样使用也就是数组的[]中的参数可以是-1 , aaa 中文等等?

1. JavaScript 数组的本质是「对象」

2. 为什么允许非数字索引?

3. 非数字索引的陷阱

4. 数组 vs Map

5. 为什么允许 arr[-1]?

6. 总结:数组的正确使用姿势

为什么设计如此「怪异」?

最佳实践

3.为什么我觉得JavaScript的语法很奇怪,在我学习了Java之后,很多地方的定义都出乎我的意料?

1. 语言定位与设计目标

2. 核心语法差异对比

变量与类型系统

函数与作用域

面向对象编程(OOP)

3. 让你「出乎意料」的常见陷阱

动态类型与隐式转换

作用域与变量提升

this 的动态绑定

4. 为什么 JavaScript 要这样设计?

5. 如何适应 JavaScript 的「怪」?

总结

4.JavaScript 的模板字符串(Template Literals)和 jQuery 中类似 $('...') 的选择器语法看似有相似的符号 ${} 和 $ 二者之间的异同?

1、核心区别对比表

2、语法细节解析

1. JavaScript 模板字符串

2. jQuery 选择器语法

3、可能混淆的场景

1. 动态生成选择器字符串

2. 其他模板引擎的误导

4、总结:二者毫无关联,但可协作

问题1:字符串加减以及+-一元运算符

1. console.log(1 + "2" + "2");

2. console.log(1 + +"2" + "2");(重点分析)

3. console.log("A" - "B" + "2");

4. console.log("A" - "B" + 2);

总结:JavaScript 的类型转换规则

重点问题再解析(第二行 1 + +"2" + "2")

练习巩固

问题2:Java和JavaScript的短路原则语法对比

JavaScript 代码解析

JavaScript 逻辑运算符规则

Java 的对比分析

Java 代码尝试

Java 逻辑运算符规则

Java 合法替代写法

问题3:JavaScript中Object.is()方法和严格相等运算符===的区别

JavaScript 知识点详解

1. Object.is() 与 === 的区别

2. 特殊值的处理逻辑


1.splice在js中为什么可以当作数组的删除函数,它的英语本意是粘贴呀?

这是一个非常有意思的观察!你的困惑其实源于 JavaScript 中 splice 方法的设计逻辑和英语原意的微妙关系。我来为你拆解这个「别扭感」的来源和背后的逻辑:


1. 英语原意 vs JavaScript 的功能

  • 英语中的 "splice"
    本意是「拼接」(比如拼接绳子、拼接胶片),指将两个部分连接在一起。例如:

    • 电影剪辑中,"splice film" 表示将两段胶片粘合。

    • 电缆工程中,"splice wires" 表示将两根电线连接。

  • JavaScript 中的 splice
    它同时具备 删除、添加、替换 数组元素的能力,本质上是在「修改原数组的某一段,并用新内容拼接上去」。例如:

    const arr = [1, 2, 3, 4, 5];
    arr.splice(1, 2, 'a', 'b'); // 从索引1开始,删除2个元素,添加'a'和'b'
    console.log(arr); // [1, 'a', 'b', 4, 5]

    这里的行为就像将原数组「切断」一部分,再「拼接」新的内容


2. 为什么用 "splice" 表示删除?

  • 历史原因splice 方法的设计灵感可能来源于早期编程语言(如 Lisp)中对列表的操作术语,其中「剪切并替换」被抽象为一种「拼接」行为。

  • 功能本质splice 不单纯是删除,而是「在数组中某个位置切开一个口子」,你可以选择:

    • 仅删除元素(不添加新内容)

    • 删除并添加新元素

    • 仅添加新元素(删除数量设为0)

    // 只删除
    arr.splice(1, 2); // 删除索引1开始的2个元素
    
    // 只添加(删除0个)
    arr.splice(1, 0, 'x'); // 在索引1插入'x'

3. 对比 slice 和 splice

  • slice:英语原意是「切片」,对应它的功能——纯函数,返回数组的一个片段,不修改原数组

    const sub = arr.slice(1, 3); // 原数组不变
  • splice:英语原意是「拼接」,对应它的功能——直接修改原数组,像手术刀一样「切开并重新拼接」


4. 为什么感觉「别扭」?

  • 如果你只使用 splice 的删除功能(比如 arr.splice(1, 1)),会觉得它和「拼接」无关。

  • 但它的完整功能更符合「拼接」:删除旧内容,插入新内容,像修改胶片一样「剪掉一段,接上一段」


5. 设计哲学

JavaScript 的数组方法命名常常借用现实世界的隐喻:

  • push/pop:像栈一样「推入」和「弹出」。

  • shift/unshift:像队列的「移动」。

  • splice:用「拼接」隐喻数组的局部修改。


总结

splice 的「别扭感」源于你只关注了它的删除功能,而它的设计初衷是更通用的「剪切并拼接」。如果要找一个纯删除的方法,可以用 filter,但 splice 的独特之处在于它能直接修改原数组并精确控制位置。下次使用时,可以想象自己在「剪接数组」,这样名字就合理多了! ✂️➔

2.为什么js的数组还可以被当作map集合那样使用也就是数组的[]中的参数可以是-1 , aaa 中文等等?

这是一个非常关键的问题!JavaScript 的数组(Array)和其他语言的数组设计理念完全不同,它的行为更像一个「混合体」。你观察到的现象(数组可以像 Map 一样使用非数字索引)源于 JavaScript 的历史设计和动态语言的特性。让我们一步步拆解这个问题的本质:


1. JavaScript 数组的本质是「对象」

  • 底层逻辑:JavaScript 的数组实际上是披着数组外衣的特殊对象。当你创建一个数组时:

    const arr = [1, 2, 3];

    它在底层等价于:

    const arr = {
      0: 1,
      1: 2,
      2: 3,
      length: 3,
      __proto__: Array.prototype // 继承数组方法
    };
  • 数字索引只是属性名arr[0] 本质上是访问对象的属性 0,而 JavaScript 允许对象的属性名是字符串或 Symbol,包括数字字符串(如 "0""-1")或任意字符串(如 "aaa")。


2. 为什么允许非数字索引?

  • 动态语言的特性:JavaScript 是弱类型、动态的,允许运行时动态扩展对象的属性。

  • 数组的宽容性:为了灵活性,数组不严格限制你只能使用数字索引。你可以为数组添加任意属性:

    const arr = [1, 2, 3];
    arr[-1] = "我是负数索引";
    arr["aaa"] = "我是字符串索引";
    arr[{ id: 123 }] = "甚至对象作为键(会被转成字符串)";
  • 但这些属性不属于「数组元素」

    • 使用 arr.length 不会统计这些非数字键的属性。

    • 数组方法(如 mapforEachfilter)会忽略这些属性。


3. 非数字索引的陷阱

  • 看似数组,实为对象:当你使用非数字索引时,数组退化为普通对象的行为。

    const arr = [1, 2, 3];
    arr["foo"] = "bar";
    console.log(arr.length); // 3(非数字键不影响长度)
    console.log(arr); // [1, 2, 3, foo: 'bar']
  • 性能问题:V8 引擎对「纯数字索引的数组」有优化(存储为连续内存),但如果添加了非数字属性,数组会被降级为「字典模式」,失去性能优势。


4. 数组 vs Map

  • 设计目的不同

    • 数组:有序集合,专注于数字索引的快速访问。

    • Map:键值对集合,支持任意类型的键(包括对象、NaN),且键值对顺序可追踪。

  • 何时用 Map

    // 需要任意类型键时,用 Map
    const map = new Map();
    map.set(-1, "负数索引");
    map.set("aaa", "字符串索引");
    map.set({ id: 1 }, "对象作为键");

5. 为什么允许 arr[-1]

  • 语法糖的代价:JavaScript 允许 arr[-1],但它会被转换为字符串 "-1",访问的是数组对象的 "-1" 属性,而不是倒数第一个元素!

  • 获取倒数元素的正解

    // 正确方式:用 arr[arr.length - 1]
    const lastElement = arr[arr.length - 1];

6. 总结:数组的正确使用姿势

场景 正确工具 错误工具
有序数字索引集合 数组 (Array) 对象/Map
任意类型键的键值对 Map 数组/普通对象
需要负数/字符串索引 Map 数组(违反设计)

为什么设计如此「怪异」?

  • 历史包袱:JavaScript 早期设计受 Java 影响,但为了简化又采用了动态特性。

  • 灵活性优先:允许开发者将数组当作「超级对象」使用,但也带来了误用风险。


最佳实践

  1. 严格区分数据用途

    • 纯数字索引集合 → 数组。

    • 键值对 → Map 或普通对象。

  2. 避免数组滥用

    // ❌ 错误:用数组存储键值对
    const badArray = [];
    badArray["user"] = "John";
    badArray["age"] = 30;
    
    // ✅ 正确:用对象或 Map
    const goodObj = { user: "John", age: 30 };
    const goodMap = new Map([["user", "John"], ["age", 30]]);
  3. 防御性编程

    // 检查一个属性是否是真正的数组元素
    function isArrayElement(arr, index) {
      return index >= 0 && index < arr.length;
    }

JavaScript 的数组设计是一种「能力越大,责任越大」的哲学——它给你极大的灵活性,但也需要开发者自律地使用。

3.为什么我觉得JavaScript的语法很奇怪,在我学习了Java之后,很多地方的定义都出乎我的意料?

你在学习 JavaScript 后感到「语法奇怪」,尤其是从 Java 转向 JavaScript 时,这种反差感非常普遍。这种「怪异感」本质上源于 JavaScript 和 Java 在语言设计哲学、类型系统、运行时行为等方面的根本性差异。以下是具体原因和关键对比:


1. 语言定位与设计目标

  • Java

    • 强类型、静态、面向对象,设计初衷是「一次编写,到处运行」的企业级应用开发。

    • 严格性优先:编译器强制类型检查,要求显式声明类、接口和类型。

  • JavaScript

    • 动态、弱类型、多范式,设计初衷是「为网页添加简单的交互」。

    • 灵活性优先:允许运行时动态修改对象、类型,甚至代码结构。


2. 核心语法差异对比

变量与类型系统
特性 Java JavaScript
变量声明 必须显式声明类型 (int x = 5;) var/let/const 无需类型注解
类型检查 编译时强类型检查(类型不匹配报错) 运行时弱类型(隐式类型转换)
默认值 基本类型有默认值(如 int 为 0) 变量未赋值时为 undefined
类型系统 原生支持基本类型和对象类型 只有 numberstringboolean 等动态类型

示例:类型容忍度

// JavaScript 允许以下操作
let x = "5" + 2;      // "52" (字符串拼接)
let y = "5" - 2;      // 3 (隐式转换为数字)
函数与作用域
特性 Java JavaScript
函数定义 必须属于某个类(方法) 函数是一等公民,可独立存在
作用域 块级作用域({} 内) 早期只有函数作用域,ES6 引入 let/const 块级作用域
闭包 不存在(Lambda 有限支持) 天然支持闭包

示例:函数作为参数传递

// JavaScript 中函数可以像变量一样传递
function greet(name) { return `Hello, ${name}!`; }
function wrapper(func, value) { return func(value); }
wrapper(greet, "Alice"); // "Hello, Alice!"
面向对象编程(OOP)
特性 Java JavaScript
继承机制 基于类的继承 (extends) 基于原型的继承(无类,只有对象和原型链)
this 关键字 指向当前实例对象 动态绑定,取决于调用方式
封装性 private/protected 修饰符 无原生私有属性(ES2022+ 支持 # 私有字段)

示例:原型链继承

function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { console.log(`${this.name} makes a noise`); };

function Dog(name) { Animal.call(this, name); }
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

const dog = new Dog("Rex");
dog.speak(); // "Rex makes a noise"

3. 让你「出乎意料」的常见陷阱

动态类型与隐式转换
console.log(1 + "1");    // "11" (不是 2)
console.log([] == 0);    // true ([] 转换为 "" → 0)
console.log(null == undefined); // true (但 null !== undefined)
作用域与变量提升
console.log(x); // undefined (不是报错)
var x = 5;
// let x = 5; 会报错(暂时性死区)
this 的动态绑定
const obj = {
  name: "Alice",
  logName: function() { console.log(this.name); }
};

const log = obj.logName;
log(); // undefined(严格模式下 this 是 undefined)

4. 为什么 JavaScript 要这样设计?

  • 历史原因:JavaScript 在 10 天内被设计出来,最初目标是为网页添加简单交互,而非复杂应用。

  • 动态语言的优势:允许快速原型开发、灵活修改对象,适合不确定需求变化的场景(如早期网页开发)。

  • 函数式编程基因:高阶函数、闭包等特性使其适合异步编程和事件驱动模型。


5. 如何适应 JavaScript 的「怪」?

  • 理解核心理念

    • 接受「一切皆对象」(包括函数)和「原型链」的概念。

    • 用 === 代替 == 避免隐式转换。

    • 使用 let/const 和严格模式 ("use strict") 减少意外行为。

  • 工具辅助

    • 使用 TypeScript 添加静态类型检查。

    • 利用 ESLint 捕捉潜在错误。

  • 学习现代 JavaScript

    • ES6+ 的 class 语法糖、模块化、Promise 等特性让代码更接近传统 OOP 语言。


总结

Java 和 JavaScript 的差异类似于「严谨的建筑图纸」与「灵活的手工雕塑」。Java 强调结构性和安全性,JavaScript 则追求表达力和适应性。这种设计差异导致语法和行为的「怪异感」,但也正是 JavaScript 能在浏览器、服务端(Node.js)、移动端(React Native)等多领域大放异彩的原因。理解这些差异后,你会逐渐欣赏 JavaScript 的独特哲学!

4.JavaScript 的模板字符串(Template Literals)和 jQuery 中类似 $('...') 的选择器语法看似有相似的符号 ${} 和 $ 二者之间的异同?

1、核心区别对比表

特性 JavaScript 模板字符串 jQuery 选择器语法
符号作用 字符串插值(变量替换) DOM 元素选择器
符号位置 反引号字符串内使用 ${expression} $() 包裹 CSS 选择器字符串
底层原理 ES6 语法特性,由 JavaScript 引擎解析 jQuery 库提供的函数,解析字符串查找 DOM
使用场景 动态拼接字符串、多行文本 快速选取或操作 HTML 元素
依赖关系 原生 JavaScript 支持(无需库) 需引入 jQuery 库

2、语法细节解析

1. JavaScript 模板字符串
  • 语法:使用 反引号`)包裹字符串,通过 ${} 嵌入变量或表达式。

  • 用途:动态生成字符串内容,替代传统字符串拼接。

    const name = "Alice";
    const age = 25;
    
    // 传统拼接
    const oldStr = "Name: " + name + ", Age: " + age;
    
    // 模板字符串
    const newStr = `Name: ${name}, Age: ${age}`;
  • 高级用法:支持多行字符串和嵌套表达式。

    const html = `
      

    ${name.toUpperCase()}

    年龄:${age > 18 ? "成年" : "未成年"}

    `;
2. jQuery 选择器语法
  • 语法:通过 $() 函数传入 CSS 选择器字符串,返回匹配的 DOM 元素集合。

  • 用途:快速操作 DOM 元素,与模板字符串无直接关联。

    // 选择 id 为 "header" 的元素
    const $header = $('#header');
    
    // 选择 class 为 "item" 的所有元素
    const $items = $('.item');
    
    // 动态选择器(需结合模板字符串)
    const dynamicId = "main";
    const $dynamicElement = $(`#${dynamicId}`); // 此处模板字符串生成选择器字符串

3、可能混淆的场景

1. 动态生成选择器字符串

若在 jQuery 的 $() 中使用模板字符串生成动态选择器,二者会结合使用,但本质是独立分工

const id = "myElement";
// 使用模板字符串生成选择器字符串,再交给 jQuery 解析
const $element = $(`#${id}`); 
  • 关键点

    • 模板字符串仅负责生成字符串(如 "#myElement"

    • jQuery 的 $() 函数负责解析该字符串并选择 DOM

2. 其他模板引擎的误导

某些模板引擎(如 Vue、Angular)或旧版库可能使用类似 ${} 的插值语法,但这与 JavaScript 模板字符串无直接关系。例如:

  • Vue 模板:使用 {{ value }} 插值(非 ${})。

  • jQuery 插件:个别插件可能自定义类似语法,但非 jQuery 核心功能。


4、总结:二者毫无关联,但可协作

  1. 模板字符串是 JavaScript 语言层面的字符串插值工具。

  2. jQuery 选择器是库提供的 DOM 操作接口。

  3. 协作场景:用模板字符串生成选择器字符串,再传递给 $() 函数。

重点注意

  • 勿混淆语法${} 仅在反引号字符串中有效,$() 是 jQuery 的函数调用。

  • 勿误解功能:模板字符串不操作 DOM,jQuery 不解析 ${} 插值(除非显式生成字符串后传递)。

理解二者的独立性和协作方式,可避免代码中的误用和逻辑混乱!

问题1:字符串加减以及+-一元运算符

1. console.log(1 + "2" + "2");

输出: "122"
解析:

  • 第一个 + 操作1 + "2"

    • 数字 1 与字符串 "2" 相加,触发字符串拼接,结果为 "12"(字符串)。

  • 第二个 + 操作"12" + "2"

    • 两个字符串直接拼接,结果为 "122"


2. console.log(1 + +"2" + "2");(重点分析)

关键:Java 严格禁止对非数值类型应用一元运算符。

输出: "32"
解析:

  • 一元加号 +"2"

    • +"2" 将字符串 "2" 转换为数字 2

  • 第一个 + 操作1 + 2

    • 数字相加,结果为 3(数字)。

  • 第二个 + 操作3 + "2"

    • 数字 3 与字符串 "2" 相加,触发字符串拼接,结果为 "32"(字符串)。

关键点:

  • 一元加号 + 的优先级高于加法 +,所以 +"2" 会先执行。

  • 表达式等价于 (1 + (+"2")) + "2"

练习:console.log(1 + -"2" + "2")

逐步解析 console.log(1 + -"2" + "2") 的输出结果:

  1. 处理一元负号运算符 -"2"

    • -"2" 将字符串 "2" 转换为数字 2,然后取负数 → -2

  2. 执行数值加法 1 + (-2)

    • 1(数字) + -2(数字) → -1(数字)。

  3. 执行字符串拼接 -1 + "2"

    • -1(数字)与 "2"(字符串)相加时,数字转换为字符串 → "-1" + "2" → "-12"(字符串)。


3. console.log("A" - "B" + "2");

输出: "NaN2"
解析:

  • 减法操作 "A" - "B"

    • 字符串 "A" 和 "B" 无法转换为有效数字,结果为 NaN(Not a Number)。

  • 加法操作 NaN + "2"

    • NaN 与字符串 "2" 相加,触发字符串拼接,结果为 "NaN2"(字符串)。


4. console.log("A" - "B" + 2);

输出: NaN
解析:

  • 减法操作 "A" - "B"

    • 同上,结果为 NaN

  • 加法操作 NaN + 2

    • NaN 与任何数字相加,结果仍为 NaN


总结:JavaScript 的类型转换规则

操作符 行为
+ 若一侧为字符串,优先拼接字符串;否则进行数字加法。
- 始终尝试将两侧转换为数字,无法转换时结果为 NaN
一元 + 将操作数强制转换为数字(例如 +"2" → 2+"A" → NaN)。

重点问题再解析(第二行 1 + +"2" + "2"

  1. 执行顺序

    javascript

    复制

    下载

    1 + (+"2") + "2"  // 分解步骤
    → 1 + 2 + "2"     // 一元加号将 "2" 转为数字 2
    → 3 + "2"         // 数字 1 + 2 = 3
    → "32"            // 数字 3 与字符串 "2" 拼接
  2. 为什么不是 1 + "2" + "2" = "122"

    • 一元加号 + 改变了优先级和类型转换逻辑,导致 +"2" 被优先转换为数字。


练习巩固

如果修改第二行为 1 + +"2" + +"2",结果会是什么?
答案: 5(数字),因为两次一元加号将字符串转为数字,最终执行 1 + 2 + 2

问题2:Java和JavaScript的短路原则语法对比

JavaScript 代码解析

console.log(0 && 1, 0 || 1, 1 && 3, 1 || 3); 
// 输出:0 1 3 1
JavaScript 逻辑运算符规则
  1. &&(逻辑与)

    • 行为:若左侧为真值(truthy),返回右侧操作数;否则返回左侧操作数。

    • 短路逻辑:左侧为假值时,右侧不执行。

  2. ||(逻辑或)

    • 行为:若左侧为真值,返回左侧操作数;否则返回右侧操作数。

    • 短路逻辑:左侧为真值时,右侧不执行。

Java 的对比分析

Java 的逻辑运算符与 JavaScript 存在本质区别,以下是相同表达式在 Java 中的行为和结果:

Java 代码尝试

// 以下代码均无法编译!
System.out.println(0 && 1); // 错误:操作数类型不兼容
System.out.println(0 || 1); // 错误:操作数类型不兼容
System.out.println(1 && 3); // 错误:操作数类型不兼容
System.out.println(1 || 3); // 错误:操作数类型不兼容
Java 逻辑运算符规则
  1. && 和 ||

    • 操作数类型:必须是布尔值(boolean),不接受其他类型(如 int)。

    • 返回值:布尔值 true 或 false

    • 短路逻辑:与 JavaScript 一致,但仅适用于布尔表达式。

  2. Java 的类型安全

    • 整数(如 01)不能直接作为逻辑运算的操作数,必须显式转换为布尔值(例如通过比较操作)。

Java 合法替代写法
System.out.println((0 != 0) && (1 != 0)); // false && true → false
System.out.println((0 != 0) || (1 != 0)); // false || true → true
System.out.println((1 != 0) && (3 != 0)); // true && true → true
System.out.println((1 != 0) || (3 != 0)); // true || true → true

问题3:JavaScript中Object.is()方法和严格相等运算符===的区别

下列表达式中,返回值为true的是()

①Object.is(NaN,NaN)

②Object.is(+0,-0)

③NaN === NaN

④+0 === -0

JavaScript 知识点详解

1. Object.is() 与 === 的区别
场景 Object.is() ===(严格相等)
NaN 比较 NaN === NaN → true NaN === NaN → false
+0 与 -0 Object.is(+0, -0) → false +0 === -0 → true
2. 特殊值的处理逻辑
  • NaN 的独特性

    • NaN 是唯一不等于自身的值(NaN !== NaN)。

    • Object.is() 专门修复此问题,认为 NaN 等于自身。

  • +0 与 -0 的物理差异

    • 1/+0 → Infinity1/-0 → -Infinity

    • === 忽略符号差异,而 Object.is() 严格区分。

你可能感兴趣的:(javascript,开发语言,前端,java)