前端学习记录~2023.7.22~JavaScript重难点实例精讲~第1章 Javascript重点概念

地图打印与 GeoTIFF 导出:框选区域、矢量标绘、一键出图 空间智能实验室 Vol.19:地图打印与 GeoTIFF 导出:框选区域、矢量标绘、一键出图 「把这块区域导成一张带地理坐标的高清图」是个很朴素的需求,但朴素不等于简单。你要在浏览器里框一个框、画几个箭头标个文字、叠一层 阅读详情

第1章 Javascript重点概念


前言

从本篇开始记录《Javascript重难点实例精讲》,关于Javascript系列预计从这本书出发,接着再通过《Javascript高等程序设计》和阮一峰老师的ES6教程进行完善

本篇为第1章 Javascript重点概念


1.1 JavaScript基本数据类型

1.1.1 JS数据类型基本分类

Javascript数据类型分为基本数据类型和引用数据类型

基本数据类型:

  1. undefined
  2. null
  3. boolean
  4. number
  5. string
  6. symbol(ES6 中新增)

引用数据类型(在第2和第3章再展开):

  1. object
  2. array
  3. function
  4. date等等

1.1.2 undefined类型

只有唯一字面值undefined,表示一个变量不存在

常见场景:

  1. 使用只声明而未初始化的变量时
  2. 获取一个对象的某个不存在的属性(自身属性和原型链继承属性)时
  3. 函数没有明确的返回值,却在其他地方使用了返回值时
  4. 函数定义时使用了多个形参,而在调用时传递的参数少于行参数量时

1.1.3 null类型

只有唯一字面值null,表示一个空指针对象,因此使用typeof检测null时会返回object

常见场景:

  1. 一般如果声明的变量是为了之后保存某个值,那声明时就应该赋值为null
  2. js在获取DOM元素时,如果没有获取到指定的元素对象,就会返回null
  3. 使用正则表达式进行捕获时,如果没有捕获结果,就会返回null

1.1.4 undefined和null两种类型的异同

相同点:

  • 转化为boolean类型时都转为false
  • 在需要将两者转换成对象时,都会抛出一个TypeError的异常,也就是平时最常见的引用异常
  • undefined类型派生自null类型,所以在非严格相等的情况下,两者是相等的

不同点:

  • null是JavaScript中的关键字,而undefined是JavaScript中的一个全局变量,即挂载在window对象上的一个变量,并不是关键字
  • 在使用typeof运算符检测时,undefined类型的值会返回undefined,而 null 类型的值会返回object
  • 在通过call调用toString()函数时,undefined类型的值会返回“[object Undefined]”,而null类型的值会返回“[object Null]”
  • 在需要进行字符串类型的转换时,null会转换为字符串"null",而undefined会转换 为字符串"undefined"
  • 在需要进行数值类型的转换时,undefined会转换为NaN,无法参与计算;null会转换为0,可以参与计算
  • 无论在什么情况下都没有必要将一个变量显式设置为undefined。如果需要定义某个变量来保存将来要使用的对象,应该将其初始化为null。这样不仅能将null作为空对象 指针的惯例,还有助于区分null和undefined

1.1.5 boolean类型及其他类型与boolean类型的转换

只有两个值true和false,注意这是区分大小写的,例如 True 就不行。

Boolean类型使用最多的场景就是用于if语句判断。在JavaScript中,if语句可以接受任何类型的表达式,即if(a)语句中的a,可以是Boolean、Number、String、Object、 Function、Null、Undefined中的任何类型。
如果a不是Boolean类型的值,那么JavaScript解释器会自动调用Boolean()函数对a 进行类型转换,返回最终符合if语句判断的true或者false值。

不同类型与boolean类型的转换:

  1. string -> boolean:
    • 空字符串""或者'' -> false
    • 其他的非空字符串 -> true
  2. number -> boolean:
    • 0和NaN -> false
    • 其他所有,包含无穷大infinity和无穷小-infinity -> true
  3. object -> boolean:
    • object为null -> false
    • 不为null,包括空对象{} -> true
  4. function -> boolean:
    • 任何function类型 -> true
  5. null -> boolean:
    • null类型只有一个null值,会转换为false
  6. undefined -> boolean
    • undefined类型只有一个undefined值,会转换为false

1.2 number类型详解

1.2.1 number类型介绍

js中的number类型及包括整型数据,也包括浮点型数据

基本采用是十进制,同时也可以通过八进制或十六进制,下面是具体要求和写法:

  • 八进制:首位是0,其他位必须是0~7的八进制序列。如024就是十进制20
  • 十六进制:前两位必须是0x,其他位是十六进制序列(0~9,a~f或者A~F)。如0x3f是十进制63

1.2.2 其他类型与number类型的数据转换

  1. boolean -> number:
    • true -> 1
    • false -> 0
  2. null -> number:
    • null类型只有一个字面值null,直接转换为0
  3. undefined -> number:
    • undefined类型只有一个字面值undefined,直接转换为NaN
  4. string -> number:
    • 字符串中只包含数字,会转为十进制数,如果前面有 0 或是八进制格式,会直接省略掉 0,如 0123 会转为 123
    • 只包含有效的浮点数,同样转为十进制数
    • 只包含有效的十六进制格式,会转为十进制数,如 0x3f 会转为 63
    • 空字符串,或包含多个空格,转换为0
    • 如果包含上述格式之外的字符串,转换为NaN
  5. object -> number:
    • Object类型在转换为Number类型时,会优先调用valueOf()函数,然后通过其返回值按照上述规则进行转换。如果转换的结果是NaN,则调用 toString()函数,通过其返回值重新按照上述规则进行转换;如果有确定的number类型返回值,则结束,否则返回NaN

1.2.3 number类型转换相关函数

在 JavaScript中将其他类型的值转换为number类型一共有3个函数可以完成,分别是Number()函数、parseInt()函数、parseFloat()函数

(1)Number() 函数

Number()函数可以用于将任何类型转换为Number类型,规则如上面 1.2.2 所示

例如

Number('012'); // 12

