JavaScript對象類型詳解

《JavaScript高級程序設計》已經學習到了第四章,不過因為第五章講的都是各種對象類型,所以在進行第五章的學習之前,先深入了解一下對象是有好處的。

JavaScript Objects in Detail

關于對象類型的方方面面在這篇文章里都寫得很清楚了,本著不重復造輪子的原則,我這里也不打算再重新寫一篇了,更何況,我這新手寫出來的文章肯定也跟人家的沒得比。

鑒于很多朋友可能對英文不是很感興趣,所以這里準備把文章翻譯過來。不過提前聲明,本人沒有什么翻譯經驗,翻譯出來的文章可能水平欠佳。如果英文比較好的話,建議直接看原文。畢竟是新的嘗試,各位高手如果有啥建議或者意見可以在評論提出,但請勿無故亂噴。


JavaScript對象類型詳解

JavaScrtip有六種數據類型,一種復雜的數據類型(引用類型),即Object對象類型,還有五種簡單的數據類型(原始類型):NumberStringBooleanUndefinedNull。其中,最核心的類型就是對象類型了。同時要注意,簡單類型都是不可變的,而對象類型是可變的。

什么是對象

一個對象是一組簡單數據類型(有時是引用數據類型)的無序列表,被存儲為一系列的名-值對(name-value pairs)。這個列表中的每一項被稱為 屬性(如果是函數則被稱為 方法)。

下面是一個簡單的對象:

    var myFirstObject = { firstName: "Richard",favoriteAuthor: "Conrad"};

可以把對象考慮成一個列表,列表中的每一項(屬性或方法)都以名-值對的方式存儲。上面例子中,對象的屬性名就是firstNamefavortieAuthor,相應的,對象的屬性值為RichardConrad

屬性名可以是字符串或者數字,但是如果以數字作為屬性名,則必須以方括號(方括號記法)來獲得這個數字屬性名對應的屬性值。稍后有方括號記法的更詳細解釋。下面是一個方括號記法的例子:

    var ageGroup = {30: "Children", 100:"Very Old"};console.log(ageGroup.30) // 報錯?// 訪問30這個屬性的正確方法console.log(ageGroup["30"]); // Children???//最好避免使用數字作為屬性名

作為一個JavaScript程序員,你會經常使用到對象數據類型。一般用它來儲存數據,或者創建自定義的方法或函數。

引用數據類型和原始數據類型

引用類型與原始類型最主要的一個不同點就是引用類型是按引用存儲的,它不會像原始類型一樣,將值直接存儲在變量中。比如:

    // 原始類型數據是按值存儲的 ?var person = "Kobe";  ?var anotherPerson = person; // anotherPerson = the value of person?person = "Bryant"; // person的值改變了?console.log(anotherPerson); // Kobe?console.log(person); // Bryan

可以注意到,即使我們將person的值改為"Bryant",對anthoerPerson也會不有絲毫影響,它仍然保存了原本person賦給它的值。

將原始類型的按值存儲跟引用類型的按引用存儲進行一下比較:

    var person = {name: "Kobe"};?var anotherPerson = person;person.name = "Bryant";?console.log(anotherPerson.name); // Bryant?console.log(person.name); // Bryant

在這個例子中,我們將person對象復制給了anthoerPerson,但是由于person對象中存儲的是引用而不是真正的值。所以當我們將person.name改變為"Bryant"的時候,anotherPerson變量也反應出了這個變化,因為它并沒有將person中的所有屬性都復制一份保存起來,而是直接保存了對象的引用。

對象屬性的特性(Attributes)

注:Attribute一般也是翻譯為屬性,但是為了跟Propertie(也翻譯為屬性)進行區分,這里將其翻譯為特性,這也是咨詢過別人的,應該無傷大雅

每個對象屬性不止保存了自身的名-值對,它同時還包含了三個特性,這三個特性默認被設置為true。

  • Configurable Attribute: 指定這個對象屬性是否可以被刪除或修改。
  • Enumerable:指定這個對象屬性在for-in循環中是否可以被取得。
  • Writable:指定這個對象屬性是否可以被修改。

在EMACScript 5中有一些新的特性,這里不做詳細講解。

創建對象

