JS中的类型转换

该节会全面介绍强制类型转换的优缺点

1、值类型转换

将值从一种类型转换为另一种类型通常称为类型转换,这是显式的情况;隐式的情况称为强制类型转换

JS中的强制类型转换总是返回标量基本类型值(参见第 2 章),如字符串、数字和布尔值,不会返回对象和函数。

也可以这样来区分:类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在强制类型转换动态类型语言的运行时。

然而在 JS中通常将它们统称为强制类型转换,用“隐式强制类型转换”(implicit coercion)和“显式强制类型转换”(explicit coercion)来区分。

var a = 42;
var b = a + ""; // 隐式强制类型转换
var c = String( a ); // 显式强制类型转换

对变量 b 而言,强制类型转换是隐式的;由于 + 运算符的其中一个操作数是字符串,所以是字符串拼接操作,结果是数字 42 被强制类型转换为相应的字符串 “42”。而 String(…) 则是将 a 显式强制类型转换为字符串。

2、抽象值操作

在ES5中,定义了一些"抽象操作"(仅供内部使用的操作)和转换规则。着重介绍ToString、ToNumber 和 ToBoolean和ToPrimitive

2.1、ToString

该操作负责处理非字符串到字符串的强制类型转换

基本类型值的字符串化规则为:null 转换为 “null”,undefined 转换为 “undefined”,true转换为 “true”。数字的字符串化则遵循通用规则

对普通对象来说,除非自行定义,否则 toString()(Object.prototype.toString())返回内部属性 [[Class]] 的值

数组的默认 toString() 方法经过了重新定义,将所有单元字符串化以后再用 “,” 连接起来:

var a = [1,2,3];
a.toString(); // "1,2,3"

toString() 可以被显式调用,或者在需要字符串化时自动调用。

JOSN字符串化

工具函数 JSON.stringify(…) 在将 JSON 对象序列化为字符串时也用到了 ToString。

对大多数简单值来说,JSON 字符串化和 toString() 的效果基本相同,只不过序列化的结果总是字符串

JSON.stringify( 42 ); // "42"
JSON.stringify( "42" ); // ""42"" (含有双引号的字符串)
JSON.stringify( null ); // "null"
JSON.stringify( true ); // "true"

JSON.stringify(…) 在对象中遇到 undefined、function 和 symbol 时会自动将其忽略,在数组中则会返回 null(以保证单元位置不变)。

JSON.stringify( undefined ); // undefined
JSON.stringify( function(){} ); // undefined
JSON.stringify(
[1,undefined,function(){},4]
); // "[1,null,null,4]"
JSON.stringify(
{ a:2, b:function(){} }
); // "{"a":2}"

对包含循环引用的对象执行 JSON.stringify(…) 会出错

如果要对含有非法 JSON 值的对象做字符串化,或者对象中的某些值无法被序列化时,就需要定义 toJSON() 方法来返回一个安全的 JSON 值

var o = { };
var a = {
	b: 42,
	c: o,
	d: function(){}
};
// 在a中创建一个循环引用
o.e = a;
// 循环引用在这里会产生错误
// JSON.stringify( a );

// 自定义的JSON序列化
a.toJSON = function() {
	// 序列化仅包含b
	return { b: this.b };
};
JSON.stringify( a ); // "{"b":42}"

也就是说,toJSON() 应该“返回一个能够被字符串化的安全的 JSON 值”,而不是“返回一个 JSON 字符串”。

我们可以向 JSON.stringify(…) 传递一个可选参数 replacer,它可以是数组或者函数,用来指定对象序列化过程中哪些属性应该被处理,哪些应该被排除

var a = {
	b: 42,
	c: "42",
	d: [1,2,3]
};
JSON.stringify( a, ["b","c"] ); // "{"b":42,"c":"42"}"

JSON.string 还有一个可选参数 space,可以是一个数字或者一个字符串。用来指定输出的缩进格式

const obj = { name: 'John', age: 30 };
const jsonStr = JSON.stringify(obj, null, 4);
console.log(jsonStr);
{
    "name": "John",
    "age": 30
}

2.2、ToNumber

有时我们需要将非数字值当作数字来使用,比如数学运算。其中 true 转换为 1,false 转换为 0。undefined 转换为 NaN,null 转换为 0。

不同之处是 ToNumber 对以 0 开头的十六进制数并不按十六进制处理。