需要注意的是如果不涉及到其他类型转number类型,而是对数字进行转换,则会按照对应的进制数据格式,同义转换为十进制并返回

Number(10); // 10
Number(010); // 8,010是八进制的数据,转换成十进制是8
Number(0x10); // 16,0x10是十六进制数据,转换成十进制是16

(2)parseInt() 函数

parseInt()函数用于解析一个字符串,并返回指定的基数对应的整数值。如果该字符串无法转换成number类型,则会返回NaN

语法格式如下

parseInt(string, radix);
  • string:表示要被解析的值。如果该参数不是字符串,那么会使用toString()函数将其转换成字符串,字符串前面的空白符会被忽略。
  • radix:表示进制转换的基数,数据范围是2~36,默认值是10也就是代表十进制。因为使用不同进制会产生不同结果,因此这个值无论如何实际使用时都必须手动补充好。

有下面 5 个注意点:

1.传入参数如果是非字符串类型,需要优先转换成字符串类型,即使传入的是整型数据

parseInt('0x12', 16); // 18
parseInt(0x12, 16); // 24

第一条语句直接将字符串"0x12"转换为十六进制数,得到的结果为1×16+2=18;
第二条语句由于传入的是十六进制数,所以会先转换成十进制数18,然后转换成字符串"18",再将字符串"18"转换成十六进制数,得到的结果为1×16+8=24

2.数据截取的前置匹配原则

即从字符串的第一个字符串开始匹配,如果处于基数指定的范围,则保留并继续往后匹配满足条件的字符,直到某个字符不满足基数指定的数据范围,则从该字符开始,舍弃后面的全部字符
例如

parseInt("fg123", 16);	//15

如果遇到的字符串以"0x"开头,那按16进制处理时会正常计算,但是按十进制就会是0

parseInt('0x12',16); // 18 = 16 + 2
parseInt('0x12',10); // 0

需要注意的一点是,如果传入的字符串中涉及算术运算,则不执行,算术符号会被当作字符处理;如果传入的参数是算术运算表达式,则会先运算完成得到结果,再参与 parseInt()函数的计算

parseInt(15 * 3, 10); // 45,先运算完成得到45,再进行parseInt(45, 10)的运算
parseInt('15 * 3', 10); // 15,直接当作字符串处理,并不会进行乘法运算

3.对包含字符e的不同数据的处理差异

当传入的参数本身就是Number类型时,会将e按照科学计数法计算后转换成字符串,然后按照对应的基数转换得到最终的结果。

如果传入的字符串中直接包含e,那么并不会按照科学计数法处理,而是会判断字符 e是否处在可处理的进制范围内,如果不在则直接忽略,如果在则转换成对应的进制数。

parseInt(6e3, 10); // 6000,相当于parseInt('6000', 10)
parseInt(6e3, 16); // 24576,相当于parseInt('6000', 16)
parseInt('6e3', 10); // 6,相当于parseInt('6', 10)
parseInt('6e3', 16); // 1763,相当于6×256 +14×16 + 3 = 1763

4.对浮点型数的处理

如果传入的值是浮点型数,则会忽略小数点及后面的数,直接取整

parseInt('6.01', 10); // 6 
parseInt('6.99', 10); // 6

5.map()函数与parseInt()函数的隐形坑

比如想把数组中每个number类型的字符串元素都转为整数,如果使用下面方法就会出问题

var arr = ['1', '2', '3', '4'];
var result = arr.map(parseInt);
console.log(result);

因为

arr.map(parseInt);
实际与下面这个等效
arr.map(function (val, index) { 
	return parseInt(val, index);
});

实际处理过程是这样

parseInt('1', 0); // 1 
parseInt('2', 1); // NaN 
parseInt('3', 2); // NaN 
parseInt('4', 3); // NaN

因此在map()函数中使用parseInt()函数时需要注意这一点,不能直接将 parseInt()函数作为map()函数的参数,而是需要在map()函数的回调函数中使用,并尽量指定基数,代码如下所示


var arr = ['1', '2', '3', '4'];
var result = arr.map(function (val) {
	return parseInt(val, 10);
});
console.log(result); // [1, 2, 3, 4]

(3)parseFloat() 函数

parseFloat()函数用于解析一个字符串,并返回对应的浮点数。如果给定值不能转换为数值,则会返回NaN。parseFloat()函数没有进制的概念。

有下面 4 条注意点:
1.如果在解析过程中遇到了正负号(+ / -)、数字0~9、小数点或者科学计数法 (e / E)以外的字符,则会忽略从该字符开始至结束的所有字符,然后返回当前已经解析的字符的浮点数形式。其中,正负号必须出现在字符的第一位,而且不能连续出现

parseFloat('+1.2'); // 1.2
parseFloat('-1.2'); // -1.2
parseFloat('++1.2'); // NaN,符号不能连续出现 
parseFloat('--1.2'); // NaN,符号不能连续出现 
parseFloat('1+1.2'); // 1,'+'出现在第二位,不会当作符号位处理

2.字符串前面的空白符会直接忽略,如果第一个字符就无法解析,则会直接返回NaN

parseFloat(' 1.2'); // 1.2
parseFloat('f1.2'); // NaN

3.对于字符串中出现的合法科学运算符e,进行运算处理后会转换成浮点型数。这点与parseInt()函数的处理有很大的不同。

parseFloat('4e3'); // 4000
parseInt('4e3', 10); // 4

4.对于小数点,只能正确匹配第一个,第二个小数点是无效的,它后面的字符也都将被忽略

parseFloat('11.20'); // 11.2
parseFloat('11.2.1'); // 11.2

(4)小结

  • Number()函数转换的是传入的整个值,并不是像parseInt()函数和parseFloat()函数一样会从首位开始匹配符合条件的值。如果整个值不能被完整转换,则会返回NaN。
  • parseFloat()函数在解析小数点时,会将第一个小数点当作有效字符,而parseInt() 函数在解析时如果遇到小数点会直接停止,因为小数点不是整数的一部分。
  • parseFloat()函数在解析时没有进制的概念,而parseInt()函数在解析时会依赖于传入的基数做数值转换。