創建對象有兩種比較常用的方法:

  1. 對象字面量
    這是創建對象最常用,也是最簡單的方式,直接使用字面量進行創建:

     // 空對象?var myBooks = {};??// 使用字面量創建的包含4個屬性的對象?var mango = {color: "yellow",shape: "round",sweetness: 8,??    howSweetAmI: function () {console.log("Hmm Hmm Good");}}
  2. 對象構造函數
    第二種常用的方法是使用對象構造函數。構造函數是一種可以用來創建新對象的特殊函數,要使用new關鍵字來調用構造函數。

     var mango =  new Object ();mango.color = "yellow";mango.shape= "round";mango.sweetness = 8;?mango.howSweetAmI = function () {console.log("Hmm Hmm Good");}

雖然可以使用某些保留字或關鍵字,比如for作為對象屬性的名稱,不過這可不是一個明智的選擇。

對象的屬性可以包含任何數據類型,包括NumberArrays,甚至是其它的Object

對象創建的實踐模式

對于創建只使用一次的用于存儲數據的簡單對象,上面的兩種方法就可以滿足需求。

但是,假設有一個程序用于展示水果和它的詳細信息。程序中的每個水果類型都有如下對象屬性:color, shape, sweetness, cost 和一個showName函數。要是每次創建一個新的水果對象時,都得敲一遍下面的代碼,那將是十分乏味和低效率的。

    var mangoFruit = {color: "yellow",sweetness: 8,fruitName: "Mango",nativeToLand: ["South America", "Central America"],??showName: function () {console.log("This is " + this.fruitName);},?nativeTo: function () {this.nativeToLand.forEach(function (eachCountry)  {console.log("Grown in:" + eachCountry);});}}

如果你有10個水果,你就得添加10次相同的代碼。并且,如果想修改nativeTo函數,就得在10個不同的地方進行修改。再進一步推想,如果你在開發一個大型網站,你為上面的對象都一個一個添加了屬性。但是,你突然發現你創建對象的方式不是很理想,你想要進行修改,這時又該怎么辦。

為了解決這些重復性的問題,軟件工程師們發明了各種模式(對于重復問題和常見任務的解決方案),使用開發程序更有效率和合理化。

下面是兩種創建對象的常用模式:

  1. 構造方法模式

     function Fruit (theColor, theSweetness, theFruitName, theNativeToLand) {this.color = theColor;this.sweetness = theSweetness;this.fruitName = theFruitName;this.nativeToLand = theNativeToLand;this.showName = function () {console.log("This is a " + this.fruitName);}this.nativeTo = function () {this.nativeToLand.forEach(function (eachCountry)  {console.log("Grown in:" + eachCountry);});}}

    使用這種模式,很容易就可以創建出各式各樣的水果來。像這樣:

     var mangoFruit = new Fruit ("Yellow", 8, "Mango", ["South America", "Central America", "West Africa"]);mangoFruit.showName(); // This is a Mango.?mangoFruit.nativeTo();?//Grown in:South America??// Grown in:Central America??// Grown in:West Africa??var pineappleFruit = new Fruit ("Brown", 5, "Pineapple", ["United States"]);pineappleFruit.showName(); // This is a Pineapple.

    如果你要改變屬性或方法,你只需要在一個地方進行修改就可以了。這個模式通過一個Fruit函數的繼承,封裝了所有水果的功能和特性。

    注意:

    • 可繼承的屬性需要定義在對象的prototype對象屬性上。比如

        someObject.prototype.firstName = "rich";
    • 屬于自身的屬性要直接定義在對象的上。比如:

        // 首先,創建一個對象var aMango = new Fruit ();// 接著,直接在對象上定義mongoSpice方法// 因為我們直接在對象身上定義了mangoSpice屬性,所以它是aMango自身的屬性,不是一個可繼承的屬性aMango.mangoSpice = “some value”;
    • 要訪問一個對象的屬性,使用object.property,如:

        console.log(aMango.mangoSpice); // "some value"
    • 要調用一個對象的方法,使用object.method(),如:

        // 首先,增加一個方法aMango.printStuff = function() { return "Printing"; }// 現在,可以調用printStuff方法aMango.printStuff(); 
  2. 原型模式

     function Fruit () {}Fruit.prototype.color = "Yellow";Fruit.prototype.sweetness = 7;Fruit.prototype.fruitName = "Generic Fruit";Fruit.prototype.nativeToLand = "USA";Fruit.prototype.showName = function () {console.log("This is a " + this.fruitName);}Fruit.prototype.nativeTo = function () {console.log("Grown in:" + this.nativeToLand);}

    下面是在原型模式中調用Fruit()構造函數的方法:

     var mangoFruit = new Fruit ();mangoFruit.showName(); //?mangoFruit.nativeTo();?// This is a Generic Fruit??// Grown in:USA

