js变量
loyalvi Lv7

js变量

变量

在函数中,变量的主要作用是暂存业务处理过程中用到的一些值。JS 中的变量是使用 var 关键字来定义的,无论什么类型的变量都使用它来定义,因此 JS 是一种弱类型语言。但是,它的变量是区分大小写的,也就是说red 、 Red、reD是三个不同的变量 。
JS中的变量早期要求必须是用_、“$”、英文字母和数字组成,而且第一个字符不可以是数字。对于现在的引擎来说,只要在词法解析的时候不引起误会就可以了,即使使用中文也是可以的。
以下是以下关键词:
!Pasted image 20240414160948.png

操作符

js运算符
!Pasted image 20240414161414.png
操作符...的作用:

  • 将参数转化为数组 function mailTo(...names){};
  • 将数组转化为参数 Math.max(...[1,3,5]);
    !Pasted image 20240414161852.png

NaN

NaN 是JavaScript中的一个特殊数值,代表“不是一个数字”(Not-a-Number)。它用于表示某些无法表示的数值结果,例如除以零、对负数开平方根等。以下是关于 NaN 的一些详细介绍:

产生NaN的情形

  • 数学运算
    • 除以零:0 / 0Infinity / InfinityInfinity / -Infinity等。
    • 对负数开平方根:Math.sqrt(-1)
    • 无穷大减去无穷大:Infinity - Infinity
  • 类型转换
    • 将非数字字符串转换为数字:Number("hello")parseInt("hello")等。
  • 不合理的运算
    • 对一个非数字进行数学运算:"hello" * 5

NaN的特性

  • 不等于任何值NaN不等于任何值,包括它自己。例如,NaN === NaN的结果是false。这是因为NaN代表一个不确定的值,无法与任何具体值进行比较。
  • 使用isNaN()函数检测:可以使用isNaN()函数来检测一个值是否为NaN。例如,isNaN(NaN)返回true。不过,isNaN()函数有时会返回不准确的结果,因为它会先将参数转换为数字,然后再判断是否为NaN。例如,isNaN("hello")也会返回true,因为"hello"会被转换为NaN
  • 使用Number.isNaN()函数检测:ES6引入了Number.isNaN()函数,用于更准确地检测一个值是否为NaN。例如,Number.isNaN(NaN)返回true,而Number.isNaN("hello")返回false
  • 传播性:在涉及NaN的运算中,结果通常也是NaN。例如,NaN + 5NaN * 10等运算的结果都是NaN

示例

1
2
3
4
5
6
7
8
9
10
11
console.log(0 / 0); // NaN
console.log(Math.sqrt(-1)); // NaN
console.log(Number("hello")); // NaN
console.log("hello" * 5); // NaN
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true
console.log(isNaN("hello")); // true
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("hello")); // false
console.log(NaN + 5); // NaN
console.log(NaN * 10); // NaN

NaN 在JavaScript中是一个重要的特殊值,了解其产生原因、特性以及检测方法,有助于避免在数值运算和类型转换中出现错误。

isNaN

在 JavaScript 中,isNaN 是一个用于检测值是否为 NaN(Not-a-Number) 的全局函数。由于 NaN 的特殊性(例如 NaN !== NaN),直接判断一个值是否是 NaN 需要借助工具函数。以下是 isNaN 的详细说明及注意事项:

一、isNaN 的基本行为

  • 语法isNaN(value)
  • 作用:检测 value 是否是 NaN,或在转换为数值后是否为 NaN
  • 返回值:布尔值(truefalse)。

关键规则:

  1. 隐式类型转换isNaN 会先将参数转换为数值,再判断是否是 NaN
  2. NaN 的特殊性NaN 是唯一一个不等于自身的值(NaN !== NaN),因此需要 isNaN 辅助判断。

二、isNaN 的示例分析

1. 直接检测 NaN

1
2
isNaN(NaN);          // true
isNaN(Number.NaN); // true

2. 数值类型

1
2
3
isNaN(123);          // false(数值)
isNaN(0 / 0); // true(0/0 结果为 NaN)
isNaN(Infinity); // false(无穷大是有效数值)

3. 字符串

1
2
3
4
isNaN("123");        // false(字符串可转为数值 123)
isNaN("abc"); // true(无法转为数值)
isNaN(""); // false(空字符串转为 0)
isNaN(" "); // false(空白字符串转为 0)

4. 其他类型

1
2
3
4
5
6
isNaN(true);         // false(true → 1)
isNaN(false); // false(false → 0)
isNaN(null); // false(null → 0)
isNaN(undefined); // true(undefined → NaN)
isNaN({}); // true(对象无法转为有效数值)
isNaN([1, 2]); // true(数组转字符串后无法转为数值)