1.2.4 isNaN()函数与Number.isNaN()函数对比

NaN存在的目的:在某些异常情况下保证程序的正常执行。

NaN的 2 个特点:任何涉及NaN的操作都会返回NaN;NaN与任何值都不相等,包括与NaN本身

判断NaN时,ES5提供了isNaN()函数,ES6为Number类型增加了静态函数isNaN()函数----Number.isNaN()

(1)isNaN()函数

传递的参数如果是number类型数据那很容易判断。

如果传递的参数不是number类型,就比较麻烦。因为isNaN()函数会进行数据的类型转换,它在处理的时候会去判断传入的变量值能否转换为数字,如果能转换成数字则会返回false,如果无法转换则会返回true。

NaN的产生条件:

  1. 数据运算时返回了一个无法表示的数值,例如0 / 0(其他数除以0会返回infinity)
  2. 需要做强制类型转换时,某些数据不能直接转换为数值类型,就会返回NaN,例如1 - 'a' = NaN

下面是一些示例

isNaN(NaN); // true 
isNaN(undefined); // true
isNaN({});	//true
isNaN("JavaScript"); // true,字符串"JavaScript"无法转换成数字 

isNaN(true); //false,Number(true)会转换成数字1
isNaN(null); //false,Number(null)会转换成数字0
isNaN(1); //false
isNaN(''); //false,Number('')会转换成数字0
isNaN("1"); //false,Number("1")会转换成数字1

// Date类型
isNaN(new Date()); // false 
isNaN(new Date().toString()); // true

其中Date类型比较特殊,当我们调用new Date()函数生成的实例并转换为数值类型时,会转换为对应的时间戳,例如下面的代码

Number(new Date()); //1543333199705

因此isNaN(new Date())会返回false
而当我们调用了toString()函数时,返回的是一串字符串表示的时间,无法转换成数值类型,因此isNaN(new Date().toString())会返回true

(2)Number.isNaN()函数

ES6中的Number.isNaN()函数会在真正意义上去判断变量是否为NaN,不会做数据类型转换。只有在传入的值为NaN时,才会返回true,传入其他任何类型的值时会返回false

(3)小结

isNaN()函数与Number.isNaN()函数的差别如下:

  • isNaN()函数在判断是否为NaN时,需要先进行数据类型转换,只有在无法转换为数字时才会返回true
  • Number.isNaN()函数在判断是否为NaN时,只需要判断传入的值是否为NaN,并不会进行数据类型转换

1.2.5 浮点型运算

(1)浮点型运算不准问题

在JavaScript中,整数和浮点数都属于Number类型,它们都统一采用64位浮点数进行存储。

虽然它们存储数据的方式是一致的,但是在进行数值运算时,却会表现出明显的差异
性。整数参与运算时,得到的结果往往会和我们所想的一样;而对于浮点型运算,有时却会出现一些意想不到的结果

例如

0.1 + 0.2 = 0.30000000000000004
0.7 * 180 = 125.99999999999999

具体原因涉及到计算机中的存储和计算方式,比较复杂而且感觉没必要了解特别清楚就先不在这里赘述

(2)解决方案

主要思路是将浮点数先乘以一定的数值转换为整数,通过整数进行运算,然后将结果除以相同的数值转换成浮点数后返回

比如 2.1和 3.15 做运算,选其中小数点后位数更多的作为乘数因子,例如 2.1 就是10,3.15就是100,那么就选100。随后两者同时乘以最大乘数因子后再进行运算,最后再除回去


1.3 String类型详解

JavaScript中的String类型(字符串类型)既可以通过双引号""表示,也可以通过单引号''表示,而且是完全等效的

在程序处理时,我们同样不可避免地会遇到将其他类型转换为String类型的场景。如果是引用类型的数据,则在转换时总是会调用toString()函数,得到不同类型值的字符串表示;如果是基本数据类型,则会直接将字面值转换为字符串表示形式。

在将某个数据转换为字符串时,有一个简单的方法是直接使用加号+拼接一个空字符串""

console.log(123 + ''); // '123'
console.log([1, 2, 3] + ''); // '1,2,3'
console.log(true + ''); // 'true'

1.3.1 String类型的定义与调用

JS中有3种定义字符串的方式,分别是字符串字面量;直接调用String() 函数;new String()构造函数

(1)字符串字面量

字符串字面量就是直接通过单引号或者双引号定义字符串的方式

var str = 'hello JavaScript'; // 正确写法
var str2 = "hello html"; // 正确写法

(2)直接调用String()函数

直接调用String()函数,会将传入的任何类型的值转换成字符串类型

转换规则如下:

1.如果是Number类型的值,则直接转换成对应的字符串

String(123); // '123' 
String(123.12); // '123.12'

2.如果是Boolean类型的值,则直接转换成true或者false

String(true); // 'true' 
String(false); // 'false'

3.如果值为null,则返回字符串null

String(null); // 'null'

4.如果值为undefined,则返回字符串undefined

String(undefined); // 'undefined'

5.如果值为字符串,则直接返回字符串本身

String('this is a string'); // 'this is a string'

6.如果值为引用类型,则会先调用toString()函数获取返回值,将返回值按照上述步骤1~5判断能否转换字符串类型。如果都不满足,则会调用对象的valueOf()函数获取返回值,并将返回值重新按照步骤1~5判断能否转换成字符串类型,如果也不满足,则会抛出类型转换的异常

(3)new String()构造函数

new String()构造函数使用new运算符生成String类型的实例。

对于传入的参数同样采用和上述String()函数一样的类型转换策略,最后的返回值是一个String类型对象的实例。

new String('hello JavaScript'); // String {"hello JavaScript"}

(4)三者在做比较时的区别

使用第一种字符串字面量方式和第二种直接调用String()函数的方式得到的字符串都是基本字符串;而通过第三种方式,new运算符生成的字符串是字符串对象

基本字符串在作比较时,只需要比较字符串的值即可;
而在比较字符串对象时,比较的是对象所在的地址。