擴展閱讀

如果需要了解這兩種模式的更詳細的解釋,可以閱讀《JavaScript高級程序設計》的第六章,其中詳細討論了這兩種方法的優缺點。書中還討論了除這兩個外的其它模式。

如何訪問對象中的屬性

訪問對象屬性的兩種主要方法是點記法(dot notation)和中括號記法(bracket notation)。

  1. 點記法

     // 這是我們前面例子中一直使用的訪問屬性的方法?var book = {title: "Ways to Go", pages: 280, bookMark1:"Page 20"};??// 使用點記法訪問book對象的title和pages屬性:?console.log ( book.title); // Ways to Go?console.log ( book.pages); // 280
  2. 中括號記法

     // 使用方括號啟示訪問book對象的屬性:console.log ( book["title"]); //Ways to Go?console.log ( book["pages"]); // 280???//如果屬性名儲存在一個變量當中,也可以這樣:??var bookTitle = "title";console.log ( book[bookTitle]); // Ways to Go?console.log (book["bookMark" + 1]); // Page 20

訪問一個對象中不存在的屬性會得到一個undefined

自身屬性和繼承屬性

對象擁有自身屬性和繼承屬性。自身屬性是直接定義在對象上的屬性,而繼承屬性是從ObjectPrototype繼承的屬性。

為了確寫一個對象是否擁有某個屬性(不管是自身屬性還是繼承屬性),可以使用in操作符:

    // 創建一個有schoolName屬性的對象var school = {schoolName:"MIT"};?// 打印出true,因為對象擁有schoolName這個屬性console.log("schoolName" in school);  // true???// 打印出false,因為我們既沒有定義schoolType屬性,也沒有從Object的Prototype中繼承schoolType屬性console.log("schoolType" in school);  // false??// 打印出true, 因為從Object的Prototype中繼承了toString方法console.log("toString" in school);  // true

hasOwnProperty

為了確定一個對象是否擁有一個特定的自身屬性,可以使用hasOwnPrototype方法。這個方法十分有用,因為我們經常需要枚舉一個對象的所有自身屬性,而不是繼承屬性。

    // 創建一個擁有schoolName屬性的對象?var school = {schoolName:"MIT"};??// 打印出true,因為schooName是school的自身屬性console.log(school.hasOwnProperty ("schoolName"));  // true??// 打印出false,因為toString是從Object的Prototype中繼承下來的,并且school的自身屬性console.log(school.hasOwnProperty ("toString"));  // false 

訪問和枚舉對象中的屬性

為了訪問對象中可以枚舉的屬性(自身或者繼承的),可以使用for-in循環或普通的循環方式。

    // 創建擁有3個屬性的school對象: schoolName, schoolAccredited, and schoolLocation.??var school = {schoolName:"MIT", schoolAccredited: true, schoolLocation:"Massachusetts"};??//使用for-in循環獲取對象中的屬性?for (var eachItem in school) {console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation?}

訪問繼承的屬性

ObjectPrototype中繼承的屬性不可枚舉的,所以在for-in循環中不會訪問到這些屬性。然而,如果是可枚舉的繼承屬性,它們也是能夠從for-in循環中訪問到的。

