javascript中的delete使用詳解

來源:互聯網
上載者:User

在這篇文章中作者從《JavaScript物件導向編程指南》一書中關於 delete 的錯誤講起,詳細講述了關於 delete 操作的實現, 局限以及在不同瀏覽器和外掛程式(這裡指 firebug)中的表現。

下面翻譯其中的主要部分。

...書中聲稱

“函數就像一個普通的變數那樣——可以拷貝到不同變數,甚至被刪除”

並附上了下面的程式碼片段作為說明:

複製代碼 代碼如下:>>> var sum = function(a, b) {return a+b;};
>>> var add = sum;
>>> delete sum;
true
>>> typeof sum;
"undefined"

你能發現片段中的問題嗎? 這個問題就是——刪除 sum 變數的操作不應該成功; delete 的聲明不應該返回 true 而 typeof sum 也不應該返回為 undefined。 因為,javascript 中不能夠刪除變數,至少不能以這個方式聲明刪除。

那麼這個例子發生了什嗎? 是列印錯誤或者玩笑? 應該不是。 這個片段是 firebug 控制台中的一個實際輸出,而 Stoyan(上面所說書的作者)應該正是用它做的快速測試。 這彷彿說明了 firebug 有一些不同的刪除規則。 正是 firebug 誤導了 Stoyan! 那麼這裡面究竟是怎麼回事呢?

為了回答這個問題,我們需要瞭解 delete 運算子在 Javascript 中是如何工作的: 哪些可以被刪除,哪些不能刪除以及為什麼。 下面我試著解釋一下這方面的細節。 我們將通過觀察 firebug 的“奇怪”的表現而認識到它實際上完全不“奇怪”; 我們將深入瞭解那些,當我們聲明變數、函數,賦值屬性和刪除它們時的,隱藏在背後的細節; 我們將看一下瀏覽器對此的實現和一些有名的 bug; 我們還會討論到 ECMAScript 版本 5 中的strict 模式(strict mode)以及它如何改變 delete 運算子的行為。

我在下面交替使用的 Javascript 和 ECMPScript 一般都指 ECMAScript(除非當明確談到 Mozilla 的 JavaScript 實現時)。

意料之中的,網路上目前對於 delete 的解釋非常少(筆者按:這篇文章寫於 2010 年 1 月)。 MDC(MDN]) 的資源大概是這其中最詳細的了,但不幸的是它遺漏了一些有趣的細節,這些細節中就包括了上述 firebug 的奇怪表現。MSDN 文檔幾乎沒什麼用處。

一、理論 | Theory
那麼,為什麼我們能刪除一個對象的屬性:

複製代碼 代碼如下:var x = { a: 1 };
delete x.a; // true
x.a; // undefined

但卻不能刪除一個變數: 複製代碼 代碼如下:var x = 1;
delete x; // false;
x; // 1

也不能刪除一個函數: 複製代碼 代碼如下:function x() {};
delete x; // false;
typeof x; // "function"

注意:delete 只有當一個屬性無法被刪除時才返回 false。

為了理解這一點,我們需要首先把握一些概念: 變數執行個體化(variable instantiation)和屬性的內部屬性(property attributes) (譯者按:關於 property 和 attributes 的區別見參考文章,根據下面涉及到的內容,擬譯成內部屬性) ——這些很少在 javascript 書中被提到。 在下面幾段中我將試著簡短地回顧這些內容,要理解它們並不難。 如果你並不關注它們表現背後的原因,可以跳過這一章。

1.1、代碼的類型 | Type of code

ECMAScript 中有三類可執行代碼:

1.全域代碼 Global code
2.函數代碼 Function code
3.Eval code

這幾類的含義大致就像它們命名的那樣,但還是快速地回顧一下:

1.當一個源檔案被看做是一個程式,它在全域範圍(scope)內執行,而這就被認為是一段全域代碼 Global code。 在瀏覽器環境下,SCRIPT 元素的內容通常都被解析為一個程式,因而作為全域代碼來執行。

2.當然,任何在一段函數中直接執行的代碼就被認為是一段函數代碼 Function code, 在瀏覽器環境下,事件屬性的內容(e.g. <a onclick="...")通常都作為函數代碼來解析和執行。