对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。为了将值转换为相应的基本类型值,抽象操作 ToPrimitive会首先(通过内部操作 DefaultValue,)检查该值是否有 valueOf() 方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString()的返回值(如果存在)来进行强制类型转换。如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误

使用 Object.create(null) 创建的对象 [[Prototype]] 属性为 null,并且没有 valueOf() 和 toString() 方法,因此无法进行强制类型转换

2.3、ToBoolean

我们可以将 1 强制类型转换为 true,将 0 强制类型转换为 false,反之亦然,但它们并不是一回事。

假值

JS中的值可以分为以下两类:
(1) 可以被强制类型转换为 false 的值
(2) 其他(被强制类型转换为 true 的值)

以下列举了布尔强制类型转换所有可能出现的结果:

以下这些是假值:undefined、null、false、+0、-0 和 NaN、“”,假值的布尔强制类型转换结果为 false,我们可以理解为假值列表以
外的值都是真值,规定所有的对象都是真值

假值对象

var a = new Boolean( false );
var b = new Number( 0 );
var c = new String( "" );

var d = Boolean( a && b && c );
d; // true

它们都是封装了假值的对象。那它们究竟是 true 还是 false 呢?d 为 true,说明 a、b、c 都为 true。

假值对象看起来和普通对象并无二致(都有属性,等等),但将它们强制类型转换为布尔值时结果为 false。

最常见的例子是 document.all,它是一个类数组对象,包含了页面上的所有元素,由DOM(而不是 JavaScript 引擎)提供给 JavaScript 程序使用。它以前曾是一个真正意义上的对象,布尔强制类型转换结果为 true,不过现在它是一个假值对象。

真值

真值就是假值列表之外的值。

var a = "false";
var b = "0";
var c = "''";
var d = Boolean( a && b && c );
d; // true

也就是说真值列表可以无限长,无法一一列举,所以我们只能用假值列表作为参考。

3、显式强制类型转换

显式强制类型转换是那些显而易见的类型转换,很多类型转换都属于此列

3.1、字符串与数字

字符串和数字之间的转换是通过 String(…) 和 Number(…) 这两个内建函数(原生构造函数)来实现的

String(…) 遵循前面讲过的 ToString 规则,将值转换为字符串基本类型。Number(…) 遵循前面讲过的 ToNumber 规则,将值转换为数字基本类型。一目了然,所以我们将它们归为显式强制类型转换。

除了 String(…) 和 Number(…) 以外,还有其他方法可以实现字符串和数字之间的显式转换:

var a = 42;
var b = a.toString();
var c = "3.14";
var d = +c;
b; // "42"
d; // 3.14

a.toString() 是显式的(“toString”意为“to a string”),不过其中涉及隐式转换。因为toString() 对 42 这样的基本类型值不适用,所以 JavaScript 引擎会自动为 42 创建一个封装对象,然后对该对象调用 toString()。这里显式转换中含有隐式转换。

在JS开源社区中,一元运算 + 被普遍认为是显式强制类型转换。

日期显式转换为数字

建议应该使用 Date.now() 来获得当前的时间戳,使用 new Date(…).getTime() 来获得指定时间的时间戳

console.log(Date.now()) // 1701266286450

~ 运算符

~即字位操作非,虽然严格说来并非强制类型转换(因为返回值类型并没有发生变化),但字位运算符(如 |和 ~)和某些特殊数字一起使用时会产生类似强制类型转换的效果,返回另外一个数字

~。它首先将值强制类型转换为 32 位数字,然后执行字位操作“非”(对每一个字位进行反转)。

在 -(x+1) 中唯一能够得到 0(或者严格说是 -0)的 x 值是 -1。也就是说如果 x 为 -1 时,~和一些数字值在一起会返回假值 0,其他情况则返回真值。
然而这与我们讨论的内容有什么关系呢?
-1 是一个“哨位值”,哨位值是那些在各个类型中(这里是数字)被赋予了特殊含义的值。在 C 语言中我们用 -1 来代表函数执行失败,用大于等于 0 的值来代表函数执行成功。

JS中字符串的 indexOf(…) 方法也遵循这一惯例,该方法在字符串中搜索指定的子字符串,如果找到就返回子字符串所在的位置(从 0 开始),否则返回 -1,indexOf(…) 不仅能够得到子字符串的位置,还可以用来检查字符串中是否包含指定的子字符串,相当于一个条件判断。例如