比如:

    //使用for-in循環訪問school對象中的屬性for (var eachItem in school) {console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation?}// 注:以下這段說明是原文的說明/* SIDE NOTE: As Wilson (an astute reader) correctly pointed out in the comments below, the educationLevel property is not actually inherited by objects that use the HigherLearning constructor; instead, the educationLevel property is created as a new property on each object that uses the HigherLearning constructor. The reason the property is not inherited is because we use of the "this" keyword to define the property.*/?// Create a new HigherLearning function that the school object will inherit from.?function HigherLearning () {this.educationLevel = "University";}// Implement inheritance with the HigherLearning constructor?var school = new HigherLearning ();school.schoolName = "MIT";school.schoolAccredited = true;school.schoolLocation = "Massachusetts";//Use of the for/in loop to access the properties in the school object?for (var eachItem in school) {console.log(eachItem); // Prints educationLevel, schoolName, schoolAccredited, and schoolLocation?}

刪除對象中的屬性

可以使用delete操作符來刪除對象中的屬性。我們不能刪除繼承的屬性,同時也不能刪除Configurable特性被設置為false的對象屬性。要刪除繼承的屬性,必須從Prototype對象中刪除(也就是定義這些屬性的地方)。并且,我們也不能刪除全局對象中的屬性。

刪除成功的時候,delete操作符會返回true。令人意外的是,當要刪除的屬性不存在,或者不能被刪除(即不是自身的屬性或者Configurable特性被設置為false)時, delete操作符也會返回true

以下是示例:

    var christmasList = {mike:"Book", jason:"sweater" }delete christmasList.mike; // deletes the mike property?for (var people in christmasList) {console.log(people);}// Prints only jason?// The mike property was deleted?delete christmasList.toString; // 返回 true, 但是因為toString是繼承的屬性,所以它不會被刪除// 因為toString沒有被刪除,所以這里還能夠正常使用christmasList.toString(); //"[object Object]"?// 如果一個屬性是對象實例的自身屬性,則我們可以刪除它。// 比如我們可以從之前例子中定義的school對象中刪除educationLevel屬性,// 因為educationLevel是定義在那個實例中的:我們在HigherLearning函數中定義educationLevel時使用了"this"關鍵字。//我們并沒有在HigherLearning函數的prototype對象在定義educationLevel屬性。console.log(school.hasOwnProperty("educationLevel")); // true?// educationLevel是一個school對象的一個自身屬性,所以 我們可以刪除它?delete school.educationLevel; // true // educationLevel屬性已經從school實例中刪除了console.log(school.educationLevel); // undefined// 但是educationLevel屬性仍然存在于HigherLearning函數中var newSchool = new HigherLearning ();console.log(newSchool.educationLevel); // University?// 如果我們在HigherLearning函數prototype中定義了一個屬性, 比如這個educationLevel2屬性:?HigherLearning.prototype.educationLevel2 = "University 2";// 這個educationLevel2屬性不屬性HigherLearning實例的自身屬性// educationLevel2屬性不是school實例的自身屬性?console.log(school.hasOwnProperty("educationLevel2")); false?console.log(school.educationLevel2); // University 2?// 嘗試刪除繼承的educationLevel2屬性?delete school.educationLevel2; // true (正如前面所提到的,這個表達式會返回true)// 繼承的educationLevel2屬性沒有被刪除console.log(school.educationLevel2); University 2?

序列化和反序列化對象

為了在HTTP中傳遞對象或者將對象轉化成字符串,我們必須將對象序列化(將其轉化為字符串)。我們可以使用JSON.stringify來序列化對象。要注意的是,在ECMAScript 5之前的版本,我們要使用json2庫來獲得JSON.stringify函數。在ECMAScript 5中,這個函數已經成為標準函數。

為了將反序列化對象(即,將字符串轉化成對象),可以使用JSON.parse函數來完成。同樣,在第5版之前要從json2庫中獲取這個函數,在第5版中已經加入這個標準函數。

示例代碼:

    var christmasList = {mike:"Book", jason:"sweater", chelsea:"iPad" }JSON.stringify (christmasList);// Prints this string:?// "{"mike":"Book","jason":"sweater","chels":"iPad"}"// To print a stringified object with formatting, add "null" and "4" as parameters:?JSON.stringify (christmasList, null, 4);// "{//      "mike": "Book",//      "jason": "sweater",//      "chels": "iPad"?//  }"// JSON.parse Examples// The following is a JSON string, so we cannot access the properties with dot notation (like christmasListStr.mike)?var christmasListStr = '{"mike":"Book","jason":"sweater","chels":"iPad"}';// Let’s convert it to an object?var christmasListObj = JSON.parse (christmasListStr); // Now that it is an object, we use dot notation?console.log(christmasListObj.mike); // Book

