是什么
CGroup 是 Control Groups 的縮寫,是 Linux 內核提供的一種可以限制、記錄、隔離進程組 (process groups) 所使用的物力資源 (如 cpu memory i/o 等等) 的機制。2007 年進入 Linux 2.6.24 內核,CGroups 不是全新創造的,它將進程管理從 cpuset 中剝離出來,作者是 Google 的 Paul Menage。CGroups 也是 LXC 為實現虛擬化所使用的資源管理手段。
subsystem,子系統,一個通過cgroup提供的工具和接口來管理進程集合的模塊。一個子系統就是一個典型的“資源控制器”,用來調度資源或者控制資源使用的上限。其實每種資源就是一個子系統。子系統可以是以進程為單位的任何東西,比如虛擬化子系統、內存子系統。
hierarchy,層級樹,多個cgroup的集合,這些集合構成的樹叫hierarchy。可以認為這是一個資源樹,附著在這上面的進程可以使用的資源上限必須受樹上節點(cgroup)的控制。hierarchy上的層次關系通過cgroupfs虛擬文件系統顯示。系統允許多個hierarchy同時存在,每個hierachy包含系統中的部分或者全部進程集合。
cgroupfs是用戶管理操縱cgroup的主要接口:通過在cgroupfs文件系統中創建目錄,實現cgroup的創建;通過向目錄下的屬性文件寫入內容,設置cgroup對資源的控制;向task屬性文件寫入進程ID,可以將進程綁定到某個cgroup,以此達到控制進程資源使用的目的;也可以列出cgroup包含的進程pid。這些操作影響的是sysfs關聯的hierarchy,對其它hierarchy沒有影響。
對于cgroup,其本身的作用只是任務跟蹤。但其它系統(比如cpusets,cpuacct),可以利用cgroup的這個功能實現一些新的屬性,比如統計或者控制一個cgroup中進程可以訪問的資源。舉個例子,cpusets子系統可以將進程綁定到特定的cpu和內存節點上。
cpu 子系統,主要限制進程的 cpu 使用率。
cpuacct 子系統,可以統計 cgroups 中的進程的 cpu 使用報告。
cpuset 子系統,可以為 cgroups 中的進程分配單獨的 cpu 節點或者內存節點。
memory 子系統,可以限制進程的 memory 使用量。
blkio 子系統,可以限制進程的塊設備 io。
devices 子系統,可以控制進程能夠訪問某些設備。
net_cls 子系統,可以標記 cgroups 中進程的網絡數據包,然后可以使用 tc 模塊(traffic control)對數據包進行控制。
net_prio — 這個子系統用來設計網絡流量的優先級
freezer 子系統,可以掛起或者恢復 cgroups 中的進程。
ns 子系統,可以使不同 cgroups 下面的進程使用不同的 namespace
hugetlb — 這個子系統主要針對于HugeTLB系統進行限制,這是一個大頁文件系統。
內核使用 cgroup 結構體來表示一個 control group 對某一個或者某幾個 cgroups 子系統的資源限制。cgroup 結構體可以組織成一顆樹的形式,每一棵cgroup 結構體組成的樹稱之為一個 cgroups 層級結構。
cgroups層級結構可以 attach 一個或者幾個 cgroups 子系統,當前層級結構可以對其 attach 的 cgroups 子系統進行資源的限制。每一個 cgroups 子系統只能被 attach 到一個 cpu 層級結構中。
創建了 cgroups 層級結構中的節點(cgroup 結構體)之后,可以把進程加入到某一個節點的控制任務列表中,一個節點的控制列表中的所有進程都會受到當前節點的資源限制。同時某一個進程也可以被加入到不同的 cgroups 層級結構的節點中,因為不同的 cgroups 層級結構可以負責不同的系統資源。所以說進程和 cgroup 結構體是一個多對多的關系。
上面這個圖從整體結構上描述了進程與 cgroups 之間的關系。最下面的P代表一個進程。每一個進程的描述符中有一個指針指向了一個輔助數據結構css_set(cgroups subsystem set)。 指向某一個css_set的進程會被加入到當前css_set的進程鏈表中。一個進程只能隸屬于一個css_set,一個css_set可以包含多個進程,隸屬于同一css_set的進程受到同一個css_set所關聯的資源限制。
上圖中的”M×N Linkage”說明的是css_set通過輔助數據結構可以與 cgroups 節點進行多對多的關聯。但是 cgroups 的實現不允許css_set同時關聯同一個cgroups層級結構下多個節點。 這是因為 cgroups 對同一種資源不允許有多個限制配置。
一個css_set關聯多個 cgroups 層級結構的節點時,表明需要對當前css_set下的進程進行多種資源的控制。而一個 cgroups 節點關聯多個css_set時,表明多個css_set下的進程列表受到同一份資源的相同限制。
CGroup 技術可以被用來在操作系統底層限制物理資源,起到 Container 的作用。圖中每一個 JVM 進程對應一個 Container Cgroup 層級,通過 CGroup 提供的各類子系統,可以對每一個 JVM 進程對應的線程級別進行物理限制,這些限制包括 CPU、內存等等許多種類的資源。下一部分會具體對應用程序進行 CPU 資源隔離進行演示。
任務(task)。在 cgroups 中,任務就是系統的一個進程;
控制族群(control group)。控制族群就是一組按照某種標準劃分的進程。Cgroups 中的資源控制都是以控制族群為單位實現。一個進程可以加入到某個控制族群,也從一個進程組遷移到另一個控制族群。一個進程組的進程可以使用 cgroups 以控制族群為單位分配的資源,同時受到 cgroups 以控制族群為單位設定的限制;
層級(hierarchy)。控制族群可以組織成 hierarchical 的形式,既一顆控制族群樹。控制族群樹上的子節點控制族群是父節點控制族群的孩子,繼承父控制族群的特定的屬性;
子系統(subsystem)。一個子系統就是一個資源控制器,比如 cpu 子系統就是控制 cpu 時間分配的一個控制器。子系統必須附加(attach)到一個層級上才能起作用,一個子系統附加到某個層級以后,這個層級上的所有控制族群都受到這個子系統的控制。
相互關系
每次在系統中創建新層級時,該系統中的所有任務都是那個層級的默認 cgroup(我們稱之為 root cgroup,此 cgroup 在創建層級時自動創建,后面在該層級中創建的 cgroup 都是此 cgroup 的后代)的初始成員;
一個子系統最多只能附加到一個層級;
一個層級可以附加多個子系統;
一個任務可以是多個 cgroup 的成員,但是這些 cgroup 必須在不同的層級;
系統中的進程(任務)創建子進程(任務)時,該子任務自動成為其父進程所在 cgroup 的成員。然后可根據需要將該子任務移動到不同的 cgroup 中,但開始時它總是繼承其父任務的 cgroup。
安裝cgroup服務
centos 6 yum install -y libcgroup centos 7 yum install -y libcgroup libcgroup-tools
配置cgroup配置
這里需要是限制cpu
先創建一個組,把需要限制的進程,啟動的時候放到這組下。
vim /etc/cgconfig.conf
mount { # cpuset = /cgroup/cpuset; cpu = /cgroup/cpu; # cpuacct = /cgroup/cpuacct; # memory = /cgroup/memory; # devices = /cgroup/devices; # freezer = /cgroup/freezer; # net_cls = /cgroup/net_cls; # blkio = /cgroup/blkio; } group yarn { # yarn 為組名 perm { task { uid = hadoop; # 權限設置,為hadoop gid = hadoop; } admin { uid = hadoop; gid = hadoop; } } cpu { # 可以用 cpu.cfs_period_us 和 cpu.cfs_quota_us 來限制該組中的所有進程在單位時間里可以使用的 cpu 時間。 cpu.cfs_period_us= 100000; # 就是時間周期,默認為 100000,即百毫秒 值的范圍: 1000-100000 cpu.cfs_quota_us= 2160000; # cpu.cfs_quota_us 就是在這期間內可使用的 cpu 時間,默認 -1,即無限制 } # 現在這個設置代表,這個組可以用的cpu為21.6盒,2160000/100000 }
啟動cgroup服務
. 初識JS
1.1 什么是JS語言
javascript是一種運行在客戶端 的腳本語言
客戶端: 即接受服務的一端,與服務端相對應,在前端開發中,通常客戶端指的就是瀏覽器。
腳本語言: 也叫解釋型語言,特點是執行一行,解釋一行,如果發現報錯,代碼就停止執行。
1.2 JS的三個組成部分
javascript的三個組成部分:ECMAScript、BOM、DOM
ECMAScript: 定義了javascript的語法規范。
BOM: 一套操作瀏覽器功能的API。
DOM: 一套操作頁面元素的API。
1.3 script 標簽
1、script標簽的書寫方式
書寫Javascript代碼有兩種方式,第一種是直接在script標簽中書寫,第二種是將代碼寫在js文件中,通過script的src屬性進行引入。
直接在script中書寫javascript代碼:
<!-- type="text/javascript" 可以省略 --> <script type="text/javascript"> alert("今天天氣真好呀"); </script>
通過script標簽引入一個JS文件,需要指定src屬性:
<!-- 表示引用了test.js文件,并且script標簽內不可以繼續寫代碼 --> <script src="test.js"></script>
如果script標簽指定了src屬性,說明是想要引入一個js文件,這個時候不能繼續在script標簽中寫js代碼,即便寫了,也不會執行。
2、script標簽的書寫位置
script標簽的書寫位置,原則上來說,可以在頁面中的任意位置書寫。
寫在head標簽中,style標簽之后:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link rel="stylesheet" href="demo.css"> <!-- 寫在這里 --> <script src="demo.js"></script> </head> <body> </body> </html>
瀏覽器有一個特性,就是在遇到<body>標簽時才開始呈現內容。如果在head里面引用js文件的話,意味著必須要等到全部的javascript代碼都被下載、解析和執行完成之后,才能開始呈現頁面的內容。如果文件數量一旦過多,將會影響頁面加載速度,此時頁面有可能會在加載完成前一片空白。
寫在</body>標簽的前面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link rel="stylesheet" href="demo.css"> </head> <body> <!-- 寫在這里 --> <script src="demo.js"></script> </body> </html>
在解析javascript代碼之前,頁面的內容已經完全呈現在瀏覽器當中了,用戶會明顯感覺頁面加載變快了。
1.4 js中輸入輸出語句
1.5 注釋
不被程序執行的代碼。用于程序員標記代碼,在后期的修改,以及他人的學習時有所幫助,在JS中,分為單行注釋和多行注釋以及文檔注釋。
單行注釋
//這是單行注釋,只能注釋一行
多行注釋
/* 這是多行注釋,不能嵌套 */
文檔注釋
/** * 求圓的面積 * @param r {number} 圓的半徑 * @returns {number} 圓的面積 * 這是文檔注釋 */ function getArea (r) { return Math.PI * r * r; }
注釋的作用
2. 變量
變量,可以變化的量,變量是在計算機中存儲數據的一個標識符。可以把變量看成存儲數據的容器。
變量與字面量:
2.1 變量的聲明與賦值
// 1- 同時聲明并且賦值 var num = 100; console.log(num); // 100 // 2- 先聲明了一個變量,再賦值 var num1; num1 = 100; console.log(num1); // 100 // 3- 沒有聲明變量,直接賦值(可以,但是不推薦) num2 = 200; console.log(num2); // 200 // 4- 有變量聲明,但是沒有賦值(可以,沒有賦值,變量的值默認是個undefined) var num9; console.log(num9); // undefined // 5- 既沒有聲明,也沒有賦值,直接用。 console.log(num3); //報錯 num3 // 6- 一個 var,逗號分隔 可以同時聲明多個變量 var name = "Levi丶", age = 18, gender = "男";
2.2 變量的命名規則與規范
命名規則(必須遵守):
命名規范(建議遵守):
2.3 交換兩個變量的值
方法一: 聲明一個新的變量
// 交換 num1 和 num2的值 var num1 = 11; var num2 = 22; var temp; temp = num1; // num1=11 temp=11 num1 = num2; // num1=22 num2=22 num2 = temp; // temp=11 num2=11
方法二: 不通過聲明變量的方式
// 不使用臨時變量 var num1 = 11; var num2 = 22; // num1 = 11 + 22 num1 = num1 + num2; // num1=33 // num2 = 33 - 22 num2 = num1 - num2; // num2=11 // num1 = 33 - 11 num1 = num1 - num2; // num1=22
3. 數據類型
基本數據類型包括了:number、string、boolean、undefined、null
3.1 如何查看數據類型
使用typeof關鍵字查看數據類型
typeof(name); // 括號可以省略 typeof name;
3.2 Number 類型
進制
浮點數
所謂浮點數,就是該數當中必須包含一個小數點,并且小數點后面至少有一位數字。
科學計數法:
//如何表示0.003和20000? var num = 3e-3; // 0.003 var num2 = 2e+4; // 20000
浮點數的精度問題:
0.1 + 0.2 = ? // 0.30000000000000004 0.07 * 100 = ? // 7.000000000000001
浮點數在運算的時候會出現精度丟失的問題,因此在做比較運算的時候,盡量不要用小數進行比較。在第五章的時候會著重講解這個問題
數值范圍
javascript不能表示世界上所有的數,因此在javascript中,數值大小是有一定限制的。
數值判斷
示例代碼:
var str = "abc"; console.log(isNaN(str)); // true 說明不是一個數字
3.3 String 類型
字面量
字符串的字面量:“abc” 、 ‘abc’ 字符串可以是雙引號,也可以是單引號引起來。
不管是雙引號,還是單引號,都是成對出現的,假如打印的字符串里有引號怎么辦呢?
這里就要活學活用,如果只有一處有引號,就可以用單雙引號混合使用:
console.log('我是"帥哥"'); // ==> 我是"帥哥"
假如引號非常多的時候怎么辦呢? 用轉義字符:“\”:
console.log("我是'帥哥',\"哈哈哈\""); // ==> 我是'帥哥',"哈哈哈"
字符串拼接
拼接字符串使用+號
示例代碼:
console.log(11 + 11); // 22 console.log("hello" + " world"); // "hello world" console.log("100" + "100"); // "100100" console.log("11" + 11); // "1111"
總結:
字符串長度
length屬性用來獲取字符串的長度
var str = "abcdefghij"; str.length;// 字符串的長度 10
3.4. boolean 類型
boolean類型只有兩個字面量,true和false,區分大小寫(True,False不是布爾類型,只是標識符)。
所有類型的值都可以轉化成true或者false
NaN、""、undefined、null、alse、0 這6個值可以轉換成false,其余的都是true。
3.5 undefined類型與null類型
undefined表示一個聲明了沒有賦值的變量
var name ; console.log(name); // undefined
null表示一個空的對象
var name = null; console.log(typeof name); // Object
如果定義的變量,將來是準備用于保存對象的話,最好將變量初始化為null
var name = null;
undefined 與 null 的關系
undefined == null; // true undefined === null; // false
實際上,undefiner值是派生自null值的,所以判斷相等時為true,但是兩種用途是完全不一樣的。
4. 簡單數據類型轉換
如何使用谷歌瀏覽器,快速的查看數據類型?
這個在調試過程中時非常有用的。
4.1 轉字符串類型
1、String()函數轉換
var num = 123; console.log(String(num)); // "123"
2、toString() 轉換不了undefined 和 null
var num = 123; console.log(num.toString()); // "123" console.log(undefined.toString()); // 報錯 console.log(null.toString()); // 報錯
3、+ "" 加引號
var num = 123; console.log(num + ""); // "123"
4.2 轉數值類型
1、Number
console.log(Number("-123")); // -123 console.log(Number("")); // 0 console.log(Number("123abc")); // NaN
2、parseInt(取整)
console.log(parseInt("123.123")); // 123 console.log(parseInt("123.123abc")); // 123 console.log(parseInt("abc123.123")); // NaN console.log(parseInt("")); // NaN console.log(parseInt("abc")); // NaN
3、parseFloat(取數)
console.log(parseFloat("123.123")); // 123.123 console.log(parseFloat("123.123abc")); // 123.123 console.log(parseFloat("abc123.123")); // NaN console.log(parseFloat("")); // NaN console.log(parseFloat("abc")); // NaN
4、參與運算==> "+" or "-0"
var str = "-123"; console.log(+str); // -123 console.log(str-0); // -123
4.3 轉布爾類型
布爾類型只有true和false,但是所有類型的值都可以轉換成布爾類型
1、能夠轉換成false的只有6種:
其余的都是true
2、! 轉換
var str = ""; // Boolean() 判斷這個參數的布爾類型 console.log(Boolean(str)); // false console.log(!str); // true
5. JS小數運算精度丟失
5.1 JS數字精度丟失的一些典型問題
js在使用小數進行計算的時候,會出現精度丟失的問題。不要用來跟其他的小數做比較。
0.1 + 0.2 != 0.3 //true 0.30000000000000004 // 16位數 和 17位數相等 9999999999999999 == 10000000000000001 // true 9007199254740992 + 1 == 9007199254740992 // true
5.2 JS數字丟失精度的原因
計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 規范,采用 雙精度存儲(double precision) ,占用 64 bit。如圖
意義:
浮點數,比如:
0.1 >> 0.0001 1001 1001 1001…(1001無限循環) 0.2 >> 0.0011 0011 0011 0011…(0011無限循環)
此時只能模仿十進制進行四舍五入了,但是二進制只有0和1兩個,于是變為0舍1入。這即是計算機中部分浮點數運算時出現誤差,丟失精度的根本原因。
大整數的精度丟失和浮點數本質上是一樣的,尾數位最大是 52 位,因此 JS 中能精準表示的最大整數是 Math.pow(2, 53),十進制即 9007199254740992。
大于 9007199254740992 的可能會丟失精度:
9007199254740992 >> 10000000000000...000 // 共計 53 個 0 9007199254740992 + 1 >> 10000000000000...001 // 中間 52 個 0 9007199254740992 + 2 >> 10000000000000...010 // 中間 51 個 0
實際上:
9007199254740992 + 1 // 丟失 9007199254740992 + 2 // 未丟失 9007199254740992 + 3 // 丟失 9007199254740992 + 4 // 未丟失
結果如圖:
以上,可以知道看似有窮的數字,在計算機的二進制表示里卻是無窮的,由于存儲位數限制因此存在“舍去”,精度丟失就發生了。
5.3 JS數字丟失精度的解決方案
對于整數,前端出現問題的幾率可能比較低,畢竟很少有業務需要需要用到超大整數,只要運算結果不超過 Math.pow(2, 53) 就不會丟失精度。
對于小數,前端出現問題的幾率還是很多的,尤其在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)
// 0.1 + 0.2 (0.1*10 + 0.2*10) / 10 == 0.3 // true
6. 運算符
6.1 一元運算符
遞增 "++" 和 遞減 "--" 還分為前自增或后自增,前自減或后自減,兩種自增自減的運算結果是不一樣的;
舉個例子,看代碼:
var num = 5; console.log(num++); // 5 console.log(++num); // 7 (因為剛剛num自增了一次,這里打印的話就等于在6的基礎上前自增了,在計算機科學中,被稱為副效應) console.log(num--); // 7 (這里是后自減,所以先返回值,返回7,再運算--,此時的num實際是等于6了) console.log(--num); // 5
6.2 邏輯運算符
示例代碼:
/*細讀上面三句話,就能理解為什么會是這個打印結果了*/ console.log(true && true); //true console.log(false || false); //false console.log(null && undefined); //null console.log(null || undefined); //undefined console.log("abc" && undefined); //undefined console.log("abc" || undefined); //abc console.log(null || false || 0 || 1 || null); //1 console.log("abc" && "bcd" && "def"); //def
6.3 運算符的優先級
示例代碼:
// 第一題 true && true console.log(((4 >= 6) || ("人" != "狗")) && !(((12 * 2) == 144) && true)); // true // 第二題 var num = 10; // true && true if(5 == num / 2 && (2 + 2 * num).toString() === "22") { console.log(true); // true }else{ console.log(false); }
7. 選擇語句
7.1 if..else語句
語法:
只有一個判斷條件的時候 if..else:
if(判斷條件){ // 當判斷條件為true的時候執行代碼1,為false的時候執行代碼2 代碼1; }else{ 代碼2; }
當不止一個判斷條件的時候 else用else if 代替:
if(判斷條件1){ // 判斷條件 1 為 true 的時候執行 代碼 1 代碼1; }else if(判斷條件2){ // 判斷條件 2 為 true 的時候執行 代碼 2 代碼2; }else{ // 兩個條件都不滿足的時候執行代碼 3 代碼3; }
思考1:
var age = 20; if(age >= 18){ console.log("沒時間解釋了,趕緊上車吧"); // 打印這條 }else if(age >= 16){ console.log("請在家長的陪同下觀看"); }else { console.log("回家學習吧"); }
思考2:
var date = new Date(); // 獲取當前的時間 var week = date.getDay(); // 獲得 0-6 表示周幾 0:星期日 if(week == 0){ console.log("今天是星期天"); }else if(week == 1){ console.log("今天是星期一"); }else if(week == 2){ console.log("今天是星期二"); }else if(week == 3){ console.log("今天是星期三"); }else if(week == 4){ console.log("今天是星期四"); }else if(week == 5){ console.log("今天是星期五"); }else if(week == 6){ console.log("今天是星期六"); }else{ console.log("你火星的來的吧"); }
7.2 switch..case
語法:
// switch: 開關; case: 案列; switch(變量){ // 判斷變量是否全等于case的值1,或者值2, case 值1: 執行代碼1; // 全等于的時候執行代碼1 break; // 然后break;代碼跳出switch語句, 不加break,會繼續執行下面的代碼 case 值2: 執行代碼2; break; default: 執行代碼3; // 當都不滿足條件的時候,會執行默認里的執行代碼3 }
思考:素質教育(把分數變成ABCDE)
// 90-100 : A // 80-89: B // 70-79: C // 60-69: D // 0-59 : E // 這里的等級是根據一個范圍的分數劃定的,用if..else很容易實現,但是switch..case是一個具體的條件,怎么辦呢? // 方法:將分數除以10再用parseInt屬性取整 var score = 85; score = parseInt(score/10); // 8 switch (score) { // score = 10 或者 9 的時候 返回 A case 10: case 9: console.log("A"); break; // score = 8 的時候 返回 B case 8: console.log("B"); break; case 7: console.log("C"); break; case 6: console.log("D"); break; default: console.log("E"); }
7.3 三元運算符
這個運算符可以用來代替if..else條件判斷。但是為什么有這個運算符呢?這里的原因是if..else使用兩個代碼塊,卻只有一個會執行,在講究的程序員看來是一種浪費。所以使用三元運算符,用一條語句就可以完成功能。
語法:
判斷語句?表達式1:表達式2; 根據判斷語句返回的布爾值,true的話,返回表達式1,false的話返回表達式2
舉個例子,看代碼:
var sex = 1; sex == 1 ? "男":"女"; // 判斷sex是否等于1,如果true,返回第一個表達式:"男"
例題:判斷兩個數的大小
// 用if..else語句解決 // 這里使用了兩個代碼塊,有點浪費 var num1 = 18; var num2 = 39; var max; if(num1>num2){ max = num1; }else{ max = num2; } console.log(max); // 用三元運算符 var num3 = 28; var num4 = 49; var max1 = num3>num4? num3:num4; console.log(max1);
注意(容易出錯的地方):
下面這個語句判斷如果是會員,費用為2美元,非會員,為10美元。現在設置了非會員,卻打印出了2美元,顯然出錯了。
var isMember = false; console.log("當前費用" + isMember ? "$2.00" : "$10.00"); // "$2.00"
出錯的原因是?號的優先級比+號低,所以實際運行的語句是
// true console.log("當前費用false" ? "$2.00" : "$10.00"); // "$2.00"
8.循環語句
8.1 while 循環
語法:
// 1. 如果循環條件的結果是true的時候,就會執行循環體 // 2. 如果循環條件的結果是false的時候,結束循環。 while(循環條件){ 循環的代碼; // 循環體 自增或者自減; // 一定不要忘記自增或自減,否則就會死循環 }
例如,求0~100的和:
var num = 0; var sum = 0; while(num <= 100){ sum += num; num++; } console.log(sum); // 5050
8.2 do..while 循環
語法:
do{ 循環的代碼; // 循環體 自增或者自減; // 一定不要忘記自增或自減,否則就會死循環 }while(循環條件);
例如,求0~100的和:
var num = 0; var sum = 0; do{ sum += num; num++; }while(num<=100); console.log(sum); // 5050
8.3 for 循環
寫while循環的經常會忘記自增,for循環其實是while循環演化過來的,語法更加的簡潔明了,使用非常的廣泛。
語法:
//主要for循環的表達式之間用的是;號分隔的,千萬不要寫成,號 for(初始化表達式;判斷表達式;自增表達式){ //循環體 }
例如:求0~100的和:
var sum = 0; for(var num = 0; num <= 100; num++){ sum += num; } console.log(sum); // 5050
8.4 break 和 continue
break:立即跳出整個循環,即循環結束,開始執行循環后面的內容(直接跳到大括號)continue:立即跳出當前循環,繼續下一次循環(跳到i++的地方)
1、continue 示例代碼:
for(var i = 1; i <= 10; i++) { if(i == 5) { continue; } console.log(i); // 1,2,3,4,6,7,8,9,10 }
2、break 示例代碼:
for(var i = 1; i <= 10; i++) { if(i == 5) { break; } console.log(i); // 1,2,3,4 }
8.5 循環語句練習
1、計算一個數的位數
當不知道循環次數的時候,用while循環:
var num = 1234567; //因為不知道循環次數,所以推薦使用while循環 var count = 0; // count記錄位數 while(num != 0){ // 循環條件 num = parseInt(num/10);// 讓num縮小10倍 count++; // ,每縮小10倍就計算一次位數了 } console.log(count); // 7
2、翻轉一個數
var num = 12345678; //因為不知道循環次數,所以推薦使用while循環 var str = ""; while(num != 0){ str += num%10; // 將每一位取余 num = parseInt(num/10);//讓num縮小10倍 } // str 是一個字符串,所以 +str將它轉回Number類型 console.log(+str); //
3、總結:
9. 數組
所謂數組,就是將多個元素(通常是同一類型的),按一定順序排列放到一個集合中,那么這個集合就稱之為數組
在javascript中,數組是一個有序的列表,可以在數組中存放任意的數據,并且數組的長度可以動態的調整
9.1 創建數組
1、通過構造函數創建數組:
2、通過數組子面量創建數組:
9.2 數組的下標與長度
數組的下標:
數組是有序的,數組中的每一個元素都對應了一個下標,下標是從0開始的
var arr = ['aa','bb','cc']; arr[0]; // 下標是0,對應的值是'aa' arr[2]; // 下標是2,對應的值是'cc'
數組的長度:
跟字符串一樣,數組也有一個length的屬性,指數組中存放的元素的個數
var arr = ['aa','bb','cc']; arr.length; // 數組的長度為3
空數組的長度為0
數組的長度與下標的關系:
數組的最大下標 = 數組的長度 - 1
9.3 數組的賦值與取值
數組的取值:
var arr = ['red','blue','green']; arr[0]; // red arr[2]; // green arr[3]; // 返回undefined,因為數組最大的下標為2
數組的賦值:
var arr = ["red", "green", "blue"]; arr[0] = "yellow"; // 把red替換成了yellow arr[3] = "pink"; // 給數組新增加了一個pink的值 arr[5] = "black"; // 數組輸出為["red", "green", "blue",empty,empty,"black"]
9.4 數組的遍歷
遍歷: 對數組的每一個元素都訪問一次,叫做遍歷
數組遍歷的基本語法:
var arr = [1,2,3,4,5,6,7,8,9]; for(var i = 0; i < arr.length; i++){ console.log(arr[i]); // 1 2 3 4 5 6 7 8 9 }
數組遍歷的逆向遍歷語法:
// i= arr.length-1 ==> 表示初始化表達式 從數組最后一位開始遍歷 // i>=0 表示判斷條件,下標要滿足大于等于0 // i--,表示每次遍歷 初始值都是自減的 var arr = [1,2,3,4,5,6,7,8,9]; for(var i = arr.length-1; i >= 0; i--){ console.log(arr[i]); // 9 8 7 6 5 4 3 2 1 }
9.5 數組綜合練習
1、求一個數組中的最大值、最小值以及對應的下標
var arr = [298, 1, 3, 4, 6, 2, 23, -88,77,44]; var max = arr[0]; // 隨機取數組中的一個值與其他值比較 var maxIndex = 0; // 初始化最大值的下標 var min = arr[0]; var minIndex = 0; for(var i = 0; i< arr.length; i++){ if(max < arr[i]){ // 用一開始選擇的值,與遍歷后的值進行比較 max = arr[i]; // 當后面的值比初始值大,就將后面的這個值賦值給初始值,再用這個全新的值再v 去與后面的比較 maxIndex = i; // 比較結束后,此時的索引就是最大值的索引 } if(min > arr[i]){ min = arr[i]; minIndex = i; } } console.log("最大的值是:" + max); console.log("最大值的下標是:" + maxIndex); console.log("最小的值是:" + min); console.log("最小值的下標是:" + minIndex);
2、讓數組倒序保存到一個新的數組中
需要了解數組的一個方法 push,在數組的最后面添加
var arr = ["大喬", "小喬", "甄姬", "不知火舞"]; var newArr = []; for (var i = arr.length - 1; i >= 0; i--) { newArr.push(arr[i]); } console.log(newArr); // ["不知火舞", "甄姬", "小喬", "大喬"]
3、將字符串數組用"|"或其他符號拼成一個字符串
var arr = ["aa","bb","cc","dd"]; var str = ""; for(var i = 0; i<arr.length; i++){ if(i == arr.length-1){ str = str + arr[i]; // 判斷一下,如果是最后一個的話就不用加“|” }else{ str = str + arr[i]+"|"; // str初始值是一個空字符串,遍歷的時候需要加上前一次的結果 } }
4、數組去重
var arr = [1, 1, 5, 7, 8, 3, 2, 5, 7, 2, 4, 6, 2, 5, 7, 2, 5]; //定義一個新數組 var newArr = []; //遍歷需要去重的數組 for (var i = 0; i < arr.length; i++) { //假設不存在 var flag = true; //需要判斷arr[i]這個值是否在新數組中存在 for(var j = 0; j < newArr.length; j++){ //進行比較即可 if(arr[i] == newArr[j]){ //如果發現了相等的數,說明存在 flag = false; } } if(flag){ //如果假設成立,說明不存在 newArr.push(arr[i]); } } console.log(newArr);
10. 冒泡排序
10.1 冒泡排序的思路
一個有8位元素的數組,讓它的第一位與后面每一位進行比較,前面一位小于后面的時候,位置不變,前面的大于后面的交換位置,就這樣一共要比七趟(最后一趟不要比,就剩一位,就是最小的);
實現原理如下圖:
10.2 按性能等級冒泡排序分3個等級
1、冒泡排序 60分:
var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; var tang = 0; var ci = 0; for (var i = 0; i < arr.length - 1; i++) { // 外層for循環,循環的是比較的趟數,因為只要比較8趟 所以i判斷的條件為length-1 tang++; for (var j = 0; j < arr.length - 1; j++) { // 內層for循環,循環的是比較的次數,每趟比較8次 ci++; if (arr[j] > arr[j + 1]) { // 判斷比較的兩個數,如果前面的大于后面的一位,交換位置 var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } console.log("趟數:" + tang); // 8趟 console.log("次數:" + ci); // 64次 console.log(arr); }
測試代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <span id="demo"></span><br/> <button id="stb">從小到大</button> <button id="bts">從大到小</button> <span id="show"> </span> <script> var demo = document.getElementById("demo"); var show = document.getElementById("show"); var bts = document.getElementById("bts"); var stb = document.getElementById("stb"); var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; demo.innerHTML = arr; bts.onclick = function() { bubbleSort(function(a, b) { return b - a; }); } stb.onclick = function() { bubbleSort(function(a, b) { return a - b; }); } function bubbleSort(fn) { var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; var strArr = []; show.innerHTML = ""; strArr.push("<br/>"); var inner = 0; var outer = 0; for (var i = 0; i < arr.length - 1; i++) { strArr.push("第" + (i + 1) + "趟"); for (var j = 0; j < arr.length - 1; j++) { if (fn(arr[j], arr[j + 1]) > 0) { var tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } inner++; } strArr.push(arr.toString()); strArr.push("共" + j + "次" + "<br/>"); outer++; } strArr.push("外循環" + outer + "次"); strArr.push("內循環" + inner + "次"); show.innerHTML = strArr.join(" "); } </script> </body> </html>
每趟都比較8次?明顯是多余了,下面進行優化
2、冒泡排序80分:
var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; var tang = 0; var ci = 0; for (var i = 0; i < arr.length - 1; i++) { tang++; for (var j = 0; j < arr.length - 1 - i; j++) { // 第二趟只比了7次 依次遞減 ci++; if (arr[j] > arr[j + 1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } console.log("趟數:" + tang); // 8趟 console.log("次數:" + ci); // 36次 console.log(arr);
i 是從下標0開始的,第一趟的時候i=0,比了8次,第二趟i=1,只需要比7次,第三趟i=2,只需要比6次...依次類推,所以 比的次數應該就是arr.length - 1 -i;
測試代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <span id="demo"></span><br/> <button id="stb">從小到大</button> <button id="bts">從大到小</button> <span id="show"> </span> <script> var demo = document.getElementById("demo"); var show = document.getElementById("show"); var bts = document.getElementById("bts"); var stb = document.getElementById("stb"); var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; demo.innerHTML = arr; bts.onclick = function() { bubbleSort(function(a, b) { return b - a; }); } stb.onclick = function() { bubbleSort(function(a, b) { return a - b; }); } function bubbleSort(fn) { var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; var strArr = []; show.innerHTML = ""; strArr.push("<br/>"); var inner = 0; var outer = 0; for (var i = 0; i < arr.length - 1; i++) { strArr.push("第" + (i + 1) + "趟"); for (var j = 0; j < arr.length - 1 - i; j++) { if (fn(arr[j], arr[j + 1]) > 0) { var tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } inner++; } strArr.push(arr.toString()); strArr.push("共" + j + "次" + "<br/>"); outer++; } strArr.push("外循環" + outer + "次"); strArr.push("內循環" + inner + "次"); show.innerHTML = strArr.join(" "); } </script> </body> </html>
還有什么可以優化的嗎? 假如8個數在第3趟的時候就排好了,還需要繼續排嗎?
3、冒泡排序100分:
假設成立法(3步):
var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; var tang = 0; var ci = 0; for (var i = 0; i < arr.length - 1; i++) { var flag = true; // 假設每一次進來都排好了 tang++; for (var j = 0; j < arr.length - 1 - i; j++) { ci++; if (arr[j] > arr[j + 1]) { flag = false; // 如果兩位比較還滿足前面的比后面的大的時候,說明假設不成立 var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } if (flag == true) { // 最后判斷一下,如果假設推翻不了,就停止運行。 break; } } console.log("趟數:" + tang); // 4 趟 console.log("次數:" + ci); // 26 次 console.log(arr);
當順序已經排好后,就不用再去執行趟數了;
測試代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <span id="demo"></span><br/> <button id="stb">從小到大</button> <button id="bts">從大到小</button> <span id="show"> </span> <script> var demo = document.getElementById("demo"); var show = document.getElementById("show"); var bts = document.getElementById("bts"); var stb = document.getElementById("stb"); var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; demo.innerHTML = arr; bts.onclick = function() { bubbleSort(function(a, b) { return b - a; }); } stb.onclick = function() { bubbleSort(function(a, b) { return a - b; }); } function bubbleSort(fn) { var arr = [3, 1, 2, 5, 4, 8, 9, 7, 6]; var strArr = []; show.innerHTML = ""; strArr.push("<br/>"); var inner = 0; var outer = 0; for (var i = 0; i < arr.length - 1; i++) { var sorted = true; strArr.push("第" + (i + 1) + "趟"); for (var j = 0; j < arr.length - 1 - i; j++) { if (fn(arr[j], arr[j + 1]) > 0) { var tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; sorted = false; } inner++; } strArr.push(arr.toString()); strArr.push("共" + j + "次" + "<br/>"); outer++; if (sorted) { break; } } strArr.push("外循環" + outer + "次"); strArr.push("內循環" + inner + "次"); show.innerHTML = strArr.join(" "); } </script> </body> </html>
第三趟已經排好了,為什么還要排第四趟呢? 原因很簡單,因為第三趟的時候js是不知道你已經排好的,只有第四趟的時候,js再進行換位比較的時候,發現位置都不需要換了,說明排好了。
如果覺得不錯可以關注微信公眾號:編程成長記
JavaScript中,函數調用和上下文是理解語言核心機制的關鍵。本文將深入探討函數調用、this關鍵字以及apply方法的使用。
首先,讓我們回顧一下函數調用的基礎。在JavaScript中,函數可以通過多種方式調用,包括常見的函數調用、方法調用和構造函數調用。例如,定義了一個簡單的函數foo:
function foo(a, b, c) {
console.log("foo功能...");
console.log(a, b, c);
return 123;
}
接下來,我們使用對象字面量創建了一個對象p1,其中包含了屬性name和age,以及兩個方法eat和run:
var p1 = { name: "yuan",
age: 22,
eat: function () {
console.log(this.name + " is eating");
},
run: foo, // 這里將函數foo作為方法賦值給run屬性
};
在這個例子中,p1對象的run屬性指向了之前定義的foo函數。然后,我們調用了eat和run方法:
p1.eat();
p1.run(1, 2, 3);
這時,我們深入研究了函數內部的this關鍵字。在對象方法中,this通常指向調用該方法的對象。但是,在JavaScript中,我們可以使用apply方法動態設置函數執行時的上下文。
var _foo = foo;
foo = function () {
console.log("截斷開始...");
_foo.apply(this, arguments);
console.log("截斷結束...");
};
在這個例子中,foo函數被重新定義,使用apply方法在函數執行前后輸出信息。此時,this在_foo.apply(this, arguments)中指向了foo函數本身。
最后,我們遇到了一個關于eval函數的問題。在JavaScript中,eval允許執行傳入的字符串作為JavaScript代碼。然而,eval的使用需要謹慎,因為它可能引起安全性和性能方面的問題。
eval('console.log("hello")');
var _eval = eval;
eval = function (src) {
console.log("eval截斷開始...");
_eval(src);
console.log("eval截斷結束...");
};
eval("console.log('hello....')");
在這個例子中,我們重新定義了eval函數,通過截斷的方式在函數執行前后輸出信息。
通過這一系列例子,我們深入了解了JavaScript中函數調用、上下文和一些關鍵字的使用。函數是JavaScript中的核心概念之一,理解函數的調用方式和上下文有助于更有效地使用這門語言。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。