在 JavaScript 比较中应该使用哪个等于运算符(== 与 ===)?

2024-11-02 21:00:00
admin
原创
37
摘要:问题描述:我正在使用JSLint来处理 JavaScript,当在语句内部进行比较之类的操作时,它返回许多建议,==用(三个等号)替换===(两个等号)。idSele_UNVEHtype.value.length == 0`if`==用替换 是否有性能优势===?由于存在许多比较运算符,任何性能改进都会受到欢...

问题描述:

我正在使用JSLint来处理 JavaScript,当在语句内部进行比较之类的操作时,它返回许多建议,==用(三个等号)替换===(两个等号)。idSele_UNVEHtype.value.length == 0`if`

==用替换 是否有性能优势===

由于存在许多比较运算符,任何性能改进都会受到欢迎。

如果不发生类型转换,性能是否会有所提升==


解决方案 1:

严格相等运算符(===)的行为与抽象相等运算符(==)相同,只是不进行类型转换,并且类型必须相同才被视为相等。

参考:JavaScript 教程:比较运算符

运算符将在进行任何必要的类型转换后==比较是否相等。运算符不会进行转换,因此如果两个值的类型不同,则将仅返回。两者同样快。===`===`false

引用 Douglas Crockford 的优秀作品《JavaScript:精​​彩部分》,

JavaScript 有两组相等运算符:===!==,以及它们的邪恶双胞胎==!=。善良的运算符按您预期的方式工作。如果两个操作数属于同一类型且具有相同的值,则===产生true!==产生false。邪恶的双胞胎在操作数属于同一类型时会做正确的事情,但如果它们是不同类型的,它们会尝试强制转换值。它们这样做的规则很复杂且难以记住。以下是一些有趣的案例:

'' == '0'           // false
0 == ''             // true
0 == '0'            // true
false == 'false'    // false
false == '0'        // true
false == undefined  // false
false == null       // false
null == undefined   // true

' == 0 // true

平等性比较表

传递性的缺乏令人担忧。我的建议是永远不要使用邪恶双胞胎。相反,始终使用===and !==。刚刚显示的所有比较都false使用===运算符。


更新

@Casebash在评论中以及@Phillipe Laybaert 关于对象的 回答中提出了一个很好的观点。对于对象,==并且===彼此一致地行动(特殊情况除外)。

var a = [1,2,3];
var b = [1,2,3];

var c = { x: 1, y: 2 };
var d = { x: 1, y: 2 };

var e = "text";
var f = "te" + "xt";

a == b            // false
a === b           // false

c == d            // false
c === d           // false

e == f            // true
e === f           // true

特殊情况是当您将一个基元与一个对象进行比较时,由于其toStringvalueOf方法,该对象求值为相同的基元。例如,考虑将字符串基元与使用构造函数创建的字符串对象进行比较String

"abc" == new String("abc")    // true
"abc" === new String("abc")   // false

这里,==运算符检查两个对象的值并返回true,但===发现它们不是同一类型并返回false。哪一个是正确的?这实际上取决于您要比较的内容。我的建议是完全绕过这个问题,不要使用String构造函数从字符串文字创建字符串对象。

参考

https://262.ecma-international.org/5.1/#sec-11.9.3

解决方案 2:

使用==运算符(等式

true == 1; //true, because 'true' is converted to 1 and then compared
"2" == 2;  //true, because "2" is converted to 2 and then compared

使用===运算符(Identity

true === 1; //false
"2" === 2;  //false

这是因为相等运算符==进行类型强制,这意味着解释器在比较之前会隐式地尝试转换值。

另一方面,身份运算符===不进行类型强制,因此在比较时不会转换值。

解决方案 3:

==以下是和之间相等性比较的有趣的可视化效果===

来源:https ://github.com/dorey/JavaScript-Equality-Table (演示,统一演示)


var1 === var2

当用于===JavaScript 相等性测试时,一切都保持原样。

在评估之前不会进行任何转换。

JS 中 === 的相等性评估

var1 == var2

当用于==JavaScript 相等性测试时,会发生一些奇怪的转换。

JS 中 == 的相等性评估

JavaScript中相等性的总结

JavaScript 中的平等


结论:

总是使用===,除非你完全理解与之相关的奇怪转换==

解决方案 4:

在这里的答案中,我没有读到任何有关相等的含义的信息。有些人会说这===意味着相等且属于同一类型,但事实并非如此。它实际上意味着两个操作数引用同一个对象,或者在值类型的情况下,具有相同的值

因此,我们来看下面的代码:

var a = [1,2,3];
var b = [1,2,3];
var c = a;

var ab_eq = (a === b); // false (even though a and b are the same type)
var ac_eq = (a === c); // true

这里也一样:

var a = { x: 1, y: 2 };
var b = { x: 1, y: 2 };
var c = a;

var ab_eq = (a === b); // false (even though a and b are the same type)
var ac_eq = (a === c); // true

甚至:

var a = { };
var b = { };
var c = a;

var ab_eq = (a === b); // false (even though a and b are the same type)
var ac_eq = (a === c); // true

这种行为并不总是显而易见的。故事中还有更多内容,而不仅仅是平等和同类型。

规则是:

对于值类型(数字):如果和具有相同的值且属于同一类型,

a === b则返回 truea`b`

对于引用类型:如果和引用完全相同的对象,

a === b则返回 truea`b`

对于字符串:

a === b如果ab都是字符串并且包含完全相同的字符,


字符串:特殊情况......

字符串不是值类型,但在 Javascript 中它们的行为类似于值类型,因此当字符串中的字符相同且长度相同时,它们将“相等”(如第三条规则所述)

现在事情变得有趣了:

var a = "12" + "3";
var b = "123";

alert(a === b); // returns true, because strings behave like value types

但是这个怎么样?:

var a = new String("123");
var b = "123";

alert(a === b); // returns false !! (but they are equal and of the same type)

我以为字符串的行为类似于值类型?嗯,这取决于你问谁... 在这种情况下,a 和 b 不是同一类型。a是类型Object,而b是类型string。只需记住,使用构造函数创建字符串对象String会创建某种类型的东西,该类型在大多数情况下Object表现为字符串。

解决方案 5:

让我补充一下这条建议:

如果有疑问,请阅读规范!

ECMA-262 是 JavaScript 的一种方言脚本语言规范。当然,在实践中,最重要的浏览器如何表现比如何处理某些事情的深奥定义更重要。但理解为什么new String("a") !== "a"是有帮助的。

请让我解释一下如何阅读规范以澄清这个问题。我发现在这个非常古老的话题中,没有人对这种非常奇怪的效果给出答案。所以,如果你能读懂规范,这将对你的职业发展大有裨益。这是一项后天习得的技能。所以,让我们继续。

在 PDF 文件中搜索 === 会带我找到规范的第 56 页:11.9.4. 严格等于运算符 ( === ),在仔细阅读规范术语后,我发现:

11.9.6 严格相等比较算法

比较 x === y (其中 x 和 y 是值)结果为truefalse。 这种比较执行如下:

  1. 如果 Type(x) 与 Type(y) 不同,则返回false

  2. 如果 Type(x) 为 Undefined,则返回true

  3. 如果 Type(x) 为 Null,则返回true

  4. 如果 Type(x) 不是 Number,则转到步骤 11。

  5. 如果 x 是NaN,则返回false

  6. 如果 y 是NaN,则返回false

  7. 如果 x 与 y 是相同的数字值,则返回true

  8. 如果 x 是 +0 且 y 是 −0,则返回true

  9. 如果 x 是 −0 且 y 是 +0,则返回true

  10. 返回false

  11. 如果 Type(x) 是 String,则如果 x 和 y 是完全相同的字符序列(长度相同且相应位置上的字符相同),则返回true ;否则返回false。12

  . 如果 Type(x) 是 Boolean,则如果 x 和 y 同时为true同时为false ,则返回true;否则返回false。13
  .如果 x 和 y 引用同一个对象或它们引用彼此连接的对象(参见 13.1.2),则返回 true 。否则返回false

有趣的是第 11 步。是的,字符串被视为值类型。但这并不能解释为什么new String("a") !== "a"。我们的浏览器是否不符合 ECMA-262?

别那么快!

让我们检查一下操作数的类型。通过将它们包装在typeof()中来亲自尝试一下。我发现new String("a")是一个对象,并且使用了步骤 1:如果类型不同,则返回false 。

如果你想知道为什么new String("a")不返回字符串,那么阅读一下规范如何?玩得开心!


Aidiakapi 在下面的评论中写道:

从规范来看

11.2.2 new 操作符

如果 Type(构造函数)不是 Object,则抛出 TypeError 异常。

换句话说,如果 String 不是 Object 类型,它就不能与 new 运算符一起使用。

new总是返回一个对象,对于String构造函数也是如此。唉!字符串的值语义(参见步骤 11)丢失了。

这最终意味着:new String("a") !== "a"

解决方案 6:

我使用如下代码在 Firefox 和Firebug中测试了这一点:

console.time("testEquality");
var n = 0;
while (true) {
  n++;
  if (n == 100000)
    break;
}
console.timeEnd("testEquality");

运行代码片段Hide results展开片段

console.time("testTypeEquality");
var n = 0;
while (true) {
  n++;
  if (n === 100000)
    break;
}
console.timeEnd("testTypeEquality");

运行代码片段Hide results展开片段

我的结果(每次测试五次并取平均值):

==: 115.2
===: 114.4

所以我认为微小的差异(记住,这是超过 100000 次迭代)可以忽略不计。性能不是这样做的理由===。类型安全(嗯,就像 JavaScript 中你能获得的安全一样)和代码质量才是。

解决方案 7:

在 PHP 和 JavaScript 中,它是一个严格相等运算符。这意味着它将比较类型和值。

解决方案 8:

在 JavaScript 中,它意味着相同的值和类型。

例如,

4 == "4" // will return true

4 === "4" // will return false 

解决方案 9:

为何==如此难以预测?

""当将空字符串与数字零进行比较时会得到什么0

true

是的,根据==空字符串和数字零是相同的,这是正确的。

事情还没有结束,下面还有另外一个:

'0' == false // true

使用数组时事情会变得非常奇怪。

[1] == true // true
[] == false // true
[[]] == false // true
[0] == false // true

然后用弦更奇怪

[1,2,3] == '1,2,3' // true - REALLY?!
'
    ' == 0 // true - Come on!

情况变得更糟:

什么时候平等不再平等?

let A = ''  // empty string
let B = 0   // zero
let C = '0' // zero string

A == B // true - ok... 
B == C // true - so far so good...
A == C // **FALSE** - Plot twist!

让我再说一遍:

(A == B) && (B == C) // true
(A == C) // **FALSE**

这只是使用原始方法所获得的疯狂的东西。

当与物体一起使用时,它会达到一个全新的疯狂水平==

此时您可能想知道...

为什么会发生这种情况?

嗯,这是因为它与“三重等号”(===)不同,它只是检查两个值是否相同。

==做了很多其他的事情

它对函数有特殊处理,对空值、未定义、字符串等都有特殊处理。

这变得相当古怪。

事实上,如果你尝试编写一个函数来执行==以下操作:

function isEqual(x, y) { // if `==` were a function
    if(typeof y === typeof x) return y === x;
    // treat null and undefined the same
    var xIsNothing = (y === undefined) || (y === null);
    var yIsNothing = (x === undefined) || (x === null);

    if(xIsNothing || yIsNothing) return (xIsNothing && yIsNothing);

    if(typeof y === "function" || typeof x === "function") {
        // if either value is a string 
        // convert the function into a string and compare
        if(typeof x === "string") {
            return x === y.toString();
        } else if(typeof y === "string") {
            return x.toString() === y;
        } 
        return false;
    }

    if(typeof x === "object") x = toPrimitive(x);
    if(typeof y === "object") y = toPrimitive(y);
    if(typeof y === typeof x) return y === x;

    // convert x and y into numbers if they are not already use the "+" trick
    if(typeof x !== "number") x = +x;
    if(typeof y !== "number") y = +y;
    // actually the real `==` is even more complicated than this, especially in ES6
    return x === y;
}

function toPrimitive(obj) {
    var value = obj.valueOf();
    if(obj !== value) return value;
    return obj.toString();
}

那么这意味着什么?

意思==是很复杂。

因为它很复杂,所以很难知道使用它时会发生什么。

这意味着你最终可能会遇到错误。

所以这个故事的寓意是......

让您的生活不再复杂。

使用===而不是==

结束。

解决方案 10:

===运算符被称为严格比较运算符,它与==运算符有所不同。

让我们取 2 个变量 a 和 b。

要使“a == b”的计算结果为真,a 和 b 必须是相同的值

在“a === b”的情况下,a 和 b 必须是相同的值并且也是相同的类型,才能计算为真。

以下面的例子为例

var a = 1;
var b = "1";

if (a == b) //evaluates to true as a and b are both 1
{
    alert("a == b");
}

if (a === b) //evaluates to false as a is not the same type as b
{
    alert("a === b");
}

总之;在您不希望的情况下,使用==运算符可能会计算为 true,因此使用===运算符会更安全。

在 90% 的使用场景中,使用哪一个并不重要,但当您有一天遇到一些意外行为时,了解差异会很方便。

解决方案 11:

===检查同侧的类型是否相等。


例子:

'1' === 1 // will return "false" because `string` is not a `number`

常见示例:

0 == ''  // will be "true", but it's very common to want this check to be "false"

另一个常见的例子:

null == undefined // returns "true", but in most cases a distinction is necessary

根据我的长期经验,非类型检查是更好的选择,因为您不必关心该值是undefined、 还是null`0`""

另一种比较方法是使用Object.is,这里有一个非常有帮助的答案。

解决方案 12:

严格相等/比较 '===' 的 Javascript 执行流程图

JavaScript 严格平等

非严格相等/比较 '==' 的 Javascript 执行流程图

JavaScript 不等式

解决方案 13:

JavaScript=== ==.

0==false   // true
0===false  // false, because they are of a different type
1=="1"     // true, auto type coercion
1==="1"    // false, because they are of a different type

解决方案 14:

这意味着不进行类型强制的相等性,
类型强制意味着 JavaScript 不会自动将任何其他数据类型转换为字符串数据类型

0==false   // true,although they are different types

0===false  // false,as they are different types

2=='2'    //true,different types,one is string and another is integer but 
            javaScript convert 2 to string by using == operator 

2==='2'  //false because by using === operator ,javaScript do not convert 
           integer to string 

2===2   //true because both have same value and same types 

解决方案 15:

在典型的脚本中,不会有性能差异。更重要的可能是,一千个“===”比一千个“==”重 1 KB :) JavaScript 分析器可以告诉您在您的案例中是否存在性能差异。

但我个人会按照 JSLint 的建议去做。这个建议不是因为性能问题,而是因为类型强制意味着`('
' == 0)`true。

解决方案 16:

相等比较运算符 == 容易引起混淆,应该避免使用。

如果你必须忍受它,那么请记住以下三件事:

  1. 它不是传递的:(a == b)(b == c)不会导致(a == c)

  2. 它与其否定是互斥的:(a == b)(a != b)始终保持相反的布尔值,a 和 b 都是如此。

  3. 如有疑问,请熟记以下真值表:

JAVASCRIPT 中的等号运算符真值表

  • 表中的每一行都是一组 3 个相互“相等”的值,这意味着使用等号 == 符号*,它们之间的任何 2 个值都是相等的

奇怪:请注意,第一列上的任意两个值在这种意义上都不相等。

''       == 0 == false   // Any two values among these 3 ones are equal with the == operator
'0'      == 0 == false   // Also a set of 3 equal values, note that only 0 and false are repeated
'    '     == 0 == false   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
'
'     == 0 == false   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
'
'     == 0 == false   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
'    
' == 0 == false   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

null == undefined  // These two "default" values are not-equal to any of the listed values above
NaN                // NaN is not equal to any thing, even to itself.

解决方案 17:

在您的使用中,这两个操作之间不太可能存在任何性能差异。无需进行类型转换,因为两个参数已经是同一类型。这两个操作都将进行类型比较,然后进行值比较。

解决方案 18:

简单地

==表示操作数之间的比较需要进行类型强制

===表示操作数之间的比较不进行类型强制。

JavaScript 中的类型强制意味着自动将数据类型转换为其他数据类型。

例如:

123 == "123"  // Returns true, because JS coerces string "123" to number 123
              // and then goes on to compare `123 == 123`.

123 === "123" // Returns false, because JS does not coerce values of different types here.

解决方案 19:

是的!这很重要。

===javascript 中的运算符不仅检查值,还检查类型,而==运算符仅检查值(如果需要,则进行类型转换)

在此处输入图片描述

您可以轻松测试它。将以下代码粘贴到 HTML 文件中,然后在浏览器中打开

<script>

function onPageLoad()
{
    var x = "5";
    var y = 5;
    alert(x === 5);
};

</script>

</head>

<body onload='onPageLoad();'>

您将在警报中得到“ falseonPageLoad() ”。现在修改方法,alert(x == 5);您将得到true

解决方案 20:

根据经验法则,我通常会使用===而不是==(而!==不是!=)。

原因在上面的答案中已经解释过了,Douglas Crockford 对此也说得很清楚(JavaScript:优点)。

但是有一个例外
== null这是一种检查“为空或未定义”的有效方法:

if( value == null ){
    // value is either null or undefined
}

例如 jQuery 1.9.1 使用了 43 次这种模式,并且JSHint 语法检查器甚至eqnull为此提供了放宽选项。

来自jQuery 风格指南:

应使用严格相等性检查 (===) 代替 ==。唯一的例外是通过 null 检查 undefined 和 null。

// Check for both undefined and null values, for some important reason. 
undefOrNull == null;

编辑2021-03:

如今大多数浏览器都
支持空值合并运算符(??
和逻辑空值赋值(??=),当变量为空或未定义时,可以使用更简洁的方式分配默认值,例如:

if (a.speed == null) {
  // Set default if null or undefined
  a.speed = 42;
}

可以写成以下任何一种形式

a.speed ??= 42;
a.speed ?? a.speed = 42;
a.speed = a.speed ?? 42;

解决方案 21:

这是一次严格的检查测试。

这是一件好事,特别是当你检查 0 和 false 以及 null 之间时。

例如,如果您有:

$a = 0;

然后:

$a==0; 
$a==NULL;
$a==false;

全部返回 true,而你可能不希望出现这种情况。假设你有一个函数,它可以返回数组的第 0 个索引,或者在失败时返回 false。如果你用“==”false 进行检查,你可能会得到一个令人困惑的结果。

因此,与上面的情况相同,但要进行严格的测试:

$a = 0;

$a===0; // returns true
$a===NULL; // returns false
$a===false; // returns false

解决方案 22:

===运算符检查变量的值以及类型是否相等。

==运算符仅检查变量的值是否相等。

解决方案 23:

JSLint 有时会给你一些不切实际的理由来修改东西。其性能与类型已经相同时===完全相同。==

只有当类型不一样时它才会更快,在这种情况下它不会尝试转换类型而是直接返回 false。

因此,恕我直言, JSLint 也许可以用来编写新代码,但应该不惜一切代价避免无用的过度优化。

意思是,当您知道 a 只能是字符串时,没有理由在检查中更改==为。===`if (a == 'test')`

以这种方式修改大量代码会浪费开发人员和审阅人员的时间并且不会取得任何效果。

解决方案 24:

一个简单的例子

2 == '2'  -> true, values are SAME because of type conversion.

2 === '2'  -> false, values are NOT SAME because of no type conversion.

解决方案 25:

前两个答案都提到 == 表示相等,而 === 表示相同。不幸的是,这个说法是错误的。

如果 == 的两个操作数都是对象,则比较它们是否是同一个对象。如果两个操作数都指向同一个对象,则相等运算符返回 true。否则,两者不相等。

var a = [1, 2, 3];  
var b = [1, 2, 3];  
console.log(a == b)  // false  
console.log(a === b) // false  

在上面的代码中,== 和 === 都为 false,因为 a 和 b 不是同一个对象。

也就是说:如果 == 的两个操作数都是对象,那么 == 的行为和 === 相同,也意味着身份相同。这两个操作符的本质区别在于类型转换。 == 在检查相等性之前会进行类型转换,而 === 则不会。

解决方案 26:

问题是您可能很容易陷入麻烦,因为 JavaScript 有很多隐式转换,这意味着……

var x = 0;
var isTrue = x == null;
var isFalse = x === null;

这很快就会成为一个问题。关于为什么隐式转换是“邪恶的”的最好例子可以从MFC / C++中的这段代码中得到,这段代码实际上会因为从 CString 到 HANDLE 的隐式转换而编译,而 HANDLE 是一个指针 typedef 类型...

CString x;
delete x;

显然,这在运行时会做一些非常不确定的事情......

在 Google 上搜索 C++ 和STL中的隐式转换,以获得一些反对它的论点......

解决方案 27:

来自核心 javascript 参考

===`true`如果操作数严格相等(参见上文)且无需进行类型转换,则返回。

解决方案 28:

相等性比较:

操作员==

当两个操作数相等时返回 true。比较之前,操作数会转换为相同类型。

>>> 1 == 1
true
>>> 1 == 2
false
>>> 1 == '1'
true

平等和类型比较:

操作员===

如果两个操作数相等且类型相同,则返回 true。通常,以这种方式进行比较会更好、更安全,因为没有幕后类型转换。

>>> 1 === '1'
false
>>> 1 === 1
true

解决方案 29:

下面是一个方便的比较表,显示了发生的转换以及==和之间的差异===

正如结论所述:

“除非您完全理解二等分的转换,否则请使用三等分。”

http://dorey.github.io/JavaScript-Equality-Table/

解决方案 30:

null 和 undefined 是虚无,也就是说

var a;
var b = null;

这里ab没有值。而 0、false 和 '' 都是值。它们之间的共同点是它们都是假值,这意味着它们都满足假条件。

因此,0、false 和 '' 一起构成一个子组。另一方面,null 和 undefined 构成第二个子组。检查下图中的比较。null 和 undefined 相等。其他三个彼此相等。但是,在 JavaScript 中,它们都被视为假条件。

在此处输入图片描述

这与任何对象(如 {}、数组等)相同,非空字符串和布尔值 true 都是真值条件。但是,它们并不相等。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用