三、isNaN 的陷阱

由于 isNaN 的隐式类型转换,某些情况下可能产生反直觉的结果:

示例:

1
2
3
4
5
6
isNaN("123abc");     // true(字符串部分可解析为数字?不,全部解析失败)
isNaN(" 123 "); // false(字符串去除空格后为 123)
isNaN(new Date()); // false(Date 对象转为时间戳数值)
isNaN([]); // false(空数组转为 0)
isNaN([123]); // false(单元素数组转为数值 123)
isNaN([NaN]); // true(数组包含 NaN,转为 NaN)

四、isNaN vs Number.isNaN

场景 isNaN 结果 Number.isNaN 结果 原因
isNaN(NaN) true true 直接检测 NaN
isNaN("abc") true false 字符串不是 NaN
isNaN(123) false false 数值有效
isNaN(undefined) true false undefined 本身不是 NaN
isNaN("123") false false 字符串可转为数值,但非 NaN

示例:

ES6 引入了更严格的 Number.isNaN不会进行隐式类型转换,直接判断是否为 NaN

1
2
3
Number.isNaN(NaN);        // true
Number.isNaN("abc"); // false(严格判断,不转换)
Number.isNaN(0 / 0); // true(0/0 是 NaN)

五、如何正确判断 NaN?

  1. 优先使用 Number.isNaN:避免隐式转换的副作用。
  2. 手动检查:利用 NaN 的特性(NaN !== NaN):
    1
    2
    3
    4
    function isTrueNaN(value) {
    return value !== value;
    }
    isTrueNaN(NaN); // true

六、总结

方法 特点 适用场景
isNaN 隐式类型转换后判断 需要兼容旧代码的宽泛检测
Number.isNaN 严格判断是否为 NaN 精确检测 NaN(推荐)
value !== value 直接利用 NaN 的特性 兼容性更好的替代方案

最佳实践

  • 避免全局 isNaN:在 ES6+ 中优先使用 Number.isNaN
  • 显式类型转换:若需要检测字符串是否为数值,应先显式转换:
    1
    2
    3
    4
    const str = "123";
    if (Number.isNaN(Number(str))) {
    console.log("字符串无法转为数值");
    }

通过理解 isNaN 的隐式转换逻辑和 Number.isNaN 的严格性,可以更安全地处理 JavaScript 中的 NaN 检测。

变量提升

在 JavaScript 中,变量提升(Hoisting)是一个重要的概念,它指的是变量和函数声明会被提升到其所在作用域的顶部。这意味着你可以在声明变量或函数之前就使用它们,因为 JavaScript 引擎在执行代码之前会先处理这些声明。不过,需要注意的是,只有声明会被提升,而赋值操作不会被提升。

变量提升

声明提升

当使用 var 关键字声明变量时,变量的声明会被提升到函数作用域(如果在函数内)或全局作用域(如果在全局作用域内)的顶部。但是,变量的初始化(赋值)不会被提升。

1
2
3
console.log(x); // 输出 undefined,而不是 ReferenceError
var x = 5;
console.log(x); // 输出 5

在这个例子中,变量 x 的声明被提升到了全局作用域的顶部,所以第一行 console.log(x) 不会抛出 ReferenceError。但是,由于赋值操作没有被提升,所以在声明和赋值之前访问 x 会得到 undefined

函数声明提升

函数声明也会被提升,这意味着你可以在函数声明之前调用该函数。

1
2
3
4
console.log(sayHello()); // 输出 "Hello"
function sayHello() {
return "Hello";
}

在这个例子中,函数 sayHello 的声明被提升到了全局作用域的顶部,所以可以在声明之前调用它。

函数表达式提升

函数表达式的行为与变量声明类似。函数表达式的声明会被提升,但赋值操作不会被提升。

1
2
3
4
console.log(sayHello()); // 抛出 TypeError: sayHello is not a function
var sayHello = function() {
return "Hello";
};

在这个例子中,变量 sayHello 的声明被提升,但赋值操作没有被提升。因此,在声明和赋值之前尝试调用 sayHello 会抛出 TypeError,因为此时 sayHelloundefined

letconst 的提升

var 不同,letconst 声明的变量也会被提升,但它们会被置于一个“暂时性死区”(Temporal Dead Zone, TDZ),这意味着在块的开始到变量声明之前,这些变量是不可访问的。

1
2
3
console.log(x); // 抛出 ReferenceError: x is not defined
let x = 5;
console.log(x); // 输出 5

在这个例子中,尽管 let 声明的变量 x 被提升了,但在声明之前访问 x 会抛出 ReferenceError,因为 x 处于暂时性死区。