var a = "Hello World";
if (a.indexOf( "lo" ) >= 0) { // true
	// 找到匹配!
}
if (a.indexOf( "lo" ) != -1) { // true
	// 找到匹配!
}
if (a.indexOf( "ol" ) < 0) { // true
	// 没有找到匹配!
}
if (a.indexOf( "ol" ) == -1) { // true
	// 没有找到匹配!
}

>= 0 和 == -1 这样的写法不是很好,称为“抽象渗漏”,意思是在代码中暴露了底层的实现细节,这里是指用 -1 作为失败时的返回值,这些细节应该被屏蔽掉。~ 和 indexOf() 一起可以将结果强制类型转换(实际上仅仅是转换)为真 / 假值:

var a = "Hello World";
~a.indexOf( "lo" ); // -4 <-- 真值!
if (~a.indexOf( "lo" )) { // true
	// 找到匹配!
}
~a.indexOf( "ol" ); // 0 <-- 假值!
!~a.indexOf( "ol" ); // true
if (!~a.indexOf( "ol" )) { // true
	// 没有找到匹配!
}

如果 indexOf(…) 返回 -1,~ 将其转换为假值 0,其他情况一律转换为真值。

字位截除

一些开发人员使用 ~~ 来截除数字值的小数部分,以为这和 Math.floor(…) 的效果一样,实际上并非如此。

~~ 中的第一个 ~ 执行 ToInt32 并反转字位,然后第二个 ~ 再进行一次字位反转,即将所有字位反转回原值,最后得到的仍然是 ToInt32 的结果,~~ 和 !! 很相似

对 ~~ 我们要多加注意。首先它只适用于 32 位数字,更重要的是它对负数的处理与 Math.floor(…) 不同。

Math.floor( -49.6 ); // -50
~~-49.6; // -49

3.2、显式解析数字字符串

解析字符串中的数字和将字符串强制类型转换为数字的返回结果都是数字。但解析和转换两者之间还是有明显的差别。

var a = "42";
var b = "42px";
Number( a ); // 42
parseInt( a ); // 42
Number( b ); // NaN
parseInt( b ); // 42

解析允许字符串中含有非数字字符,解析按从左到右的顺序,如果遇到非数字字符就停止。而转换不允许出现非数字字符,否则会失败并返回 NaN。解析字符串中的浮点数可以使用 parseFloat(…) 函数。

parseInt(…) 针对的是字符串值。向 parseInt(…) 传递数字和其他类型的参数是没有用的,比如 true、function(){…} 和 [1,2,3]。

parseInt(…) 会根据字符串的第一个字符来自行决定基数。如果第一个字符是 x 或 X,则转换为十六进制数字。如果是 0,则转换为八进制数字。默认为十进制

解析非字符串

3.3、显式转换为布尔值

来看看非布尔值转换为布尔值的情况

与前面的 String(…) 和 Number(…) 一样,Boolean(…)(不带 new)是显式的 ToBoolean 强制类型转换

var a = "0";
var b = [];
var c = {};
var d = "";
var e = 0;
var f = null;
var g;
Boolean( a ); // true
Boolean( b ); // true
Boolean( c ); // true
Boolean( d ); // false
Boolean( e ); // false
Boolean( f ); // false
Boolean( g ); // false

一元运算符 ! 显式地将值强制类型转换为布尔值。但是它同时还将真值反转为假值(或者将假值反转为真值)。所以显式强制类型转换为布尔值最常用的方法是 !!,因为第二个 ! 会将结果反转回原值

建议使用 Boolean(a) 和 !!a 来进行显式强制类型转换。

4、隐式强制类型转换

隐式强制类型转换指的是那些隐蔽的强制类型转换,隐式强制类型转换的作用是减少冗余,让代码更简洁

4.1、字符串与数组之间的隐式强制类型转换

var a = "42";
var b = "0";
var c = 42;
var d = 0;
a + b; // "420"
c + d; // 42
var a = [1,2];
var b = [3,4];
a + b; // "1,23,4"

简单来说就是,如果 + 的其中一个操作数是字符串,则执行字符串拼接;否则执行数字加法。

a + “”(隐式)和前面的 String(a)(显式)之间有一个细微的差别需要注意。根据ToPrimitive 抽象操作规则,a + “” 会对 a 调用 valueOf() 方法,然后通过 ToString 抽象操作将返回值转换为字符串。而 String(a) 则是直接调用 ToString()