更多關于JavaScript對象的討論和解釋,以及ECMAScript第5版增加的內容,可以參考《JavaScript權威指南(第6版)》第六章。


后記

第一次翻譯文章,真心覺得要把翻譯做好也不是那么簡單的,很多簡單的句子看著很明白,結果真正想翻譯出來的時候,卻是死活想不出合適的表達方式。通篇文章都是根據我自己的理解,然后通過意譯出來的,沒有逐句進行翻譯。所以,如果有哪些地方理解有偏差,或者翻譯不當的地方,請盡量指出,我會盡快改正。畢竟翻譯這往篇文章也是想跟大家分享,我不希望因為自己理解的錯誤,導致對大家產生誤導。

就醬,收工。

轉載于:https://www.cnblogs.com/buginux/p/4106008.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/375486.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/375486.shtml
英文地址,請注明出處:http://en.pswp.cn/news/375486.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

旋轉鏈表

給定一個鏈表,旋轉鏈表,將鏈表每個節點向右移動 k 個位置,其中 k 是非負數。 示例 1: 輸入: 1->2->3->4->5->NULL, k 2輸出: 4->5->1->2->3->NULL解釋:向右旋轉 1 步: 5->1->2->3->4->NULL向…

內心的平靜就是財富本身-Cell組件-用友華表的由來-T君

時至今日,Cell組件仍是應用廣泛的商業報表組件 作者:人生三毒 編者注:本文作者人生三毒為知名網站及網頁游戲公司創始人,此前曾為IT類媒體資深編輯,見證了中國互聯網早期的發展。 認識T君之前先認識的是他的軟件&#…

mybatis06 增刪改差 源碼