var str2 = String(str);
var str3 = String('hello');
var str4 = new String(str); 
var str5 = new String(str); 
var str6 = new String('hello');
str === str2; // true 
str2 === str3; // true 
str3 === str4; // false 
str4 === str5; // false 
str5 === str6; // false

(5)函数的调用

在String对象的原型链上有一系列的函数,例如indexOf()函数、substring()函数、slice()函数等,通过String对象的实例可以调用这些函数做字符串的处理。

但是我们发现,采用字面量方式定义的字符串没有通过new运算符生成String对象的实例也能够直接调用原型链上的函数

'hello'.indexOf('e'); // 1
'hello'.substring(1); // 'ello'
'hello'.slice(1); // 'ello'

这是因为实际上基本字符串本身是没有字符串对象的函数,而在基本字符串调用字符串对象才有的函数时,JavaScript会自动将基本字符串转换为字符串对象,形成一种包装类型,这样基本字符串就可以正常调用字符串对象的方法了。

eval()函数会将基本字符串作为源代码处理,如果涉及表达式会直接进行运算,返回运算后的结果;而字符串对象则会被看作对象处理,返回对象本身

var s1 = '2 + 2'; // 创建一个字符串字面量
var s2 = new String('2 + 2'); // 创建一个对象字符串 
console.log(eval(s1)); // 4
console.log(eval(s2)); // String {"2 + 2"}

1.3.2 String类型常见算法

关于算法相关还是主要想参考《代码随想录》进行学习,因此此部分只考虑记录一下相关题目,并不记录具体分析和解决过程。

等后面专门复习算法部分时候,可以再顺便参考原书过一遍这块的相关解法。

(1)字符串逆序输出

(2)统计字符串中出现次数最多的字符及出现的次数

(3)去除字符串中重复的字符

(4)判断一个字符串是否为回文字符串


1.4 运算符

在JavaScript中描述了一组用于操作数据值的运算符,包括算术运算符(+、-)、关系运算符(>、<)、等于运算符(==、===)、位运算符(与&&、 或||、非!)等。

由于JavaScript是弱类型语言,因此在运算符的使用上更加灵活,接下来就对其中比较重要的一些运算符进行详细的讲解。

1.4.1 等于运算符

  • 双等于运算符==在比较时,会将两端的变量进行隐式类型转换,然后比较值的大小
  • 三等于运算符===在比较时,会优先比较数据类型,数据类型相同才去判断值的大小, 如果类型不同则直接返回false

(1)三等于运算符

基本规则如上面所说。

额外需要注意的是:

  • 如果参与比较的值中有任何一方为NaN,则直接返回false,所以两个NaN也不能三等于
  • 如果比较的值都是null或者undefined,则返回true;如果只有一方为null或者undefined,则返回false
  • 如果比较的值都是引用类型,则比较的是引用类型的地址,当两个引用指向同一个地址时,则返回true,否则返回false
var a = []; var b = a;
var c = [];
console.log(a === b); // true
console.log(a === c); // false
console.log({} === {}); // false

实际上,如果不是通过赋值运算符=将定义的引用类型的值赋予变量,那么引用类型的值在比较后都会返回false,所以我们会发现空数组或者空对象的直接比较返回的是false

[] === []; // false 
{} === {}; // false

(2)双等于运算符

因为它不区分数据类型,而且会做隐式类型转换,所以要稍微复杂些。

下面是一些规则

  1. 如果比较的值类型相同,则采用与三等于运算符一样的规则

  2. 如果比较的值类型不同,则会按照下面的规则进行转换后再进行比较

    • 如果比较的一方是null或者undefined,只有在另一方是null或者undefined的情况下才返回true,否则返回false
    • 如果比较的是字符串和数值类型数据,则会将字符串转换为数值后再进行比较,如果转换后的数值相等则返回true,否则返回false。(需要注意的是,如果字符串是十六进制的数据,会转换为十进制后再进行比较。字符串并不支持八进制的数据,如果字符串以0开头,则0会直接省略,后面的值当作十进制返回)
    • 如果其中一个值是对象类型,另一个值是基本数据类型或者对象类型,则会调用对象的valueOf()函数或者toString()函数,将其转换成基本数据类型后再作比较

1.4.2 typeof运算符

(1)基本使用

typeof运算符用于返回操作数的数据类型,有以下两种使用形式

1. typeof operand
2. typeof(operand)

operand:表示需要返回数据类型的操作数,可以是引用类型,也可以是基本数据类型。

括号有的时候是必须的,如果不加上括号将会因为优先级的问题得不到我们想要的结果。

下面是typeof运算符在处理不同数据类型时得到的不同结果

类型结果
Undefined“undefined”
Null“object”
Boolean“boolean”
Number“number”
String“string”
Symbol (ES6新增)“symbol”
函数对象“function”
任何其他对象“object”

不止是Undefined类型,一共有3种值会返回undefined:

  1. 未声明的变量
  2. 已声明但未初始化的变量
  3. undefined本身

NaN也是number类型的值,因此typeof(NaN) === 'number'

对于Function类型的数据,可以概括为以下这些值,typeof运算符在处理时会返回function:

  • 函数的定义,包括函数声明或者函数表达式两种形式
  • 使用class关键字定义的类(class是在ES6中新增的关键字,它不是一个全新的概念,原理依旧是原型继承,本质上仍然是一个Function)
  • 某些内置对象的特定函数,例如Math.sin()函数、Number.isNaN()函数等
  • Function类型对象的实例,一般通过new关键字得到,例如typeof new Function() === 'function'

对于Object类型的数据,可以概括为以下这些值,typeof运算符在处理时会返回object:

  • 对象字面量形式。例如{name: 'kingx'}
  • 数组。例如[1, 2, 3]和Array(1, 2, 3)
  • 所有构造函数通过new操作符实例化后得到的对象,例如new Date()、new function(){},但是new Function(){}除外
  • 通过new操作符得到的基本数据类型的包装类型对象,如new Boolean(true)、new Number(1),但不推荐这么写

(2)使用typeof需要注意的问题