var a = {
	valueOf: function() { return 42; },
	toString: function() { return 4; }
};
a + ""; // "42"
String( a ); // "4"

4.2、布尔值到数字的隐式强制类型转换

将true转换为1,false转换为0

4.3、隐式强制类型转换为布尔值

(1) if (…) 语句中的条件判断表达式。
(2) for ( … ; … ; … ) 语句中的条件判断表达式(第二个)。
(3) while (…) 和 do…while(…) 循环中的条件判断表达式。
(4) ? : 中的条件判断表达式。
(5) 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)

以上情况中,非布尔值会被隐式强制类型转换为布尔值,遵循前面介绍过的 ToBoolean 抽象操作规则

var a = 42;
var b = "abc";
var c;
var d = null;
if (a) {
	console.log( "yep" ); // yep
}
while (c) {
	console.log( "nope, never runs" );
}
c = d ? a : b;
c; // "abc"
if ((a && d) || c) {
	console.log( "yep" ); // yep
}

上例中的非布尔值会被隐式强制类型转换为布尔值以便执行条件判断。

4.4、|| 和 &&

在 JS中它们返回的并不是布尔值。它们的返回值是两个操作数中的一个(且仅一个)。即选择两个操作数中的一个,然后返回它的值。

&& 和 || 运算符的返回值并不一定是布尔类型,而是两个操作数其中一个的值。

var a = 42;
var b = "abc";
var c = null;
a || b; // 42
a && b; // "abc"
c || b; // "abc"
c && b; // null

对于 || 来说,如果条件判断结果为 true 就返回第一个操作数(a 和 c)的值,如果为false 就返回第二个操作数(b)的值。

&& 则相反,如果条件判断结果为 true 就返回第二个操作数(b)的值,如果为 false 就返回第一个操作数(a 和 c)的值。

下面是一个十分常见的 || 的用法,也许你已经用过但并未完全理解:

function foo(a,b) {
a = a || "hello";
b = b || "world";
console.log( a + " " + b );
}
foo(); // "hello world"
foo( "yeah", "yeah!" ); // "yeah yeah!"