user.java package cn.itcast.mybatis.po;import java.util.Date;public class User {private int id;private String username;// 用戶姓名private String sex;// 性別private Date birthday;// 生日private String address;// 地址public int getId() {return id;}public voi…

socket 編程 基于 select 實現的回射客戶端/服務程序

github 代碼 地址 unp.h #include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <string.h> #include <sys/socket.h> #include <stdlib.h> #include <errno.h> #include <sys/wait.h> #include <sys…

MyEclipse的優化

出自&#xff1a;http://blog.csdn.net/u010124571/article/details/41316255?refmyread 第一步: 取消自動validation validation有一堆&#xff0c;什么xml、jsp、jsf、js等等&#xff0c;我們沒有必要全部都去自動校驗一下&#xff0c;只是需要的時候才會手工校驗一下&…

NSlog輸出

NSLog的定義 void NSLog(NSString *format, …); 基本上&#xff0c;NSLog很像printf&#xff0c;同樣會在console中輸出顯示結果。不同的是&#xff0c;傳遞進去的格式化字符是NSString的對象&#xff0c;而不是char *這種字符串指針。 實例 NSLog可以如下面的方法使用&#x…

推理題,會則秒解

你和你的朋友&#xff0c;兩個人一起玩 Nim 游戲&#xff1a;桌子上有一堆石頭&#xff0c;每次你們輪流拿掉 1 - 3 塊石頭。 拿掉最后一塊石頭的人就是獲勝者。你作為先手。 你們是聰明人&#xff0c;每一步都是最優解。 編寫一個函數&#xff0c;來判斷你是否可以在給定石頭…

【圖論】割點、橋、雙連通

連通分量 個數可以通過一次BFS或者DFS得到 割點和橋 可以枚舉刪除每一個點或者每一條邊&#xff0c;判斷連通分量個數是否增加 更好的方法 該算法是R.Tarjan發明的。對圖深度優先搜索&#xff0c;定義DFS(u)為u在搜索樹&#xff08;以下簡稱為樹&#xff09;中被遍歷到的次序號…

奇酷手機顯示Log

1、在桌面點擊撥號&#xff0c;在撥號盤輸入“*20121220#”&#xff0c;進入工程模式;2、看到日志輸出等級&#xff0c;點進去 Log print enable 選 enable Java log level 選 LOGV C and C log level 選 LOGV Kernel log level 選 KERN_DEBUG3、完畢 參考網址&#xff1a;http…

getCanonicalPath getAbsolutePath區別

1、在winows環境下它們的區別是 &#xfeff;&#xfeff;getCanonicalPath是標準路徑&#xff0c;沒有特殊字符&#xff0c;getAbsolutePath是有特殊字符的 2、在AIX系統中它們的區別&#xff1a; 首先編譯&#xff1a;javac com/ai/test/BugTest.java 然后運行&#xff1a;ja…

Hbase與hive整合

//hive與hbase整合create table lectrure.hbase_lecture10(sname string, score int) stored by org.apache.hadoop.hive.hbase.HBaseStorageHandler whth serdeproperties("hbase.columns.mapping" :key,cf1:score)tblproperties("hbase.table.name" &q…

C++實現一個http服務器

一個簡單的博客后端服務器 github地址&#xff0c;持續更新 設計參考 #define MYSQLPP_MYSQL_HEADERS_BURIED #include "httplib.h" #include "rapidjson/document.h" #include <mysql/mysql.h> #include <iostream> #include <string>…

KMP算法的java實現

package com.trs.utils;public class KMPStr {/** 在KMP算法中&#xff0c;最難求的就是next函數&#xff0c;如何理解next函數是一個難題&#xff0c;特別是knext[k]&#xff0c;這里* 需要指出的是當p[i]!p[j]時&#xff0c;我們只有通過回溯將k的值逐漸減小&#xff0c;貌似…

線段分割法實現微信搶紅包

無意間看到的一種實現搶紅包的方法&#xff0c;于是用C實現了一下。 將一個紅包分成 n 份 具體的思路是&#xff0c;將一個紅包看作是一個線段&#xff0c;線段的長就是紅包總金額&#xff0c;然后在這個線段上隨機切 n-1 刀&#xff0c;分成 n 份&#xff0c;然后搶紅包的人依…

JAVA多線程和并發基礎面試問答(轉載)

JAVA多線程和并發基礎面試問答 原文鏈接&#xff1a;http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-answers/ 多線程和并發問題是Java技術面試中面試官比較喜歡問的問題之一。在這里&#xff0c;從面試的角度列出了大部分重要的問題&#xff0c…

Linux的學習--crontab

之前了解過一點crontab&#xff0c;前段時間比較閑&#xff0c;就熟悉了一下&#xff0c;今天總結記錄一下。 crontab命令常見于Unix和類Unix的操作系統之中&#xff0c;用于設置周期性被執行的指令。該命令從標準輸入設備讀取指令&#xff0c;并將其存放于"crontab"…

C++雪花算法實現

看來一下雪花算法的實現方法&#xff0c;用 c試著實現了一下&#xff0c;這里僅僅是實現了算法的流程&#xff0c;但是具體的細節&#xff0c;如并發、多線程訪問等等沒有具體考慮。 雪花算法的簡單講解參考 #include <sys/select.h> #include <iostream> #includ…

CAlayer層的屬性

iOS開發UI篇—CAlayer層的屬性 一、position和anchorPoint 1.簡單介紹 CALayer有2個非常重要的屬性&#xff1a;position和anchorPoint property CGPoint position; 用來設置CALayer在父層中的位置 以父層的左上角為原點(0, 0) property CGPoint anchorPoint; 稱為“定位點”、…

Window Linux下實現指定目錄內文件變更的監控方法

轉自&#xff1a;http://qbaok.blog.163.com/blog/static/10129265201112302014782/ 對于監控指定目錄內文件變更&#xff0c;window 系統提供了兩個未公開API&#xff1a;SHChangeNotifyRegister SHChangeNotifyDeregister 分別用于注冊Notify以及監視。 同時&#xff0c;還提…

Odoo9發行說明

2015年10月1日&#xff0c;期待已久的Odoo9正式發布。本文是Odoo9正式版發行說明&#xff0c;基于官網資料翻譯。 譯者: 蘇州-微塵原文地址&#xff1a;https://www.odoo.com/page/odoo-9-release-notes譯文地址&#xff1a;http://blog.csdn.net/wangnan537/article/details/4…