3.最後,放入內建函數 eval 中的代碼就作為 Eval code 來解析。 我們將很快看到為什麼這一類型是特殊的。

1.2、代碼執行的上下文 | Execution Context
當 ECMAScript 代碼執行時,它總是發生在一個確定的執行內容(context)中。 執行範圍是一個抽象實體,它有助於理解範圍和變數執行個體化的工作原理。 上面三類可執行代碼都有各自的執行內容。 當函數代碼執行時,我們說控制端進入了函數代碼的執行內容; 當全域代碼執行時,我們說控制端進入了全域代碼的執行內容,以此類推。

正如你所見,執行內容在邏輯上是一個棧(stack)。 首先可能有一段全域代碼,它擁有屬於自己的執行內容; 在這段代碼中可能調用一個函數,這個函數同樣擁有屬於自己的執行內容; 這個函數可能調用另一個函數,等等。 即使當函數遞迴調用自己時,在每一步調用中仍然進入了不同的執行內容。

1.3、活化對象和變數對象 | Activation object / Variable object
每一個執行內容都有一個與之相關聯的變數對象(Variable object)。 和它相似的,變數對象也是一個抽象實體,一種用來描述變數執行個體化的機制。 而有趣的是,在一段原始碼中聲明的變數和函數事實上被作為變數對象(Variable object)的屬性(properties)而添加到變數對象中。

當控制進入了全域代碼的執行內容時,一個全域對象被用作變數對象。 這恰恰是為什麼全域聲明的變數和函數變成一個全域對象的屬性的原因:

複製代碼 代碼如下:var GLOBAL_OBJECT = this;
var foo = 1;
GLOBAL_OBJECT.foo; // 1
function bar() {};
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true

Ok, 所以全域變數成了全域函數的屬性,那麼局部變數——那些在函數代碼(Function code)中聲明的變數呢? 事實上那很簡單:他們也成了變數對象的屬性。 唯一的區別是,在函數代碼中,變數對象不是一個全域對象, 而是一個我們稱之為活化對象(Activation object)。 每次進入函數代碼的執行內容時都會建立一個活化對象。

並非只有在函數代碼中聲明的變數和函數才成為活化對象的屬性: 函數的每一個實參(arguments,以各自相對應的形參的名字為屬性名稱), 以及一個特殊的Arguments對象(以arguments為屬性名稱)同樣成為了活化對象的屬性。 需要注意的是,活化對象作為一個內部的機制事實上不能被程式碼所訪問。

複製代碼 代碼如下:(function(foo) {
var bar = 2;
function baz() {};
/*
在抽象的過程中,
特殊的'arguments'對象變成了所在函數的活化對象的屬性:
ACTIVATION_OBJECT.arguments = arguments;
...參數'foo‘也是一樣:
ACTIVATION_OBJECT.foo; // 1
...變數'bar'也是一樣:
ACTIVATION_OBJECT.bar; // 2
...函數'baz'也是一樣:
typeof ACTIVATION_OBJECT.baz; // "function"
*/
}) (1);