typeof运算符的使用在绝大部分情况下都是安全的,但是在ES6以后情况就不一样了。大致有下面 3 种问题需要在使用typeof运算符时进行考虑:

  1. typeof运算符会区分对待Object类型和Function类型
  2. typeof运算符对null的处理(会是object)
  3. typeof运算符相关语法的括号(有时括号是必要的,涉及处理优先级问题)

1.4.3 逗号运算符

逗号基本有两种用途:

  1. 函数传递多个参数时使用逗号分隔
  2. 作为运算符将多个表达式连接起来

对于第二种用途,如下所示

x = 8 * 2, x * 4

这是一个使用了逗号运算符的语句,首先执行左边的部分,x = 8×2,即x = 16,然后执行右边的语句,x×4 = 16×4 = 64,并将其返回。如果将整个语句赋值给一个 变量y,则该变量y的值为64。

(1)在for循环中批量执行表达式

比如:

for (var i = 0, j = 10; i < 10, j < 20; i++, j++) 
	{ console.log(i, j);
}

(2)用于交换变量,无须额外变量

需要交换两个变量值时的常规做法如下:


var a = 'a'; 
var b = 'b';
var c;
c = a; 
a = b; 
b = c;

如果不使用额外的变量储存,那么下面有 2 种通过,实现的方案:

var a = 'a'; 
var b = 'b';
// 方案1
a = [b, b = a][0]; 
// 方案2
a = [b][b = a, 0];

在方案1中,前一部分[b, b = a]是一个一维数组,数组第二项值是b = a,实际会将 a值赋给b,然后返回“‘a’”,因此数组最终的值为[‘b’, ‘a’],然后取索引0的值为’b’,赋 给变量a,最终实现a = ‘b’, b = ‘a’。

在方案2中,前一部分[b]是一个一维数组,后一部分[b = a, 0],实际会先执行b = a,将a值赋给b,然后返回“0”,因此后一部分实际是修改了b的值并返回索引“0”, 最终是a = [b][0],即a = b,实现了a与b的交换。

(3)用于简化代码

比如

if (x) { 
	foo();
	return bar(); 
} else {
return 1; 
}

使用,简化后

x ? (foo(), bar()) : 1;

(4)用小括号保证逗号运算符的优先级

在所有的运算符中,逗号运算符的优先级是最低的,因此对于某些涉及优先级的问题,我们需要使用到小括号,将含有逗号运算符的表达式括起来。

例如

var a = 20; 
var b = ++a, 10;
console.log(b); // Uncaught SyntaxError: Unexpected number

修改为

var a = 20;
var b = (++a, 10); 
console.log(b); // 10

1.4.4 运算符优先级

优先级决定了表达式在执行时的先后顺序,其中优先级最高的最先执行,优先级最低的最后执行。

首先可以先列出所有运算符类型,如下所示

1. 一元运算符 (new、delete、typeof、void) 
2. 乘除法运算符 (*、/、%、++) 
3. 加减法运算符 (+、-) 
4. 关系运算符 (<、>、<=、>=、instanceof) 
5. 相等运算符 (==、!=、===、!==) 
6. 逻辑运算符 (&&、||、!) 
7. 条件运算符 (?:) 
8. 赋值运算符 (=、+=、-=、*=、/=、%=、<<=、>>=、>>>=) 
9. 逗号运算符 (,)

优先级顺序为:

一元运算符 > 乘除法运算符 > 加减法运算符 > 关系运算符 > 相等运算符 > 逻辑运算符 > 条件运算符 > 赋值运算符 > 逗号运算符


1.5 toString()函数与valueOf()函数

在JavaScript中,toString()函数与valueOf()函数解决的是值的显示和运算的问题, 所有引用类型都拥有这两个函数。

1.5.1 两种函数基本知识

(1)toString()函数

toString()函数的作用是把一个逻辑值转换为字符串,并返回结果。

Object类型数据的toString()函数默认的返回结果是[object Object],当我们自定义新的类时,可以重写toString()函数,返回可读性更高的结果。

在JavaScript中,Array,Function,Date等类型都实现了自定义的toString()函数:

  • Array的toString()函数返回值为以逗号分隔构成的数组成员字符串,例如[1,2,3].toString()结果为字符串’1,2,3’
  • Function的toString()函数返回值为函数的文本定义,例如(function(x){return x*2;}).toString()的结果为字符串"function(x){return x * 2;}"
  • Date的toString()函数返回值为具有可读性的时间字符串,例如,new Date().toString()的结果为字符串"Sun Nov 25 2018 15:00:16 GMT+0800 (中国标准时间)"

(2)valueOf()函数

valueOf()函数的作用是返回最适合引用类型的原始值,如果没有原始值,则会返回引用类型自身。

Object类型数据的valueOf()函数默认的返回结果是"{}",即一个空的对象字面量。

对于Array、Function、Date等类型,valueOf()函数的返回值:

  • Array的valueOf()函数返回的是数组本身,例如[1, 2, 3].valueOf()返回的结果为“[1,2,3]”
  • Function的valueOf()函数返回的是函数本身,例如(function(x){return x * 2;}).valueOf()返回的结果为函数本身“function(x){return x * 2;}”
  • Date的valueOf()函数返回的是指定日期的时间戳,例如new Date().valueOf()返回 的结果为“1543130166771”

1.5.2 两个函数同时存在时做隐式转换的选择

如果一个引用类型的值既存在toString()函数又存在valueOf()函数,那么在做隐式转换时,会调用哪个函数呢?

这里我们可以概括成 2 种场景,分别是引用类型转换为String类型,以及引用类型转换为Number类型。

(1)引用类型转换为String类型

一个引用类型的数据在转换为String类型时,一般是用于数据展示,转换时遵循以下规则:

  1. 如果对象具有toString()函数,则会优先调用toString()函数。如果它返回的是一个原始值,则会直接将这个原始值转换为字符串表示,并返回该字符
  2. 如果对象没有toString()函数,或者toString()函数返回的不是一个原始值,则会再去调用valueOf()函数,如果valueOf()函数返回的结果是一个原始值,则会将这个结果转换为字符串表示,并返回该字符串
  3. 如果通过toString()函数或者valueOf()函数都无法获得一个原始值,则会直接抛出类型转换异常