总结

  • 变量提升var 声明的变量会被提升到其所在作用域的顶部,但赋值操作不会被提升。
  • 函数声明提升:函数声明会被提升,可以在声明之前调用。
  • 函数表达式提升:函数表达式的声明会被提升,但赋值操作不会被提升。
  • letconst 提升letconst 声明的变量会被提升,但会处于暂时性死区,直到它们被声明。
    理解变量提升有助于避免一些常见的错误,特别是在使用 var 声明变量时。使用 letconst 可以减少变量提升带来的问题,因为它们的提升行为更加严格。

实例

1
2
3
4
5
6
7
8
var foo = {n:1};
(function(foo){
console.log(foo.n);
foo.n = 3;
var foo = {n:2};
console.log(foo.n);
})(foo);
console.log(foo.n);

等价于:

1
2
3
4
5
6
7
8
9
10
11
12
13
var foo = {
n: 1
};
(function(foo) { //形参foo同实参foo一样指向同一片内存空间,这个空间里的n的值为1
var foo; //优先级低于形参,无效。
console.log(foo.n); //输出1
foo.n = 3; //形参与实参foo指向的内存空间里的n的值被改为3
foo = {
n: 2
}; //形参foo指向了新的内存空间,里面n的值为2.
console.log(foo.n); //输出新的内存空间的n的值
})(foo);
console.log(foo.n); //实参foo的指向还是原来的内存空间,里面的n的值为3.

let和`const

letconst 都是 ES6(ECMAScript 2015)引入的新关键字,用于声明变量,它们在很多方面都优于传统的 var 关键字。虽然它们在块级作用域、暂时性死区等方面有相似之处,但也存在一些关键区别。以下是 letconst 的主要区别:

1. 可变性

  • let:声明的变量可以重新赋值。
  • const:声明的变量必须初始化,并且一旦赋值后就不能再被重新赋值。

示例

1
2
3
4
5
6
let x = 5;
x = 10; // 合法,可以重新赋值
console.log(x); // 输出 10
const y = 5;
y = 10; // 抛出 TypeError: Assignment to constant variable.
console.log(y); // 这行代码不会执行

2. 初始化要求

  • let:声明的变量可以不立即初始化,但可以在后续的代码中赋值。
  • const:声明的变量必须在声明时立即初始化。

示例

1
2
3
4
let z; // 合法,可以不立即初始化
z = 15;
console.log(z); // 输出 15
const w; // 抛出 SyntaxError: Missing initializer in const declaration

3. 适用场景

  • let:适用于需要在块级作用域内重新赋值的变量,比如循环计数器、临时变量等。
  • const:适用于不需要重新赋值的常量,比如配置项、常量值、函数和对象引用等。即使使用 const 声明的对象,对象的属性仍然可以被修改,只是对象引用本身不能被重新赋值。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用 let 声明循环计数器
for (let i = 0; i < 5; i++) {
console.log(i); // 输出 0, 1, 2, 3, 4
}
console.log(i); // 抛出 ReferenceError: i is not defined
// 使用 const 声明对象
const person = {
name: 'Kimi',
age: 25
};
person.age = 26; // 合法,可以修改对象的属性
console.log(person); // { name: 'Kimi', age: 26 }
// 但不能重新赋值对象引用
person = { name: 'Moonshot', age: 30 }; // 抛出 TypeError: Assignment to constant variable.

4. 作用域

  • letconst 都具有块级作用域,这意味着它们只在声明它们的块({ ... })内有效。这与 var 的函数级作用域或全局作用域不同。

示例

1
2
3
4
5
6
if (true) {
let a = 1;
const b = 2;
}
console.log(a); // 抛出 ReferenceError: a is not defined
console.log(b); // 抛出 ReferenceError: b is not defined

5. 暂时性死区(Temporal Dead Zone, TDZ)

  • letconst 都会提升到块的顶部,但它们在声明之前处于暂时性死区,这意味着在声明之前访问它们会抛出 ReferenceError
  • var 也会提升,但提升后的值是 undefined,而不是进入暂时性死区。

示例

1
2
3
4
console.log(a); // 抛出 ReferenceError: a is not defined
let a = 5;
console.log(b); // 抛出 ReferenceError: b is not defined
const b = 10;

总结

  • let:适用于需要在块级作用域内重新赋值的变量。
  • const:适用于不需要重新赋值的常量,有助于代码的可读性和维护性。
    在实际开发中,推荐尽可能使用 const 声明变量,除非确实需要重新赋值,再使用 let。这样可以减少变量被意外修改的风险,提高代码的健壮性。

实例

  • 报错,a 变量提升,声明后暂时性死区
1
2
3
4
5
6
7
var a = 1;
function test(){
console.log(a);
class a {}
//console.log(a);
}
test();
由 Hexo 驱动 & 主题 Keep
访客数 访问量