a = a || “hello”(又称为 C# 的“空值合并运算符”的 JavaScript 版本)检查变量 a,如果还未赋值(或者为假值),就赋予它一个默认值(“hello”)。

再来看看 &&:

有一种用法对开发人员不常见,然而JS代码压缩工具常用。就是如果第一个操作数为真值,则 && 运算符“选择”第二个操作数作为返回值,这也叫作“守护运算符”,即前面的表达式为后面的表达式“把关”:

function foo() {
	console.log( a );
}
var a = 42;
a && foo(); // 42

开发人员通常使用 if (a) { foo(); }。但 JavaScript代码压缩工具用的是 a && foo(),因为更简洁

4.5、符号的强制类型转换

ES6 中引入了符号类型,ES6 允许从符号到字符串的显式强制类型转换,然而隐式强制类型转换会产生错误。

var s1 = Symbol( "cool" );
String( s1 ); // "Symbol(cool)"
var s2 = Symbol( "not cool" );
s2 + ""; // TypeError

符号不能够被强制类型转换为数字(显式和隐式都会产生错误),但可以被强制类型转换为布尔值(显式和隐式结果都是 true)。

5、宽松相等和严格相等

宽松相等(loose equals)== 和严格相等(strict equals)=== 都用来判断两个值是否“相等”,但是它们之间有一个很重要的区别,特别是在判断条件上

== 允许在相等比较中进行强制类型转换,而 === 不允许

5.1、抽象相等

几个特殊的情况:NaN不等于NaN,+0不等于-0

对象(包括函数和数组)的宽松相等 ==。两个对象指向同一个值时即视为相等,不发生强制类型转换

== 在比较两个不同类型的值时会发生隐式强制类型转换,会将其中之一或两者都转换为相同的类型后再进行比较

字符串与数字

var a = 42;
var b = "42";
a === b; // false
a == b; // true

(1) 如果 Type(x) 是数字,Type(y) 是字符串,则返回 x == ToNumber(y) 的结果。
(2) 如果 Type(x) 是字符串,Type(y) 是数字,则返回 ToNumber(x) == y 的结果。

其他类型与布尔值之间的相等比较

var a = "42";
var b = true;
a == b; // false

(1) 如果 Type(x) 是布尔类型,则返回 ToNumber(x) == y 的结果;
(2) 如果 Type(y) 是布尔类型,则返回 x == ToNumber(y) 的结果。

Type(x) 是布尔值,所以 ToNumber(x) 将 true 强制类型转换为 1,变成 1 == “42”,二者的类型仍然不同,“42” 根据规则被强制类型转换为 42,最后变成 1 == 42,结果为 false

反过来也一样,Type(y) 是布尔值,所以 ToNumber(y) 将 false 强制类型转换为 0,然后 “42” == 0 再变成42 == 0,结果为 false

但 “42” == true 中并没有发生布尔值的比较和强制类型转换。这里不是 “42” 转换为布尔值(true),而是 true 转换为 1,“42” 转换为 42

null与undefined

null 和 undefined 之间的 == 也涉及隐式强制类型转换

(1) 如果 x 为 null,y 为 undefined,则结果为 true。
(2) 如果 x 为 undefined,y 为 null,则结果为 true。

在 == 中 null 和 undefined 相等(它们也与其自身相等),除此之外其他值都不存在这种情况。这也就是说在 == 中 null 和 undefined 是一回事,可以相互进行隐式强制类型转换

对象与非对象之间

(1) 如果 Type(x) 是字符串或数字,Type(y) 是对象,则返回 x == ToPrimitive(y) 的结果;
(2) 如果 Type(x) 是对象,Type(y) 是字符串或数字,则返回 ToPromitive(x) == y 的结果。

var a = 42;
var b = [ 42 ];
a == b; // true

[ 42 ] 首先调用 ToPromitive 抽象操作,返回 “42”,变成 “42” == 42,然后又变成 42 == 42,最后二者相等。

ToPromitive 抽象操作的所有特性(如 toString()、valueOf())在这里都适用

var a = null;
var b = Object( a ); // 和Object()一样
a == b; // false
var c = undefined;
var d = Object( c ); // 和Object()一样
c == d; // false
var e = NaN;
var f = Object( e ); // 和new Number( e )一样
e == f; // false

因为没有对应的封装对象,所以 null 和 undefined 不能够被封装(boxed),Object(null)和 Object() 均返回一个常规对象。
NaN 能够被封装为数字封装对象,但拆封之后 NaN == NaN 返回 false,因为 NaN 不等于 NaN

5.2、少见的情况

返回其他数字

Number.prototype.valueOf = function() {
	return 3;
};
console.log(new Number( 2 ) == 3) // true
var i = 2;
Number.prototype.valueOf = function() {
	return i++;
};
var a = new Number( 42 );
if (a == 2 && a == 3) {
	console.log( "Yep, this happened." );
}

假值的相等比较

"0" == null; // false
"0" == undefined; // false
"0" == false; // true -- 晕!
"0" == NaN; // false
"0" == 0; // true
"0" == ""; // false
false == null; // false
false == undefined; // false
false == NaN; // false
false == 0; // true -- 晕!
false == ""; // true -- 晕!
false == []; // true -- 晕!
false == {}; // false
"" == null; // false
"" == undefined; // false
"" == NaN; // false
"" == 0; // true -- 晕!
"" == []; // true -- 晕!
"" == {}; // false
0 == null; // false
0 == undefined; // false
0 == NaN; // false
0 == []; // true -- 晕!
0 == {}; // false

比如我们都知道 “” 和 NaN 不相等,“0” 和 0 相等

极端情况

[] == ![] // true
0 == "\n"

根据 ToBoolean 规则,它会进行布尔值的显式强制类型转换(同时反转奇偶校验位)。所以 [] == ![] 变成了 [] == false。前面我们讲过 false == [],最后的结果就顺理成章了

“”、"\n"等空字符串被ToNumber强制类型转换为0

完整性检查

安全运用隐式强制转换

  • 如果两边的值中有 true 或者 false,千万不要使用 ==
  • 如果两边的值中有 []、“” 或者 0,尽量不要使用 ==

这时最好用 === 来避免不经意的强制类型转换。这两个原则可以让我们避开几乎所有强制类型转换的坑
请添加图片描述

6、抽象关系比较

a < b 中涉及的隐式强制类型转换不太引人注意,不过还是很有必要深入了解一下

该算法仅针对 a < b,a=“”> b 会被处理为 b <a

比较双方首先调用 ToPrimitive,如果结果出现非字符串,就根据 ToNumber 规则将双方强制类型转换为数字来进行比较

var a = [ 42 ];
var b = [ "43" ];
a < b; // true
b < a; // false

如果比较双方都是字符串,则按字母顺序来进行比较

var a = [ "42" ];
var b = [ "043" ];
a < b; // false

var a = [ 4, 2 ];
var b = [ 0, 4, 3 ];
a < b; // false

var a = { b: 42 };
var b = { b: 43 };
a < b; // false
// 因为 a 是 [object Object],b 也是 [object Object],所以按照字母顺序a < b 并不成立

a 和 b 并没有被转换为数字,因为 ToPrimitive 返回的是字符串,所以这里比较的是 "42"和 “043” 两个字符串,它们分别以 “4” 和 “0” 开头。因为 “0” 在字母顺序上小于 “4”,所以最后结果为 false。

下面的例子就有些奇怪了

var a = { b: 42 };
var b = { b: 43 };
a < b; // false
a == b; // false
a > b; // false
a <= b; // true
a >= b; // true

因为根据规范 a <= b 被处理为 b < a,然后将结果反转。因为 b < a 的结果是 false,所以 a <= b 的结果是 true

JavaScript 中 <= 是“不大于”的意思(即 !(a > b),处理为 !(b < a))。同理 a >= b 处理为 b <= a

7、总结

本章介绍了 JavaScript 的数据类型之间的转换,即强制类型转换:包括显式和隐式。

强制类型转换常常为人诟病,但实际上很多时候它们是非常有用的。作为有使命感的JavaScript 开发人员,我们有必要深入了解强制类型转换,这样就能取其精华,去其糟粕。

显式强制类型转换明确告诉我们哪里发生了类型转换,有助于提高代码可读性和可维护性。

隐式强制类型转换则没有那么明显,是其他操作的副作用。感觉上好像是显式强制类型转换的反面,实际上隐式强制类型转换也有助于提高代码的可读性。

在处理强制类型转换的时候要十分小心,尤其是隐式强制类型转换。在编码的时候,要知其然,还要知其所以然,并努力让代码清晰易读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/205685.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

5 存储器映射和寄存器

文章目录 5.3 芯片内核5.3.1 ICache5.3.2 DCache5.3.3 FlexRAM 5.4 存储器映射5.4.1 存储器功能划分5.4.1.1 存储器 Block0 内部区域功能划分5.4.1.2 储存器 Block1 内部区域功能划分5.4.1.3 储存器 Block2 内部区域功能划分 5.5 寄存器映射5.5.1 GPIO1的输出数据寄存器 5.3 芯…

如何使用内网穿透实现无公网ip环境访问VScode远程开发

文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…

leetCode 78.子集 + 回溯算法 + 图解

给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[],[1],[2],[1,2],[3],[1…

Jmeter无法录制Chrome和Edge浏览器问题:ERR_SSL_PROTOCOL_ERROR

问题描述&#xff1a; Jmeter使用的是5.6.2&#xff0c;设置了代理proxy&#xff0c;添加了crt证书&#xff0c;但是录制时&#xff0c;在火狐浏览器可以正常录制&#xff0c;但在chrome和Edge上时&#xff0c;访问网站&#xff0c;回车后&#xff0c;一直是空白页面。 在谷歌…

TA-Lib学习研究笔记——Volume Indicators (四)

TA-Lib学习研究笔记——Volume Indicators &#xff08;四&#xff09; 1.AD Chaikin A/D Line 量价指标 函数名&#xff1a;AD 名称&#xff1a;Chaikin A/D Line 累积/派发线&#xff08;Accumulation/Distribution Line&#xff09; 简介&#xff1a;Marc Chaikin提出的一…

【前端开发】Next.js与Nest.js之间的差异2023

在快节奏的网络开发领域&#xff0c;JavaScript已成为构建可靠且引人入胜的在线应用程序的标准语言。然而&#xff0c;随着对适应性强、高效的在线服务的需求不断增加&#xff0c;开发人员通常不得不从广泛的库和框架中进行选择&#xff0c;以满足其项目的要求。Next.js和Nest.…

每日一练2023.11.30——验证身份【PTA】

题目链接 &#xff1a;验证身份 题目要求&#xff1a; 一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下&#xff1a; 首先对前17位数字加权求和&#xff0c;权重分配为&#xff1a;{7&#xff0c;9&#xff0c;10&#xff0c;5&a…

【动态规划】01第 N 个泰波那契数(easy)

题目链接 &#xff1a;leetcode第 N 个泰波那契数 目录 题目解析&#xff1a; 算法原理 1.状态表示 2.状态转移方程 3.初始化 4.填表顺序 5.返回值 编写代码 题目解析&#xff1a; 题目让我们求第n个数的泰波那契数。 由题可得&#xff1a; 我们可以把它改写为&#…

Windows系列:windows server 2003 - 组策略部署软件

通过组策略为域内用户部署&#xff08;deploy&#xff09;软件&#xff0c;可分为指派&#xff08;assign&#xff09;和发布&#xff08;publish&#xff09;。 软件指派给用户&#xff1a;用户在域内登录后&#xff0c;被“通告 advertised”给用户&#xff0c;此时仅安装了部…

Java中的synchronized关键字

目录 1、synchronized是什么 2、synchronized的用法 synchronized可以用在方法或者代码块上&#xff0c;分别称为同步方法和同步代码块。 用法理解 3、synchronized的实现原理 ⭐synchronized锁的对比 4、synchronized的优缺点 ⭐扩展&#xff1a;synchronized 和 vola…

深度解析 Spring Security 自定义异常失效问题:源码剖析与解决方案

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

leetcode面试经典150题——34 有效的数独(矩阵)

题目&#xff1a; 有效的数独 描述&#xff1a; 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出…

JAVA调优

1 JAVA虚拟机 1.1 基本组成 通常来说Java平台标准版&#xff08;Java SE&#xff09;包括 Java SE开发工具包&#xff08;JDK&#xff09;和Java SE运行时环境&#xff08;JRE&#xff09;。 JRE提供了运行以Java编程语言编写的applet和应用程序所必需的库&#xff0c;Java虚…

CodeTON Round 7 (Div. 1 + Div. 2, Rated, Prizes!)

B. AB Flipping 老规矩&#xff0c;自己要模拟一遍样例&#xff0c;发现样例还不够&#xff0c;就自己构造样例&#xff0c;这样做着做着就会有思路。 分析&#xff1a;假如现在有这样一个字符串 BBBAABABBAAA。会发现前三个和后三个一定是不会被操作的&#xff0c;因为不会满…

5、DMA Demo(STM32F407)

DMA简介 DMA 全称Direct Memory Access&#xff0c;即直接存储器访问。 DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作&#xff0c;传输动作本身是由DMA控制器来实现和完成的。 DMA传输方式无需CPU直接控制传输&#xff0c;也没有中断处理方式那…

力扣295. 数据流的中位数(java,堆解法)

Problem: 295. 数据流的中位数 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 由于该题目的数据是动态的我们可以维护两个堆来解决该问题 1.维护一个大顶堆&#xff0c;一个小顶堆 2.每个堆中元素个数接近n/2&#xff1b;如果n是偶数&#xff0c;两个堆中的数据个数…

SpringCloud核心组件

Eureka 注册中心&#xff0c;服务的注册与发现 Feign远程调用 Ribbon负载均衡&#xff0c;默认轮询 Hystrix 熔断 降级 Zuul微服务网关&#xff08;这个组件负责网络路由&#xff0c;可以做统一的降级、限流、认证授权、安全&#xff09; Eureka 微服务的功能主要有以下几…

LLM之Agent(二):BabyAGI的详细教程

BabyAGI是一个 AI 支持的任务管理系统&#xff08;Python脚本&#xff09;&#xff0c;使用 OpenAI 和 Pinecone API 创建, 优先级排序和执行任务。该系统背后的主要思想是基于先前任务的结果和预定义的目标创建任务。脚本然后使用 OpenAI 的自然语言处理&#xff08;NLP&#…

数据结构学习笔记——二叉树的遍历和链式存储代码实现二叉树

目录 一、二叉树的遍历&#xff08;一&#xff09;二叉树的先序遍历&#xff08;DLR&#xff09;&#xff08;二&#xff09;二叉树的中序遍历&#xff08;LDR&#xff09;&#xff08;三&#xff09;二叉树的后序遍历&#xff08;LRD&#xff09;&#xff08;四&#xff09;二…

【古月居《ros入门21讲》学习笔记】07_创建工作空间和功能包

目录 说明&#xff1a; 1. 工作空间(workspace) 结构&#xff1a; 2. 创建工作空间和功能包 创建工作空间 编译工作空间 创建功能包 设置环境变量 3. 注意 同一个工作空间下&#xff0c;不能存在同名的功能包&#xff1b; 不同工作空间下&#xff0c;可以存在同名的功…