(2)引用类型转换为Number类型

一个引用类型的数据在转换为Number类型时,一般是用于数据运算,转换时遵循以 下规则:

  1. 如果对象具有valueOf()函数,则会优先调用valueOf()函数,如果valueOf()函数返回一个原始值,则会直接将这个原始值转换为数字表示,并返回该数字
  2. 如果对象没有valueOf()函数,或者valueOf()函数返回的不是原生数据类型,则会再去调用toString()函数,如果toString()函数返回的结果是一个原始值,则会将这个结果转换为数字表示,并返回该数字
  3. 如果通过toString()函数或者valueOf()函数都无法获得一个原始值,则会直接抛出 类型转换异常

事实上,对除了Date类型以外的引用类型数据转换为原生数据类型时,如果是用于数据运算,则会优先调用valueOf()函数,在valueOf()函数无法满足条件时,则会继续调用toString()函数,如果toString()函数也无法满足条件,则会抛出类型转换异常。

如果是用于数据展示,则会优先调用toString()函数,在toString()函数无法满足条 件时,则会继续调用valueOf()函数,如果valueOf()函数也无法满足条件,则会抛出类型 转换异常。


1.6 JavaScript中常用的判空方法

一个便捷判断变量是否为空的方法是,对变量取反,然后判断是否为true

if(!x){}

这是一个便捷判断变量是否为空的方法,但是其涉及的场景却很多,这里我们就分多种情况来讨论变量判空的方法:

(1)判断变量为空对象

a. 判断变量为null或者undefined

判断一个变量是否为空时,可以直接将变量与null或者undefined相比较,需要注意 双等于==和三等于===的区别

if(obj == null) {} // 可以判断null或者undefined 
if(obj === undefined) {} // 只能判断undefined

b. 判断变量为空对象{}

判断一个变量是否为空对象时,可以通过for...in语句遍历变量的属性,然后调用 hasOwnProperty()函数,判断是否有自身存在的属性,如果存在则不为空对象,如果不存在自身的属性(不包括继承的属性),那么变量为空对象

// 判断变量为空 
function isEmpty(obj) {
	for(let key in obj) { 
		if(obj.hasOwnProperty(key)) {
			return false; 
		}
	}
	return true;
}

(2)判断变量为空数组

判断变量是否为空数组时,首先需要判断变量是否为数组,然后通过数组的length 属性确定。当以上两个条件都满足时,变量是一个空数组。

arr instanceof Array && arr.length === 0

(3)判断变量为空字符串

判断变量是否为空字符串时,可以直接将其与空字符串相比较,或者调用trim()函数去掉前后的空格,然后判断字符串的长度。当满足以上两个条件中任意一个时,变量是一个空字符串。

str == '' || str.trim().length == 0;

(4)判断变量为0或者NaN

当一个变量为Number类型时,判空即判断变量是否为0或者NaN,因为NaN与任何值比较都为false,所以我们可以通过取非运算符完成。

!(Number(num) && num) == true;

(5)!x == true的所有情况

本小节一开始就讲到 !x 为true时,会包含很多种情况,这里我们一起来总结下:

  • 变量为null
  • 变量为undefined
  • 变量为空字符串' '
  • 变量为数字0,包括+0、-0
  • 变量为NaN

1.7 JavaScript中的switch语句

基本语法

switch(expression) { 
	case value1:
		statement1;
		break; 
	case value2:
		statement2;
		break; 
	default:
		statement; 
}

JavaScript中的switch特殊的点在于,switch语句可以用来判断任何类型的值,不一定是Number类型。

要注意JavaScript中对于case的比较是采用严格相等===的!

蓝桥杯单片机开发实战:Keil环境配置与IAP15F2K61S2头文件设置详解 本文详细讲解了为蓝桥杯单片机竞赛配置Keil开发环境的关键步骤,重点解析了IAP15F2K61S2专用头文件的获取与设置方法。通过使用STC-ISP工具添加芯片型号和头文件,确保编译器能正确识别寄存器,是成功进行单片机编程和竞赛备赛的基础。文中还提供了从软件安装、工程创建到常见问题排查的完整实战指南。 阅读详情

相关推荐

哈密顿-雅可比不等式:连接最优控制与优化算法的自然物理原理

在控制理论与优化算法之间,存在一个深刻而实用的桥梁。最优控制理论旨在通过设计控制输入,使动态系统在时间维度上达到性能最优,其核心数学工具如庞特里亚金极大值原理和哈密顿-雅可比-贝尔曼方程,为解决连续时间系统的优化问题提供了优雅框架。然而,这些方法在高维非凸问题中面临求解难题。哈密顿-雅可比不等式作为一种松弛方法,通过构造李雅普诺夫函数并验证一个不等式条件,转而寻求性能有保障且易于实现的次优解,其本质是保证系统能量衰减和性能上界。这一原理与耗散性理论紧密相连,体现了自然物理系统(如阻尼振动)中能量耗散的普遍规

weixin_30582943的博客 313

JavaScript重难点实例精讲

我们可能会想到在Array的map()函数中调用parseInt()函数。像下面这样: 但是在运行后,得到的结果是[1, NaN, NaN, NaN],与我们期望的结果[1, 2, 3, 4]差别很大,其实这就是一个藏在map()函数与parseInt()函数中的隐形坑。 上面的代码实际与下面的代码等效: parseInt()函数接收的第二个参数实际为数组的索引值,所以实际处理的过程如下所示。 任何整数以0为基数取整时,都会返回本身,所以第一行代码会返回“1”。第二行代码parseInt(‘2’, 1),因

weixin_43867717的博客 909

手把手教你用nRF52832+RFX2401C设计远距离蓝牙模块(附完整原理图/PCB文件)

本文详细介绍了如何利用nRF52832 SoC与RFX2401C射频前端芯片设计远距离蓝牙模块,涵盖芯片选型、阻抗匹配、PCB布局等关键环节。通过实战指南和完整原理图/PCB文件,帮助工程师实现输出功率可达20dBm的蓝牙模块设计,适用于智能家居、运动追踪等物联网应用。