最後,Eval code 中聲明的變數成為了內容相關的變數對象(context's Variable object)的屬性。 Eval code 簡單地使用在它調用中的執行內容的變數對象。

複製代碼 代碼如下:var GLOBAL_OBJECT = this;
eval('var foo = 1');
GLOBAL_OBJECT.foo; // 1;

(function() {
eval('var bar = 2');

/*
在抽象過程中
ACTIVATION_OBJECT.bar; // 2
*/
}) ();

1.4、屬性的內部屬性 | Property attributes
就要接近主題了。 現在我們明確了變數發生了什麼(它們成了屬性),剩下的需要理解的概念就是屬性的內部屬性(property attributes)。 每一個屬性擁有零至多個如內部屬性——*ReadOnly,DontEnum,DontDelete和Internal**。 你可以把它們想象為標籤——一個屬性可能擁有也可能沒有某個特殊的內部屬性。 在今天的討論中,我們所感興趣的是 DontDelete。

當聲明變數和函數時,它們成為了變數對象(Variable object)——要麼是活化對象(在函數代碼中), 要麼是全域對象(在全域代碼中)——的屬性,這些屬性伴隨產生了內部屬性 DontDelete。 然而,任何顯式/隱式賦值的屬性不產生 DontDelete。 而這就是本質上為什麼我們能刪除一些屬性而不能刪除其他的原因。

複製代碼 代碼如下:var GLOBAL_OBJECT = this;

/* 'foo'是全域對象的一個屬性,
它通過變數聲明而產生,因此擁有內部屬性DontDelete
這就是為什麼它不能被刪除*/
var foo = 1;
delete foo; // false
typeof foo; // "number"

/* 'bar'是全域對象的一個屬性,
它通過變數聲明而產生,因此擁有DontDelete子
這就是為什麼它同樣不能被刪除*/
function bar() {};
delete bar; // false
typeof bar; // "function"

/* 'baz'也是全域對象的一個屬性,
然而,它通過屬性賦值而產生,因此沒有DontDelete
這就是為什麼它可以被刪除*/
GLOBAL_OBJECT.baz = "baz";
delete GLOBAL_OBJECT.baz; // true
typeof GLOBAL_OBJECT.baz; // "undefined"

1.5、內建和DontDelete | Build-ins and DontDelete

所以這就是所有這一切發生的原因:屬性的一個特殊的內部屬性控制著該屬性是否可以被刪除。 注意:內建對象的一些屬性擁有內部屬性 DontDelete,因此不能被刪除; 特殊的 arguments 變數(如我們所知的,活化對象的屬性)擁有 DontDelete; 任何函數執行個體的 length (返回形參長度)屬性也擁有 DontDelete:

複製代碼 代碼如下:(function() {
//不能刪除'arguments',因為有DontDelete
delete arguments; // false;
typeof arguments; // "object"

//也不能刪除函數的length,因為有DontDelete
function f() {};
delete f.length; // false;
typeof f.length; // "number"
}) ();

與函數 arguments 相關聯的屬性也擁有 DontDelete,同樣不能被刪除

複製代碼 代碼如下:(function(foo,bar) {
delete foo; // false
foo; // 1

delete bar; // false
bar; // "bah"
}) (1,"bah");

1.6、未聲明的變數賦值 | Undeclared assignments
你可能記得,未聲明的變數賦值會成為全域對象的屬性,除非這一屬性在範圍鏈內的其他地方被找到。 而現在我們瞭解了屬性賦值和變數聲明的區別——後者產生 DontDelete 而前者不產生——這也就是為什麼未聲明的變數賦值可以被刪除的原因了。

複製代碼 代碼如下:var GLOBAL_OBJECT = this;

/* 通過變數聲明產生全域對象的屬性,擁有DontDelete */
var foo = 1;

/* 通過未聲明的變數賦值產生全域對象的屬性,沒有DontDelete */
bar = 2;

delete foo; // false
delete bar; // true
注意:內部屬性是在屬性產生時確定的,之後的賦值過程不會改變已有的屬性的內部屬性。 理解這一區別是重要的。

/* 'foo'建立的同時產生DontDelete */
function foo() {};

/* 之後的賦值過程不改變已有屬性的內部屬性,DontDelete仍然存在 */
foo = 1;
delete foo; // false;
typeof foo; // "number"

/* 但賦值一個不存在的屬性時,建立了一個沒有內部屬性的屬性,因此沒有DontDelete */
this.bar = 1;
delete bar; // true;
typeof bar; // "undefined"

二、Firebug 的混亂 | Firebug confusion

那麼, firebug 中發生了什嗎? 為什麼在控制台中聲明的變數能夠被刪除,而不是想我們之前討論的那樣? 我之前說過,Eval code 在它處理變數聲明時有一個特殊的行為: 在 Eval code 中聲明的變數事實上產生一個沒有 DontDelete 的屬性。

複製代碼 代碼如下:eval('var foo = 1;');
foo; // 1
delete foo; // true
typeof foo; // "undefined"

在函數代碼中也是一樣:

複製代碼 代碼如下:(function() {
eval('var foo = 1;');
foo; // 1
delete foo; // true
typeof foo; // "undefined"
}) ();

而這就是 Firebug 中異常行為的原因了。 所有在控制台中的調試文本似乎是以 Eval code 來編譯和執行的,而不是在全域或函數代碼中執行。 顯然,其中的變數聲明最終都產生了不帶 DontDelete 的屬性,所以可以被刪除。 所以要小心普通的全域代碼和 Firebug 控制台中代碼的區別。

2.1、通過eval刪除變數 | Delete variables via eval
這個有趣的 eval 行為,結合 ECMAScript 的另一個方面可以在技術上允許我們刪除那些原本不能刪除的屬性。 這個方面是關於函式宣告——在相同的執行內容中它們能覆蓋同名的變數:

複製代碼 代碼如下:function x() { };
var x;
typeof x; // “function”

那麼為什麼函式宣告擁有優先權而能覆蓋同名變數(或者換句話說,變數對象(Variable object)的相同屬性)呢? 這是因為函式宣告的執行個體化過程在變數聲明之後,因此可以覆蓋它們。

(譯者按:函式宣告只能覆蓋聲明而未賦值的同名變數,如果在聲明時賦值了值(e.g. var x = 1)則賦值值的過程在函數初始化之後,函式宣告反而被變數賦值所覆蓋,如下:)

複製代碼 代碼如下:var x = 1;
function x() { };
typeof x; // "number"

函式宣告不止替換了屬性的值,同時也替換了它的內部屬性。 如果我們通過 eval 來聲明函數,這一函數也將用它自己的內部屬性來替換之前的。 而由於在 eval 中聲明的變數產生的屬性沒有 DontDelete, 執行個體化這個函數將在“理論上”移除原屬性已有的 DontDelete 內部屬性, 而使得這一屬性可以刪除(當然,同時也將值指向了新產生的函數)。

複製代碼 代碼如下:var x = 1;
/*不能刪除,‘x'擁有DontDelete*/
delete x; // false
typeof x; // "number"

eval('function x() { }');
/* 屬性'x'現在指向函數,並且應該沒有DontDelete */
typeof x; // "function"
delete x; // 應該是‘true';
typeof x; // 應該是"undefined"

不幸的是,這種欺騙技術在我嘗試的各個瀏覽器中都沒有成功。 這裡我可能錯過了什麼,或者這個行為太隱蔽而以至於各個瀏覽器沒有注意到它。

(譯者按:這裡的問題可能在於:函式宣告和變數聲明之間的覆蓋只是值指向的改變, 而內部屬性 DontDelete 則在最初聲明處確定而不再改變,而 eval 中聲明的變數和函數,也只是在其外部內容中未聲明過的那部分才能被刪除。 關於執行順序,由於 eval 作為函數,它的調用永遠在其外部內容中其他變數和函式宣告之後, 因此相關的內部屬性也已確定,覆蓋的只是值的指向。如下:)

複製代碼 代碼如下:/* 第一個 alert 返回 “undefined”,因為賦值過程在聲明過程和eval執行過程之後;
第二個alert返回 “false”, 因為儘管x聲明的位置在eval之後,
但是eval的執行卻在變數聲明之後,因此已無法刪除 */
eval(' alert( x ); alert(delete x) ');
var x = 1;

三、瀏覽器的遵守情況 | Browsers compliance

瞭解事物的工作原理是重要的,但實際的實現情況更重要。 瀏覽器在建立和刪除變數/屬性時都遵守這些標準嗎? 對於大部分來說,是的。

我寫了一個簡單的測試單元來檢查全域代碼、函數代碼和Eval代碼的遵守情況。 測試單元同時檢測了 delete 操作的傳回值和屬性是否像預期那樣被刪除。 delete 的傳回值並不像它的實際結果那樣重要,delete 操作返回 true 或 false 並不重要, 重要的是擁有/沒有 DontDelete 的屬性是否被刪除。

現代瀏覽器總的來說還是遵守刪除規則的,以下瀏覽器全部通過測試: Opera 7.54+, Firefox 1.0+, Safari 3.1.2+, Chrome 4+。

Safari 2.x 和 3.0.4 在刪除函數 arguments 時存在問題,似乎這些屬性在建立時不帶 DontDelete,因此可以被刪除。 Safari 2.x 還有其他問題——刪除無引用時(例如delete 1)拋出錯誤(譯者按:IE 同樣有); 函式宣告產生了可刪除的屬性(奇怪的是變數聲明則正常); eval 中的變數聲明變成不可刪除(而 eval 中的函式宣告則正常)。

與 Safari 類似,Konqueror(3.5,而非4.3)在 delete 無引用和刪除 arguments 是也存在同樣問題。

3.1、Gecko DontDelete bug
Gecko 1.8.x 瀏覽器—— Firefox 2.x, Camino 1.x, Seamonkey 1.x, etc. ——存在一個有趣的 bug:顯式賦值值給一個屬效能移除它的 DontDelete,即使該屬性通過變數或函式宣告而產生。

複製代碼 代碼如下:function foo() { };
delete foo; // false;
typeof foo; // "function"

this.foo = 1;
delete foo; // true
typeof foo; // "undefined"

令人驚訝的是,IE5.5-8 也通過了絕大部分測試,除了刪除非引用拋出錯誤(e.g. delete 1,就像舊的 Safari)。 但是,雖然不能馬上發現,事實上 IE 存在更嚴重的 bug,這些 bug 是關於全域對象。

四、IE bugs

在 IE 中(至少在 IE6-8 中),下面的運算式拋出異常(在全域代碼中):

複製代碼 代碼如下:this.x = 1;
delete x; // TypeError: Object doesn't support this action

而下面則是另一個:

複製代碼 代碼如下:var x =1;
delete this.x; // TypeError: Cannot delete 'this.x'
// 譯者按:在IE8下拋出此異常,在IE6,7下拋出的是和上面一樣的異常

這似乎說明,在 IE 中在全域代碼中的變數聲明並沒有產生全域對象的同名屬性。 通過賦值建立的屬性(this.x = 1)然後通過 delete x 刪除時拋出異常; 通過變數聲明(var x = 1)建立的屬性然後通過 delete this.x 刪除時拋出另一個(譯者按:在 IE6,7 下錯誤資訊與上面的相同)。

但不只是這樣,事實上通過顯式賦值建立的屬性在刪除時總是拋出異常。 這不只是一個錯誤,而是建立的屬性看上去擁有了 DontDelete 內部屬性,而按規則應該是沒有的:

複製代碼 代碼如下:this.x = 1;
delete this.x; // TypeError: Object doesn't support this action
delete x; // TypeError: Object doesn't support this action

另一方面,未聲明的變數賦值(那些同樣產生全域對象的屬性)又確實在IE下能夠正常刪除:

複製代碼 代碼如下:x = 1;
delete x; // true

但如果你試圖通過 this 關鍵字來進行刪除(delete this.x),那麼上面的異常又將拋出:

複製代碼 代碼如下:x = 1;
delete this.x; //TypeError: Cannot delete 'this.x'

如果歸納一下,我們將發現在全域代碼中‘delete this.x'永遠不會成功。 當通過顯式賦值來產生屬性(this.x = 1)時拋出一個異常; 當通過聲明/非聲明變數的方式(var x = 1 or x = 1)產生屬性時拋出另一個異常。 而另一方面,delete x 只有在顯示賦值產生屬性(this.x = 1)時才拋出異常。

在 9 月我討論了這個問題,其中 Garrett Smith 認為在 IE 中全域變數對象(Global variable object)實現為一個 JScript 對象,而全域對象則由宿主對象實現。

我們能通過幾個測試來在某種程度上確認這一理論。 注意,this 和 window 似乎引用同一個對象(如果 ‘==='運算子可以信任的話), 而變數對象 Variable object (函式宣告的基礎)則與 this 引用的不同。

複製代碼 代碼如下:function getBase() { return this; };

getBase() === this.getBase(); // false
this.getBase() === this.getBase(); // true
window.getBase() === this.getBase(); // true
window.getBase() === getBase(); // false

五、誤解 | Misconceptions

我們不能低估理解事物工作原理的重要性。 我看過網路上一些關於 delete 操作的誤解。 例如,Stackoverflow 上的一個答案(而且等級還很高),裡面解釋說“delete is supposed to be no-op when target isn't an object property”。 現在我們瞭解了 delete 操作的核心,也就清楚了這個答案是不正確的。 delete 不區分變數和屬性(事實上在 delete 操作中這些都是引用),而只關心 DontDelete(以及屬性是否已經存在)。

六、'delete'和宿主對象 | 'delete‘ and host object

一個 delete 的演算法大致像這樣:

1. 如果運算元(operand)不是引用,返回 true
2. 如果對象沒有同名的**直接屬性**,返回 true (如我們所知,對象可以是全域對象也可以是活化對象)
3. 如果屬性已經存在但有 DontDelete,返回 false
4. 否則,刪除移除屬性並返回 true
然而,對於宿主對象(host object)的 delete 操作的行為卻可能是不可預料的。 而事實上這並沒有錯:宿主對象(通過一定規則)允許實現任何操作, 例如讀(內部[[Get]]方法)、寫(內部[[Write]]方法)、刪除(內部[[Delete]]方法),等等。 這種允許自訂[[Delete]]行為導致了宿主對象的混亂。

我們已經看到了在IE中的一些問題:當刪除某些對象(那些實現為了宿主對象)屬性時拋出異常。 一些版本的 firefox 當試圖刪除 window.location 時拋出異常(譯者按:IE 同樣拋出)。 同樣,在一些宿主對象中你也不能相信 delete 的傳回值, 例如下面發生在 firefox 中的(譯者按:chrome 中同樣結果;IE 中拋出異常;opera 和 safari 允許刪除,並且刪除後無法調用,姑且算'正常‘,儘管,從下面的討論來看似乎卻是不正常的,它們事實上刪除了不能刪除的屬性,而前面的瀏覽器沒有):

複製代碼 代碼如下:/* 'alert'是'window‘的一個直接屬性(如果我們能夠相信'hasOwnProperty') */
window.hasOwnProperty('alert'); // true

delete window.alert; // true
typeof window.alert; // "function"

delete window.alert 返回 true,儘管這個屬性沒有任何條件可能產生這個結果(按照上面的演算法): 它解析為一個引用,因此不能在第一步返回 true; 它是 window 對象的直接屬性,因此不能在第二步返回 true; 唯一能返回 true 的是當演算法達到最後一步同時確實刪除這個屬性,而事實上它並沒有被刪除。 (譯者按:不,在 opera 和 safari 中確實被刪除了...)。

所以這個故事告訴我們永遠不要相信宿主對象。

七、ES5 strict 模式 | ES5 strict mode

那麼 ECMAScript 第 5 版中的strict 模式將帶來什嗎? 目前介紹了其中的一些限制。 當刪除操作指向一個變數/函數參數/函式宣告的直接引用時拋出 SyntaxError。 此外,如果屬性擁有內部屬性[[Configurable]] == false,將拋出 TypeError:

複製代碼 代碼如下:(function(foo) {
"use strict"; //在函數中開啟strict 模式

var bar;
function baz;
delete foo; // SyntaxError,當刪除函數參數時
delete bar; // SyntaxError,當刪除變數時
delete baz; // SyntaxError,當刪除由函式宣告建立的變數時

/* function執行個體的length擁有[[Configurable]] : false */
delete (function() {}).length; // TypeError
}) ();

而且,在strict 模式下,刪除未聲明的變數(換句話說,未解析的引用),同樣拋出 SyntaxError; 與它類似的,相同模式下未聲明的賦值也將拋出異常(ReferenceError)

複製代碼 代碼如下:"use strict";
delete i_dont_exist; // SyntaxError
i_dont_exist_either = 1; // ReferenceError

看了之前給出的變數、函式宣告和參數的例子,相信現在你也理解了,所有這些限制都是有其意義的。 strict 模式採取了更積極的和描述性的措施,而不只是忽略這些問題。

八、總結 | Summary

由於這篇文章已經很長了,因此我就不再討論另一些內容(e.g.通過 delete 刪除數組項及其影響)。 你可以翻閱 MDC/MDN 上的文章或閱讀規範然後自己測試。

下面是關於 Javascript 中 delete 如何工作的一個簡單的總結:

•變數和函式宣告都是活化(Activation)全域(Global)對象的屬性。
•屬性擁有內部屬性,其中一個—— DontDelete 負責確定一個屬性是否能夠被刪除。
•全域代碼或函數代碼中的變數、函式宣告都產生擁有 DontDelete 的屬性。
•函數參數同樣是活化對象的屬性,也擁有 DontDelete。
•Eval代碼中的變數和函式宣告都產生沒有 DontDelete 的屬性。
•新的未聲明的屬性在產生時帶空的內部屬性,因此也沒有 DontDelete。
•宿主對象允許以任何它們期望的方式來響應刪除過程。

原文:Understanding delete譯文:javascript 中的 delete譯者:justjavac

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.