weixin_29214199的博客 287

eclipse javascript_重磅!《JavaScript重难点实例精讲》书籍问世

1、书籍问世两年的坚持,个人编写的JavaScript进阶书籍《JavaScript重难点实例精讲》终于问世了。回想起两年前,当人民邮电出版社的编辑找到我,与我沟通有没有出书的打算时,其实也没有思考多久,我就答应下来了。因为我一直有在头条上坚持写文章,内容都是自己的工作心得,这些心得都是对实际问题的记录和总结,其中也不乏在工作过程中所踩的一些坑,所以在写作的素材上我是不缺乏的。如果我能将自己的经验...

weixin_39710660的博客 406

炸金花游戏(3)--基于EV(期望收益)的简单AI模型

前言:   炸金花这款游戏, 从技术的角度来说, 比德州差了很多. 所以他的AI模型也相对简单一些. 本文从EV(期望收益)的角度, 来尝试构建一个简单的炸金花AI. 相关文章:  德州扑克AI--Programming Poker AI(译).  系列文章说来惭愧, 之前一直叫嚷着写德州AI, 不过可惜懒癌晚期, 一直没去实践, T_T. 相比而言,***简单很多, 也更偏重于运...

weixin_30377461的博客 2341

蓝牙Channel Sounding为真正的距离感知赋能

1:工作原理蓝牙™信道探测使用两种经过验证的测距方法——PBR(PBR)和RTT(RTT),实现蓝牙设备之间的安全精密测距。

weixin_43720159的博客 807

【项目推荐】基于 SpringBoot3 + Spring AI 的游泳馆管理系统:Agent + RAG + Function Calling 一次学全

它不是玩具 Demo,而是一个「业务 + AI」双主线交织的真实系统——预约业务在跑,AI 客服在旁边实时查库作答,两条线通过 Function Calling、RAG、站内消息串成一体。面试官问「你做过什么 AI 项目」时,你能从业务场景讲到架构取舍,从 RAG 切分参数讲到降级兜底——这和「我调了一下 ChatGPT 的 API」是完全不同的两个段位。项目已包含完整的建表 SQL、初始化数据、部署文档,克隆下来就能跑。获取方式:见文末说明 / 后台回复关键词获取。—— 完 ——

深耕开发,专注编程实战。分享源码解析、项目搭建、问题排查笔记,沉淀技术,持续更新。 347

Cluster Computing投稿

在overleaf中新建picins.sty文件,该文件是导入的包在.tex文件中添加以下内容\usepackage{picins}%实现作者简介%下边这块有几个人复制几段\vspace{0\baselineskip} %这一行控制作者间隔,可视情况改数字!!!!!!!!

这里是萤石ym的学习路途,努力搬砖! 5534

Web 版翻译者资料库

本项目是桌面版《翻译者资料库》的 Web 移植版,基于 Spring Boot 3.3、Thymeleaf、Bootstrap 5.3 与 MySQL 8.0 构建。它面向翻译写作提供资料、词汇、句库、文本工具、对照制作、查找、备份恢复及旧库迁移等功能,完整继承桌面版核心业务规则,支持一键启动与离线访问。

howard2005的专栏 140

【YOLO 系列】基于YOLO V8的智能车载交通标志识别系统【python源码+Pyqt5界面+数据集+训练代码】

本文详细介绍了基于YOLO V8构建智能车载交通标志识别系统的完整流程。从环境搭建、数据集准备、模型训练到PyQt5图形界面开发,提供了Python源码和实战代码,帮助开发者快速实现高精度、实时的交通标志检测应用,提升驾驶辅助系统的开发效率。

wdx012345的博客 966

RK3566/RK3568 Android 11 无操作自动隐藏导航栏、底部上拉显示导航栏

RK3566/RK3568 Android 11 定制大全在系统服务中增加无操作自动隐藏导航栏方法,在上层app动态调用无操作自动隐藏导航栏方法,系统会在5秒无操作后自动隐藏导航栏,隐藏导航栏后从底部上拉可显示导航栏,设备关机和重启后也能继续生效。

qq_23921815的博客 1260

2026小红书小工具怎么制作:纯前端 ZIP 打包、沙箱限制与笔记挂载流程

小红书小工具制作的核心不是复杂开发,而是把需求压缩成纯前端轻交互,并按平台要求完成 ZIP 交付。适合做的方向是测试、计算器、生成器、学习小测和记录工具;不适合做的方向是账号体系、云端数据、支付和远程接口。制作时先确认沙箱限制,再整理输入、计算、输出链路,最后通过 Builder Hub 提交审核并挂载笔记。在需求简单、素材较少的情况下,十几分钟做出一个可用的小工具是现实路径,袋马就是一个选择。

qq_39308835的博客 286

46、大数据助力SDN优化流量管理与控制器

本文探讨了大数据在软件定义网络(SDN)中的应用,重点分析了如何利用大数据技术优化SDN控制器的可扩展性和弹性。通过介绍SDN控制器的核心功能及其面临的挑战,提出了集中式、分布式、混合式和分层式控制器放置策略,并结合大数据分析系统架构与技术,详细阐述了在控制器放置优化、流聚合和备份路径识别等场景中的实际应用。文章还给出了大数据分析在SDN控制器优化中的操作流程与优势,展望了未来结合人工智能与物联网的发展方向,强调了数据安全与算法效率的重要性。

xx56789的博客 86

Agent 的 ReAct、Plan、Execute、Reflection 模式

本文系统解析 Agent 的 ReAct、Plan、Execute、Reflection 四模式,揭示其核心为“下一步谁定”与“错了怎么发现”两大问题。ReAct 逐步决策,适合动态环境但易冗余;Plan 一次性规划,高效却惧变化;Execute 严格落地,需明确判据与失败策略;Reflection 事后校验,强化纠错闭环。四者协同:外层规划,执行中嵌入思考,每步后反思,连续失败才重规划,构建稳定可控的智能体工作流。

2401_89855466的博客 325

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

海外平台频繁弹验证?从 DNS、WebRTC 到浏览器指纹的环境异常排查清单

在运营多地区海外平台账号时,即使代理节点通畅、TCP 握手成功,仍可能遇到频繁人机验证、地区限制、播放失败等异常。核心结论是:这类问题往往不在网络传输层,而在浏览器环境参数与网络出口之间的逻辑自洽性。本文从 DNS 泄漏、WebRTC 穿透、时区与语言匹配、Canvas/WebGL 指纹、TLS 特征五个维度,给出一份可复现的环境异常排查清单,并对比个人手动配置与工程化环境管理方案的适用边界。适用于跨境运营、多账号运维团队。

P1Browser【p1指纹浏览器】的博客 183

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

信号链电路怎么选运放?奥特四款信号链运放解析【选型参考】

在模拟电路设计中,运放作为信号链的“第一道门槛”,其微小误差往往决定了整个系统的成败。

naNa_1120的博客 191

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

智能问诊的流程

本文介绍基于FastAPI构建的医疗多Agent系统MediAgent,通过症状分析、诊断推理与治疗建议等模块协同,实现从问诊到报告生成的完整工作流。系统采用前后端分离架构,结合MySQL、RabbitMQ与Docker实现工程化部署,支持异步化验结果接入。虽当前状态暂存于内存,但已具备可扩展性,未来可通过Redis持久化、消息队列联动及医疗安全机制完善,应用于慢病管理、辅助决策等场景,推动AI在医疗流程中的落地应用。

Joey 的工作空间~ 724

流式输出 SSE:实现 AI 对话打字机效果后端开发

摘要解决传统 HTTP 请求在 LLM 长文本生成时导致的客户端长时间阻塞与 TTFT 极高的问题。适用于需为 AI 应用提供打字机效果、优化交互体验的后端开发工程师。读完可获得基于 FastAPI 与 SSE(Server-Sent Events)技术的流式响应实现代码、协议规范及生产环境避坑指南。关键词SSE、流式输出、FastAPI、打字机效果、TTFT、大模型推理背景与问题大模型(LLM)生成完整回复通常耗时数秒至数十秒,传统请求-响应模型导致用户界面长时间无响应,体验极差。

2402_85729970的博客 178

Vue-Router

本文详解前端路由原理与实现,以网易云音乐为例,介绍通过 vue-router 实现页面无刷新切换。核心步骤包括:安装模块、创建路由实例、注册应用、配置页面组件与路由出口。使用 router-link 实现声明式导航,结合 query 和 params 传参,支持模糊与精确匹配激活状态。强调 useRoute(读取参数)与 useRouter(执行跳转)的区别,帮助开发者掌握单页应用路由的核心机制。

2301_79245606的博客 342

基于 YOLOv8 的森林火灾检测系统(Web 版)

本项目面向林区早期火情识别场景,使用YOLOv8完成火焰(Fire)与烟雾(Smoke)两类目标的检测模型训练,并以FastAPI构建 Web 检测平台,把模型能力封装为可直接登录使用的系统。平台除常规的图片、批量、视频、摄像头检测外,还提供同图多权重对比、数据集浏览与标注核对、模型热切换,以及把训练搬到网页上的能力:可在浏览器配置超参数发起真实训练,训练中在线调整学习率、查看进度与动态曲线,训练完成后下载产物或直接应用到检测。

Michael_Jay的博客 60

身份验证漏洞专题(三):持久登录、密码重置与密码修改中的凭据绑定缺陷

密码登录与双因素认证是身份验证的主干流程,但一个完整的认证体系还依赖三类辅助机制:持久登录("记住我")、密码重置、密码修改。这三类机制的共同特征是:它们同样携带或验证凭据,同样直接决定认证状态的归属,但实现时获得的防护强度往往远低于登录页面本身。攻击者绕不开登录接口时,常常可以从这些辅助入口进入。

2401_85321170的博客 629

二进制木马免杀-Python 分离免杀与 Web 挂马

二进制木马免杀-Python 分离免杀与 Web 挂马

2503_94637398的博客 268

Vue 前端开发 AI 对话页面:流式消息渲染、文件上传

关键点是不能手动设置 Content-Type 为 multipart/form-data,必须由浏览器自动生成带有 boundary 的请求头,否则后端无法解析。思路:将对象序列化为 JSON 字符串,作为 FormData 的一个普通字段(如 formData.append(‘payload’, JSON.stringify(data))),后端接收该字段后再反序列化。若上传大文件(>10MB),需实现独立的分片上传接口,在提交对话前先获取文件 id 或 url,再随文本发送引用。移除输入框的禁用态;

2402_85729970的博客 387

在Open-WebUI中提升向量化速度

让Open-WebUI AI聊天的向量化过程不超过10秒,而不是默认的20+分钟……

xyzzklk的博客 573

Writeup 4 0xGame 2021 · command (WEB)

前端限制不可信:永远不要相信前端的任何限制(如disabled按钮、JS 验证等)。攻击者可以直接构造 HTTP 数据包绕过前端校验,所有的安全限制必须在后端实现。命令执行/注入(RCE):Web 应用如果直接调用系统命令且未对输入进行严格过滤,攻击者可以利用;||等符号拼接恶意命令。绕过技巧:在 CTF 中,如果遇到命令执行被过滤,常见的绕过方式包括:${IFS}$IFS$9等。。cat /fla?或cat /fl*。Linux 基础。

hengdonghui的博客 131

vue3 markdown编辑框与压缩包文件上传及预览

el-input v-model="form.author" placeholder="作者/组织" maxlength="50" clearable /><el-select v-model="form.category" placeholder="请选择标签" class="full-width"><el-select v-model="form.license" placeholder="请选择" class="full-width">'编辑技能' : '新建技能' }}</div>

每一篇博客都是对自己技术的沉淀 174
上一篇: 前端学习记录~2023.7.18~CSS杂记 Day10 媒体查询入门 & 传统布局方法 & 支持旧浏览器
下一篇: 前端学习记录~2023.7.24~JavaScript重难点实例精讲~第2章 引用数据类型
山药泥拌饭
博客等级 码龄10年 5粉丝 50原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山药泥拌饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值