在JavaScript中包含6種基本的值類型:數字(number)、字符串(string)、布爾值(boolean)、對象(object)、函數(function)、和未定義類型(undefined)node
數字(number)類型的值即數字值。在JavaScript中寫成以下形式:程序員
13web
注意:在處理分數的時候,將其視爲近似值,而非精確值shell
JavaScript中的算術運算以下所示:編程
100 + 4 * 11小程序
正無窮大: Infinity數組
負無窮大:-Infinity瀏覽器
非數值: NaNbash
咱們使用字符串來表示文本信息。使用引號將內容括起來。數據結構
"Patch my boat with chewing gum"
'Monkeys wave goodbye'
console.log(typeof 4.5); // number
console.log(typeof "x"); // string
複製代碼
該類型只有兩種取值:true和false
console.log(3 > 2); // true
console.log(3 < 2); // false
console.log("Aardark" < "Zoroaster"); // true
console.log( NaN == NaN); // false
複製代碼
JavaScript支持三種邏輯運算符:與(and)、或(or)、非(not)
console.log(true && false); // false
console.log(true && true); // true
console.log(false || true); // true
console.log(false || false); // false
// 三元運算符
console.log(true ? 1 : 2); // 1
console.log(false ? 1 : 2); // 2
複製代碼
null和undefined用於表示無心義的值。它們各自表示其自身含義,除此以外不包含任何信息。
console.log(8 * null); // 0
console.log("5" - 1); // 4
console.log("5" + 1); // 51
console.log("five" * 2); // NaN
console.log(false == 0); // true
console.log("" === false); // false
複製代碼
邏輯運算符的短路特性
console.log(null || "user"); // user
console.log("Karel" || "user"); // Karel
複製代碼
最簡單的一條語句由一個表達式和其後的分號組成。好比這就是一個程序:
1;
!false;
複製代碼
var caught = 5 * 5;
var ten = 10;
console.log(ten * ten); // 100
var mood = "light";
console.log(mood); // light
mood = "dark";
console.log(mood); // dark
var luigisDebt = 140;
luigisDebt = luigisDebt - 35;
console.log(luigisDebt); // 105
var one = 1,two = 2;
console.log(one + two); // 3
複製代碼
某些具備特殊含義的單詞稱之爲關鍵字,好比var,關鍵字不能做爲變量名。另外還有一些單詞預留給JavaScript使用。
咱們將給定時間內的變量和變量值的集合稱爲環境。
在默認環境中包含了不少函數類型的值。函數是指包裝在變量中的一段程序,咱們可使用這些值來執行包裝好的程序。
alert("Good morning!");
複製代碼
var x = 30;
console.log("the value of x is ",x); // the value of x is 30
複製代碼
咱們將函數生成值的操做稱之爲返回值。
console.log(Math.max(2,4); // 4
console.log(Math.min(2,4) + 100); // 102
複製代碼
在現代web編程當中不多使用這兩個函數,主要緣由是你沒法對彈出窗口的風格進行控制。但對於小程序或測試程序來講,這兩個函數仍是很是有用的。
confirm("Shall we,then?");
prompt("Tell me everything you know.","...");
複製代碼
若你的程序中包含了不止一條語句,那麼這些語句必定是按照從上到下的順序執行的。
var theNumber = Number(prompt("Pick a number","");
alert("Your number is the square root of " + theNumber * theNumber);
複製代碼
var theNumber = Number(prompt("Pick a number","");
if(!isNaN(theNumber)){
alert("Your number is the square root of " + theNumber * theNumber);
}else{
alert("Hey,Why didn't you give me a number?");
}
var num = Number(prompt("Pick a number","");
if(num < 10){
alert("small");
}else if(num < 100){
alert("medium");
}else{
alert("large");
}
複製代碼
var number = 0;
while(number <= 12){
console.log(number);
number = number + 2;
}
// 編寫一個程序用於顯示2的10次方
var result = 1;
var counter = 0;
while(counter < 10){
result = result * 2;
counter++;
}
console.log(result);
do{
var name = prompt("who are you?");
}while(!name);
console.log(name);
複製代碼
for(var number = 0; number <= 12;number = number + 2){
console.log(number);
}
var result = 1;
for(var counter = 0;counter < 10;counter++){
result = result * 2;
}
console.log(result);
複製代碼
for(var current = 20; ; current++){
if(current % 7 == 0){
break;
}
}
console.log(current); // 21
複製代碼
counter += 1;
result *= 2;
counter++;
counter--;
複製代碼
switch(prompt("What is the weather like?")){
case "rainly":
console.log("Remember to bring an umbrella");
break;
case "sunny":
console.log("Dress lightly");
break;
case "cloudly":
console.log("Go outside");
break;
default:
console.log("Unknown weather type!");
break;
}
複製代碼
var accountBalance = calculateBalance(account);
// It's a green hollow where a river sings accountBalance.adjust(); // Madly catching white tatters in the grass var report = new Report(); // Where the sun on the proud mountain rings: addToReport(accountBalance,report); // It's a little valley,foaming like light in a glass
/*
I first found ths number scrawled on the back of one of
my notebooks a few years ago.Since then,it has often
dropped by,showing up in phone numbers and the serial
numbers of products that I've bought.It obviously likes me,so I've decided to keep it.
*/
var myNumber = 112213;
複製代碼
// 1
var str = "";
for(var i = 0; i < 7;i++){
str = str + "#";
console.log(str);
}
// 2
for(var i = 1; i <= 100;i++){
if(i % 3 == 0){
console.log("Fizz");
}else if(i % 5 == 0){
console.log("Buzz")
}else{
console.log(i);
}
}
for(var i = 1; i <= 100;i++){
if(i % 3 == 0 && i % 5 == 0){
console.log("FizzBuzz");
}else if(i % 3 == 0){
console.log("Fizz")
}else if(i % 5 == 0){
console.log("Buzz")
}else{
console.log(i);
}
}
// 3
var str = "";
var size = 8;
for(var i = 0; i < size; i++){
for(var j = 0; j < size;j++){
if((i + j)% 2 == 0){
str = str + "#";
}else{
str = str + " ";
}
}
str += "\n";
}
console.log(str);
複製代碼
一個函數定義就是普通的變量定義,只不過變量類型剛好是函數。
建立函數的表達式以關鍵字function開頭。
某些函數能夠產生值,而一些函數不會產生值。函數中的return語句決定了函數的返回值。當函數執行到return語句時,會當即跳出當前函數,並將返回值賦給調用者。若是return關鍵字後沒有任何表達式,則該函數返回undefined。
// 計算給定數字的平方
var square = functon(x) {
return x*x;
}
console.log(square(12)); // 144
var makeNoise = function() {
console.log("Pling!");
}
makeNoise(); // Pling!
var power = function(base,exponent) {
var result = 1;
for(var count = 0;count < exponent;count++) {
result *= base;
return result;
}
}
console.log(power(2,10)); // 1024
複製代碼
函數的參數如同一個普通變量,但其初始值是由函數的調用者提供的,而不是函數自身的內部代碼。
函數有一個重要屬性,就是其內部建立的變量以及參數,都屬於函數的局部變量。這意味着示例中power函數的result變量會在每次函數調用從新建立,這種隔離機制確保了不一樣函數之間不會相互干擾。
var x = "outside";
var f1 = function() {
var x = "inside f1";
}
f1();
console.log(x); // outside
var f2 = function() {
x = "inside f2";
}
f2();
console.log(x); // inside f2
複製代碼
JavaScript不只能區分全局變量和局部變量,還能夠在函數中建立其餘函數,併產生不一樣程度的局部做用域。
var landscape = function(){
var result = "";
var flat = function(size){
for(var count = 0;count < size;count++){
result +="_";
}
}
var mountain = function(size) {
result +="/";
for(var count = 0;count < size;count++){
result += "'";
}
result += "\\";
}
flat(3);
mountain(4);
flat(6);
mountain(1);
flat(1);
return result;
};
console.log(landscape());
複製代碼
函數變量一般只是充當一段特定程序的名字。這種變量只須要定義一次,並且永遠不會改變。
你不只能夠直接調用函數,還能夠像使用其餘類型的值同樣使用函數類型的值、將其用在任何表達式中、將其存放在新的變量中或者將其做爲參數傳遞給其餘函數等。同理,一個用於存放函數的變量也只不過是一個普通變量,能夠被賦予新的值。
var launchMissiles = function(value) {
missileSystem.launch("now");
};
if(safeMode){
launchMissiles = function(value){
// do something
}
}
複製代碼
相較於使用「var square = function...」表達式來聲明函數,還可使用另外一種更爲簡潔的方法聲明函數。
function square(x) {
return x*x;
}
複製代碼
從概念上看,函數聲明會被移動到其做用域的頂端,全部做用域內的函數調用都不會有任何問題。
console.log("The future says:",future());
function future(){
return "We STILL have no flying cars."
}
console.log("The future says:",future1()); // Uncaught TypeError: future1 is not a function
var future1 = function () {
return "1111";
}
複製代碼
function greet(who){
console.log("Hello" + who);
}
greet("Harry");
console.log("Bye");
複製代碼
因爲函數須要在執行結束後跳轉回調用該函數的代碼位置,所以計算機必須記住函數調用的上下文。
咱們將計算機存儲這個上下文的區域稱之爲調用棧。每當函數調用時,當前的上下文信息就會被存儲在棧頂。當函數返回時,系統會刪除存儲在棧頂的上下文信息,並使用該信息繼續執行程序。
棧須要保存在計算機內存中。若棧存儲的空間過大,計算機就會提示相似於「棧空間溢出」或「遞歸過多」的信息。
function chicken(){
return egg();
}
function egg(){
return chicken();
}
console.log(chicken() + "came first.");
複製代碼
JavaScript對傳入函數的參數數量幾乎不作任何限制。若是你傳遞了過多參數,多餘的參數就會被忽略掉,而若是你傳遞的參數過少,遺漏的參數將會被賦值成undefined。
該特性的缺點是你可能剛好向函數傳遞了錯誤數量的參數,但沒有人會告訴你這個錯誤。
該特性的優勢是咱們能夠利用這種行爲來讓函數接收可選參數。
function power(base,exponent){
if(exponent == undefined) {
exponent = 2;
}
var result = 1;
for(var count = 0;count < exponent;count++){
result *= base;
}
return result;
}
console.log(power(4)); // 16
console.log(power(4,3)); // 64
複製代碼
函數能夠做爲值使用,並且其局部變量會在每次函數調用時從新建立。
function wrapValue(n) {
var localVariable = n;
return function() {
return localVariable;
}
}
var wrap1 = wrapValue(1);
var wrap2 = wrapValue(2);
console.log(wrap1()); // 1
console.log(wrap2()); // 2
複製代碼
其實同一變量的多個實例能夠同時存在,這也就很好地印證了局部變量會在每次函數調用時從新建立,不一樣的函數調用是不會對其餘函數內的局部變量產生影響的。
咱們把這種引用特定的局部變量實例的功能稱爲閉包。一個包裝了一些局部變量的函數是一個閉包。
function multiplier(factor) {
return function(number) {
return number * factor;
}
}
var twice = multiplier(2);
console.log(twice(5)); // 10
複製代碼
函數徹底能夠本身調用本身,只要避免棧溢出的問題便可。咱們把函數調用自身的行爲稱爲遞歸。
function power(base,exponent){
if(exponent == 0){
return 1;
}else{
return base * power(base,exponent -1);
}
}
console.log(power(2,3)); // 8
複製代碼
在標準的JavaScript實現當中,遞歸寫法的函數執行效率比循環寫法的函數慢了大約10倍。執行簡單的循環操做比屢次函數調用效率要高不少。
基本原則:除非程序執行速度確實太慢,不然先不要關注效率問題。一旦出現速度太慢的狀況,找出佔用時間最多的部分,而後將其替換成效率更高的代碼。
可是,咱們並不能以偏概全的說遞歸就只是比循環的效率低。對於某些問題來講,遞歸相較於循環更能解決問題。這類問題一般須要執行和處理多個分支,而每一個分支又會引出更多的執行分支。
// 從數字1開始,每次能夠將數字加5或乘以3,循環執行。這樣能夠產生無窮多個新數字。那麼如何編寫函數來找出一個加法乘法序列,以產生指定的數字呢?例如,數字13能夠經過1次乘法和2次加法生成,而數字15永遠沒法獲得。使用遞歸編碼的解決方案以下:
function findSolution(target){
function find(start,history){
if(start == target){
return history;
}else if(start > target){
return null;
}else{
return find(start + 5,"(" + history + "+5 )") || find(start * 3,"(" + history + "*3 )")
}
}
return find(1,"1");
}
console.log(findSolution(24));
複製代碼
這裏有兩種經常使用的方法將函數引入到程序中。
第一種方法是找出你的程序中屢次出現的類似代碼。咱們能夠把重複功能的代碼提取出來放到一個函數中去,並起一個合適的名字。
第二種方法是當你寫一些新功能代碼,並以爲這些代碼應該成爲一個函數時,咱們將這部分代碼寫到一個函數中,並取一個函數名。
// 編寫一個打印兩個數字的程序,第一個數字是農場中牛的數量,第二個數字是農場中雞的數量,並在數字後面跟上Cows 和Chickens用以說明,而且在兩個數字前填充0,以使得每一個數字老是由三位數字組成。
function printFarmInventory(cows,chickens) {
var cowString = String(cows);
while(cowString.length < 3){
cowString = "0" + cowString;
}
console.log(cowString + " cows");
var chickenString = String(chickens);
while(chickenString.length < 3){
chickenString = "0" + chickenString;
}
console.log(chickenString + " Chickens");
}
printFarmInventory(7,11);
// 擴展軟件來輸出豬的數量
function printZeroPaddedWithLabel(number,label){
var numberString = String(number);
while(numberString.length < 3){
numberString = "0" + numberString;
}
console.log(numberString + " " + label);
}
function printFarmInventory(cows,chickens,pigs){
printZeroPaddedWithLabel(cows,"Cows");
printZeroPaddedWithLabel(chickens,"Chickens");
printZeroPaddedWithLabel(pigs,"Pigs");
}
printFarmInventory(7,11,3);
// 繼續優化
function zeroPad(number,width){
var string = String(number);
while(string.length < width){
string = "0" + string;
}
return string;
}
function printFarmInventory(cows,chickens,pigs){
console.log(zeroPad(cows,3) + "Cows");
console.log(zeroPad(chickens,3) + "Chickens");
console.log(zeroPad(pigs,3) + "pigs");
}
printFarmInventory(7,16,3);
複製代碼
比較好的方法是,儘可能不要在函數中添加過多功能,除非你真的須要它。咱們老是但願可以編寫一個通用的框架來解決遇到的各種問題。可是別急,這麼作其實並不能解決任何問題,並且不會有人真的去使用這樣的代碼。
咱們能夠將函數分紅兩類:一類調用後產生反作用,而另外一類則產生返回值(固然咱們也能夠定義同時產生反作用和返回值的函數)。
相比於直接產生反作用的函數,產生返回值的函數則更容易集成到新的環境當中使用。
純函數是一種只會產生值並且不會影響它本身範圍外任何事情的函數。
// 1
function min(a,b){
if(a> b){
return b;
}else{
return a;
}
}
console.log(min(1,2));
// 2
function isEven(n){
if(n == 0){
return true;
}else if(n == 1){
return false;
}else if (n < 0){
return isEven(-n);
}else{
return isEven(n-2);
}
}
console.log(isEven(50)); // true
console.log(isEven(75)); // false
console.log(isEven(-1)); // false
// 3
function countBs(str){
var count = 0;
for(var i = 0;i < str.length;i++){
if(str[i] == 'B'){
count++;
}
}
return count;
}
console.log(countBs("12B45B12B"));
function countChar(str,target){
var count = 0;
for(var i = 0; i < str.length;i++){
if(str[i] == target){
count++;
}
}
return count;
}
console.log(countChar("12A345AaA","A"));
// 官方正解
function countChar(string, ch) {
let counted = 0;
for (let i = 0; i < string.length; i++) {
if (string[i] == ch) {
counted += 1;
}
}
return counted;
}
function countBs(string) {
return countChar(string, "B");
}
console.log(countBs("BBC"));
console.log(countChar("kakkerlak", "k"));
複製代碼
數字、布爾值和字符串構成了基本的數據結構。少了其中任何同樣,你可能都很難構造出完整的結構。咱們可使用對象來把值和其餘對象組織起來,經過這種手段來構造更爲複雜的結構。
JavaScript提供了一種數據類型,專門用於存儲一系列的值。咱們將這種數據類型稱爲數組(array),將一連串的值寫在方括號當中,值之間使用逗號(,)分隔。
var listOfNumbers = [2,3,5,7,11];
console.log(listOfNumbers[1]); // 3
console.log(listOfNumbers[1 -1]); // 2
複製代碼
在JavaScript中,幾乎全部的值都有屬性。但null和undefined沒有。
在JavaScript中有兩種最爲經常使用的訪問屬性的方法:使用點(.)和方括號[]。 若是使用點,則點以後的部分必須是一個合法變量名,即直接寫屬性名稱。若是使用方括號,則JavaScript會將方括號中表達式的返回值做爲屬性名稱。
除了length屬性之外,字符串和數組對象還包含了許多其餘屬性,這些屬性是函數值。
var doh = "Doh";
console.log(typeof doh.toUpperCase); // function
console.log(doh.toUpperCase()); // DOH
複製代碼
咱們一般將包含函數的屬性稱爲某個值的方法(method)。好比說,「toUpperCase是字符串的一個方法」。
var mack = [];
mack.push("Mack");
mack.push("the","knife");
console.log(mack);
console.log(mack.join(" "));
console.log(mack.pop());
console.log(mack);
複製代碼
咱們可使用push方法向數組的末尾添加值,pop方法則做用相反:刪除數組末尾的值,並返回給調用者。咱們可使用join方法將字符串數組拼接成單個字符串,join方法的參數是鏈接數組元素之間的文本內容。
對象類型的值能夠存儲任意類型的屬性,咱們能夠隨意增刪這些屬性。一種建立對象的方法是使用大括號。
var day1 = {
squirrel: false,
events: ["work","touched tree","pizza","running","television"]
};
console.log(day1.squirrel); // false
console.log(day1.wolf); // undefined
day1.wolf = false;
console.log(day1.wolf); // false
複製代碼
在大括號中,咱們能夠添加一系列的屬性,並用逗號分隔。每個屬性均以名稱開頭,緊跟一個冒號,而後是對應屬性的表達式。若是屬性名不是有效的變量名或者數字,則須要使用引號將其括起來。
var description = {
work: "Went to work",
"touched tree": "Touched a tree"
}
複製代碼
讀取一個不存在的屬性就會產生undefined值。
delete是個一元運算符,其操做數是訪問屬性的表達式,能夠從對象中移除指定屬性。
var anObject = {
left: 1,
right: 2
}
console.log(anObject.left); // 1
delete anObject.left;
console.log(anObject.left); // undefined
console.log("left" in anObject); // false
console.log("right" in anObject); // true
複製代碼
二元運算符in的第一個操做數是一個表示屬性名的字符串,第二個操做數是一個對象,它會返回一個布爾值,表示該對象是否包含該屬性。將屬性設置爲undefined與使用delete刪除屬性的區別在於:對於第一種狀況,對象仍然包含left屬性,只不過該屬性沒有引用任何值;對於第二種狀況,對象中已不存在left屬性,所以in運算符會返回false。
數組只不過是一種用於存儲數據序列的特殊對象,所以typeof[1,2]的執行結果是「Object」。
// 咱們能夠用一個數組對象來表示雅克的日誌
var journal = [
{
events: ["work","touched tree","pizza","running","television"],
squirrel: false
},
{
events: ["work","ice cream","cauliflower","lasagna","touched tree","brushed teeth"],
squirrel: false
}
/* and so on */
];
複製代碼
數字、字符串和布爾值都是不可變值,咱們沒法修改這些類型值的內容。
但對於對象來講,咱們能夠經過修改其屬性來改變對象的內容。
var object1 = {value: 10};
var object2 = object1;
var object3 = {value: 10};
console.log(object1 == object2); // true
console.log(object1 == object3); // false
object1.value = 15;
console.log(object2.value); // 15
console.log(object3.value); // 10
複製代碼
在JavaScript中,使用==運算符來比較兩個對象時,只有兩個對象引用了同一個值,結果纔會返回true。比較兩個不一樣的對象將會返回false,哪怕對象內容相同。JavaScript中沒有內置深度比較運算符(比較對象內容),但你能夠本身編寫一個。
var journal = [];
function addEntry(events,didITurnIntoASquirrel){
journal.push({
events: events,
squirrel: didITurnIntoASquirrel
})
}
addEntry(["work","touched tree","pizza","running","television"],false);
addEntry(["work","ice cream","cauliflower","lasagna","touched tree","brushed teeth"],false);
addEntry(["weekend","cycling","break","peanuts","beer"],true);
複製代碼
// 計算數組的係數Φ
function phi(table) {
return (table[3] * table[0] - table[2] * table[1])/Math.sqrt((table[2] + table[3]) * (table[0] + table[1]) * (table[1] + table[3]) * (table[0] + table[2]));
}
console.log(phi([76,9,4,1]));
// 循環遍歷整個記錄,並計算出與變身成松鼠相關事件發生的次數。
var JOURNAL = [
{"events":["carrot","exercise","weekend"],"squirrel":false},
{"events":["bread","pudding","brushed teeth","weekend","touched tree"],"squirrel":false},
{"events":["carrot","nachos","brushed teeth","cycling","weekend"],"squirrel":false},
{"events":["brussel sprouts","ice cream","brushed teeth","computer","weekend"],"squirrel":false},
{"events":["potatoes","candy","brushed teeth","exercise","weekend","dentist"],"squirrel":false},
{"events":["brussel sprouts","pudding","brushed teeth","running","weekend"],"squirrel":false},
{"events":["pizza","brushed teeth","computer","work","touched tree"],"squirrel":false},
{"events":["bread","beer","brushed teeth","cycling","work"],"squirrel":false},
{"events":["cauliflower","brushed teeth","work"],"squirrel":false},
{"events":["pizza","brushed teeth","cycling","work"],"squirrel":false},
{"events":["lasagna","nachos","brushed teeth","work"],"squirrel":false},
{"events":["brushed teeth","weekend","touched tree"],"squirrel":false},
{"events":["lettuce","brushed teeth","television","weekend"],"squirrel":false},
{"events":["spaghetti","brushed teeth","work"],"squirrel":false},
{"events":["brushed teeth","computer","work"],"squirrel":false},
{"events":["lettuce","nachos","brushed teeth","work"],"squirrel":false},
{"events":["carrot","brushed teeth","running","work"],"squirrel":false},
{"events":["brushed teeth","work"],"squirrel":false},
{"events":["cauliflower","reading","weekend"],"squirrel":false},
{"events":["bread","brushed teeth","weekend"],"squirrel":false},
{"events":["lasagna","brushed teeth","exercise","work"],"squirrel":false},
{"events":["spaghetti","brushed teeth","reading","work"],"squirrel":false},
{"events":["carrot","ice cream","brushed teeth","television","work"],"squirrel":false},
{"events":["spaghetti","nachos","work"],"squirrel":false},
{"events":["cauliflower","ice cream","brushed teeth","cycling","work"],"squirrel":false},
{"events":["spaghetti","peanuts","computer","weekend"],"squirrel":true},
{"events":["potatoes","ice cream","brushed teeth","computer","weekend"],"squirrel":false},
{"events":["potatoes","ice cream","brushed teeth","work"],"squirrel":false},
{"events":["peanuts","brushed teeth","running","work"],"squirrel":false},
{"events":["potatoes","exercise","work"],"squirrel":false},
{"events":["pizza","ice cream","computer","work"],"squirrel":false},
{"events":["lasagna","ice cream","work"],"squirrel":false},
{"events":["cauliflower","candy","reading","weekend"],"squirrel":false},
{"events":["lasagna","nachos","brushed teeth","running","weekend"],"squirrel":false},
{"events":["potatoes","brushed teeth","work"],"squirrel":false},
{"events":["carrot","work"],"squirrel":false},
{"events":["pizza","beer","work","dentist"],"squirrel":false},
{"events":["lasagna","pudding","cycling","work"],"squirrel":false},
{"events":["spaghetti","brushed teeth","reading","work"],"squirrel":false},
{"events":["spaghetti","pudding","television","weekend"],"squirrel":false},
{"events":["bread","brushed teeth","exercise","weekend"],"squirrel":false},
{"events":["lasagna","peanuts","work"],"squirrel":true},
{"events":["pizza","work"],"squirrel":false},
{"events":["potatoes","exercise","work"],"squirrel":false},
{"events":["brushed teeth","exercise","work"],"squirrel":false},
{"events":["spaghetti","brushed teeth","television","work"],"squirrel":false},
{"events":["pizza","cycling","weekend"],"squirrel":false},
{"events":["carrot","brushed teeth","weekend"],"squirrel":false},
{"events":["carrot","beer","brushed teeth","work"],"squirrel":false},
{"events":["pizza","peanuts","candy","work"],"squirrel":true},
{"events":["carrot","peanuts","brushed teeth","reading","work"],"squirrel":false},
{"events":["potatoes","peanuts","brushed teeth","work"],"squirrel":false},
{"events":["carrot","nachos","brushed teeth","exercise","work"],"squirrel":false},
{"events":["pizza","peanuts","brushed teeth","television","weekend"],"squirrel":false},
{"events":["lasagna","brushed teeth","cycling","weekend"],"squirrel":false},
{"events":["cauliflower","peanuts","brushed teeth","computer","work","touched tree"],"squirrel":false},
{"events":["lettuce","brushed teeth","television","work"],"squirrel":false},
{"events":["potatoes","brushed teeth","computer","work"],"squirrel":false},
{"events":["bread","candy","work"],"squirrel":false},
{"events":["potatoes","nachos","work"],"squirrel":false},
{"events":["carrot","pudding","brushed teeth","weekend"],"squirrel":false},
{"events":["carrot","brushed teeth","exercise","weekend","touched tree"],"squirrel":false},
{"events":["brussel sprouts","running","work"],"squirrel":false},
{"events":["brushed teeth","work"],"squirrel":false},
{"events":["lettuce","brushed teeth","running","work"],"squirrel":false},
{"events":["candy","brushed teeth","work"],"squirrel":false},
{"events":["brussel sprouts","brushed teeth","computer","work"],"squirrel":false},
{"events":["bread","brushed teeth","weekend"],"squirrel":false},
{"events":["cauliflower","brushed teeth","weekend"],"squirrel":false},
{"events":["spaghetti","candy","television","work","touched tree"],"squirrel":false},
{"events":["carrot","pudding","brushed teeth","work"],"squirrel":false},
{"events":["lettuce","brushed teeth","work"],"squirrel":false},
{"events":["carrot","ice cream","brushed teeth","cycling","work"],"squirrel":false},
{"events":["pizza","brushed teeth","work"],"squirrel":false},
{"events":["spaghetti","peanuts","exercise","weekend"],"squirrel":true},
{"events":["bread","beer","computer","weekend","touched tree"],"squirrel":false},
{"events":["brushed teeth","running","work"],"squirrel":false},
{"events":["lettuce","peanuts","brushed teeth","work","touched tree"],"squirrel":false},
{"events":["lasagna","brushed teeth","television","work"],"squirrel":false},
{"events":["cauliflower","brushed teeth","running","work"],"squirrel":false},
{"events":["carrot","brushed teeth","running","work"],"squirrel":false},
{"events":["carrot","reading","weekend"],"squirrel":false},
{"events":["carrot","peanuts","reading","weekend"],"squirrel":true},
{"events":["potatoes","brushed teeth","running","work"],"squirrel":false},
{"events":["lasagna","ice cream","work","touched tree"],"squirrel":false},
{"events":["cauliflower","peanuts","brushed teeth","cycling","work"],"squirrel":false},
{"events":["pizza","brushed teeth","running","work"],"squirrel":false},
{"events":["lettuce","brushed teeth","work"],"squirrel":false},
{"events":["bread","brushed teeth","television","weekend"],"squirrel":false},
{"events":["cauliflower","peanuts","brushed teeth","weekend"],"squirrel":false}
];
function hasEvent(event,entry) {
return entry.events.indexOf(event) != -1;
}
function tableFor(event,journal) {
var table = [0,0,0,0];
for(var i = 0; i < journal.length; i++){
var entry = journal[i];
var index = 0;
if(hasEvent(event,entry)){
index += 1;
}
if(entry.squirrel){
index += 2;
}
table[index] += 1;
}
return table;
}
console.log(tableFor("pizza",JOURNAL));
複製代碼
映射表(map)能夠經過一個值(在本例中是事件名)來獲取對應的另外一個值(在本例中是Φ係數)。
var map = {};
function storePhi(event,phi) {
map[event] = phi;
}
storePhi("pizza",0.069);
storePhi("touched tree",-0.081);
console.log("pizza" in map); // true
console.log(map["touched tree"]); // -0.081
複製代碼
JavaScript提供了另外一種遍歷對象屬性的循環語句。它與通常的for循環看起來很像,只是咱們使用的關鍵字不是for而是in。
for(var event in map) {
console.log("The correlation for '" + event + "' is " + map[event]);
}
// The correlation for 'pizza' is 0.069
// The correlation for 'touched tree' is -0.081
複製代碼
爲了找出數據集中存在的全部事件類型,咱們只需依次處理每條記錄,而後遍歷記錄中的全部事件便可。
function gatherCorrelations(journal) {
var phis = {};
for(var entry = 0;entry < journal.length;entry++){
var events = journal[entry].events;
for(var i = 0; i < events.length;i++){
var event = events[i];
if(!(event in phis)){
phis[event] = phi(tableFor(event,journal));
}
}
}
return phis;
}
var correlations = gatherCorrelations(JOURNAL);
console.log(correlations.pizza);
for(var event in correlations) {
console.log(event + ":" + correlations[event]);
}
for(var event in correlations) {
var correlation = correlations[event];
if(correlation > 0.1 || correlation < -0.1){
console.log(event + ":" + correlation);
}
}
for(var i = 0;i < JOURNAL.length;i++){
var entry = JOURNAL[i];
if(hasEvent("peanuts",entry) && !hasEvent("brushed teeth",entry)){
entry.events.push("peanut teeth");
}
}
console.log(phi(tableFor("peanut teeth",JOURNAL))); // 1
複製代碼
一些實用的數組方法
push和pop,分別用於在數組末尾添加或刪除元素。
unshift和shift,分別用於在數組的開頭添加或刪除元素。
var todoList = [];
function rememberTo(task) {
todoList.push(task);
}
function whatIsNext() {
return todoList.shift();
}
function urgentlyRememberTo(task) {
todoList.unshift(task);
}
複製代碼
indexOf, 從數組第一個元素向後搜索。
lastIndexOf,從數組最後一個元素向前搜索。
indexOf和lastIndexOf方法都有一個可選參數,能夠用來指定搜索的起始位置。
console.log([1,2,3,2,1].indexOf(2)); // 1
console.log([1,2,3,2,1].lastIndexOf(2)); // 3
複製代碼
slice,該方法接受一個起始索引和一個結束索引,而後返回數組中兩個索引範圍內的元素。起始索引元素包含在返回結果中,但結束索引元素不會包含在返回結果中。若是沒有指定結束索引,slice會返回從起始位置以後的全部元素。對於字符串來講,它也有一個具備相同功能的slice方法供開發人員使用。
console.log([0,1,2,3,4].slice(2,4)); // [2,3]
console.log([0,1,2,3,4].slice(2)); // [2,3,4]
複製代碼
concat 方法用於拼接兩個數組,其做用相似於字符串的+運算符。
function remove(array,index) {
return array.slice(0,index).concat(array.slice(index + 1 ));
}
console.log(remove(["a","b","c","d","e"],2)); // ["a","b","d","e"]
複製代碼
咱們能夠調用字符串的length或toUpperCase這樣的屬性,但不能向字符串中添加任何新的屬性。
var myString = "Fido";
myString.myProperty = "value";
console.log(myString.myProperty); // undefined
複製代碼
字符串、數字和布爾類型的值並非對象,所以當你向這些值中添加屬性時JavaScript並不會報錯,但實際上你並無將這些屬性添加進去。這些值都是不可變的,並且沒法向其中添加任何屬性。
但這些類型的值包含一些內置屬性。每一個字符串中包含了若干方法供咱們使用,最有用的方法可能就是slice和indexOf了,它們的功能與數組中的同名方法相似。
console.log("coconuts".slice(4,7)); // nut
console.log("coconuts".indexOf("u")); // 5
複製代碼
惟一的區別在於,字符串的indexOf方法可使用多個字符做爲搜索條件,而數組中的indexOf方法則只能搜索單個元素。
console.log("one two three".indexOf("ee")); // 11
複製代碼
trim方法用於刪除字符串中開頭和結尾的空白符號(空格、換行和製表符等符號)。
console.log(" okay \n ".trim()); // okay
複製代碼
// 獲取字符串中某個特定的字符
var string = "abc";
console.log(string.length); // 3
console.log(string.charAt(0)); // a
console.log(string[1]); // b
複製代碼
每當函數被調用時,就會在函數體的運行環境當中添加一個特殊的變量arguments。該變量指向一個包含了全部入參的對象。在JavaScript中,咱們能夠傳遞多於(或少於)函數參數列表定義個數的參數。
function noArguments(){};
noArguments(1,2,3); // This is okay
function threeArgumnents(a,b,c){};
threeArguments(); // And so is this
複製代碼
arguments對象有一個length屬性,表示實際傳遞給函數的參數個數。每一個參數對應一個屬性,被命名爲0,1,2,以此類推。
function argumentCounter() {
console.log("You gave me",arguments.length,"arguments.");
}
argumentCounter("Straw man","Tautology","Ad hominem"); // You gave me 3 arguments.
複製代碼
function addEntry(squirrel){
var entry = {
events: [],
squirrel: squirrel
};
for(var i = 1; i < arguments.length; i++){
entry.events.push(arguments[i]);
}
journal.push(entry);
}
addEntry(true,"work","touched tree","pizza","running","television");
複製代碼
Math對象簡單地把一組相關的功能打包成一個對象供用戶使用。全局只有一個Math對象,其對象自己沒有什麼實際用途。Math對象其實提供了一個「命名空間」,封裝了全部的數學運算函數和值,確保這些元素不會變成全局變量。
過多的全局變量會對命名空間形成「污染」。全局變量越多,就越有可能一不當心把某些變量的值覆蓋掉。
function randomPointOnCircle(radius) {
var angle = Math.random() * 2 * Math.PI;
return {
x: radius * Math.cos(angle),
y: radius * Math.sin(angle)
};
}
console.log(randomPointOnCircle(2));
複製代碼
Math.random,每次調用該函數時,會返回一個僞隨機數,範圍在0(包括) ~ 1(不包括)之間。
console.log(Math.random());
console.log(Math.random());
console.log(Math.random());
複製代碼
Math.floor,向下取整
Math.ceil,向上取整
Math.round,四捨五入
console.log(Math.floor(Math.random() * 10)); // 等機率地取到0~9中的任何一個數字。
複製代碼
JavaScript全局做用域中有許多全局變量,均可以經過全局對象進行訪問。每個全局變量做爲一個屬性存儲在全局對象當中。在瀏覽器中,全局對象存儲在window變量當中。
var myVar = 10;
console.log("myVar" in window); // true
console.log(window.myVar); // 10
複製代碼
對象和數組(一種特殊對象)能夠將幾個值組合起來造成一個新的值。
在JavaScript中,除了null和undefined之外,絕大多數的值都含有屬性。
在數組中有一些具名屬性,好比length和一些方法。
對象能夠用做映射表,將名稱與值關聯起來。咱們可使用in運算符肯定對象中是否包含特定名稱的屬性。咱們一樣能夠在for循環中(for(var name in object))使用關鍵字in來遍歷對象中包含的屬性。
// 1 編寫一個range函數,接受兩個參數:start和end,而後返回包含start到end(包括end)之間的全部數字。
function range(start,end){
var arr = [];
if(start > end){
for(var i = start; i >= end;i--){
arr.push(i);
}
}else{
for(var i = start; i <= end;i++){
arr.push(i);
}
}
return arr;
}
console.log(range(1,10));
console.log(range(5,2,-1));
// 2 編寫一個sum函數,接受一個數字數組,並返回全部數字之和
function sum(arr){
var sum = 0;
for(var i = 0; i < arr.length;i++){
sum += arr[i];
}
return sum;
}
console.log(sum(range(1, 10)));
// 3 附加題修改range函數,接受第3個可選參數,指定構建數組時的步數(step)。
function range(start,end,step){
var arr = [];
if(step == undefined){
step = 1;
}
if(start > end){
for(var i = start; i >= end;i= i + step){
arr.push(i);
}
}else{
for(var i = start; i <= end;i = i + step){
arr.push(i);
}
}
return arr;
}
console.log(range(1,10,2));
console.log(range(5,2,-1));
// 官方正解
function range(start, end, step = start < end ? 1 : -1) {
let array = [];
if (step > 0) {
for (let i = start; i <= end; i += step) array.push(i);
} else {
for (let i = start; i >= end; i += step) array.push(i);
}
return array;
}
function sum(array) {
let total = 0;
for (let value of array) {
total += value;
}
return total;
}
// 逆轉數組
function reverseArray(arr){
var output = [];
for(var i = arr.length-1;i >= 0;i--){
output.push(arr[i]);
}
return output;
}
console.log(reverseArray([1,2,3,4,5]));
// 逆轉數組2
function reverseArrayInPlace(array){
for(let i = 0; i < Math.floor(array.length / 2);i++){
let old = array[i];
array[i] = array[array.length -1-i];
array[array.length-1-i]= old;
}
return array;
}
console.log(reverseArrayInPlace([1,2,3,4,5]));
// 實現列表
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
}
function arrayToList(array) {
let list = null;
for (let i = array.length - 1; i >= 0; i--) {
list = {value: array[i], rest: list};
}
return list;
}
console.log(arrayToList([1,2,3]));
// 列表轉換成數組
function listToArray(list) {
let array = [];
for (let node = list; node; node = node.rest) {
array.push(node.value);
}
return array;
}
function listToArray(list){
let array = [];
for(let node = list;node;node = node.rest){
array.push(node.value);
}
return array;
}
console.log(listToArray(list));
// 建立一個新的列表
function prepend(value,list) {
return {
value,
rest: list
};
}
// 返回列表中指定位置的元素
function nth(list, n) {
if (!list) return undefined;
else if (n == 0) return list.value;
else return nth(list.rest, n - 1);
}
function nth(list,n) {
if(!list){
return undefined;
}else if(n == 0){
return list.value;
}else {
return nth(list.rest,n-1);
}
}
console.log(list,3);
// 深度比較 編寫一個函數deepEqual,接受兩個參數,若兩個對象是同一個值或兩個對象中有相同屬性,且使用deepEqual比較屬性值均返回true時,返回true
function deepEqual(a, b) {
if (a === b) return true;
if (a == null || typeof a != "object" ||
b == null || typeof b != "object") return false;
let keysA = Object.keys(a), keysB = Object.keys(b);
if (keysA.length != keysB.length) return false;
for (let key of keysA) {
if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false;
}
return true;
}
複製代碼
讓咱們簡單回顧一下前言當中的兩個示例。其中第一個程序包含了6行代碼並能夠直接運行。
var total = 0,count = 1;
while(count <= 10){
total += count;
count += 1;
}
console.log(total);
複製代碼
第二個程序則依賴於外部函數才能執行,且只有一行代碼。
console.log(sum(range(1,10)));
複製代碼
第二個程序編寫的代碼很好地表達了咱們指望解決的問題。相比於將這些代碼直接寫到一塊兒,這種表述方式更爲簡單,同時也易於避免錯誤。
在程序設計中,咱們把這種編寫代碼的方式稱爲抽象。抽象能夠隱藏底層的實現細節,從更高(或更加抽象)的層次看待咱們要解決的問題。
做爲一名程序員,咱們須要具有在恰當時候將代碼抽象出來,造成一個新的函數或概念的能力。
// 1.將數組中的每一個元素打印到控制檯
var array = [1,2,3];
for(var i = 0; i < array.length;i++){
var current = array[i];
console.log(current);
}
// 2.將1抽象成一個函數
function logEach(array){
for(var i = 0; i < array.length;i++){
console.log(array[i]);
}
}
function forEach(array,action){
for(var i = 0; i < array.length;i++){
action(array[i]);
}
}
forEach(["Wampeter","Foma","Granfalloon"],console.log);
// 3.一般來講,咱們不會給forEach傳遞一個預約義的函數,而是直接新建一個函數值。
var numbers = [1,2,3,4,5],sum = 0;
forEach(numbers,function(number){
sum += number;
});
console.log(sum);
複製代碼
實際上,咱們不須要本身編寫forEach函數,該函數實際上是數組的一個標準方法。
function gatherCorrelations(journal) {
var phis = {};
for(var entry = 0;entry < journal.length;entry++){
var events = journal[entry].events;
for(var i = 0; i < events.length;i++){
var event = events[i];
if(!(event in phis)){
phis[event] = phi(tableFor(event,journal));
}
}
}
return phis;
}
// 使用forEach改寫上面的代碼
function gatherCorrelations(journal){
var phis = {};
journal.forEach(function(entry){
entry.events.forEach(function (event){
if(!(event in phis)){
phis[event] = phi(tableFor(event,journal));
}
})
})
return phis;
}
複製代碼
若是一個函數操做其餘函數,即將其餘函數做爲參數或將函數做爲返回值,那麼咱們能夠將其稱爲高階函數。
咱們可使用高階函數對一系列操做和值進行抽象。
// 使用高階函數新建另外一些函數
function greaterThan(n) {
return function(m){
return m > n;
}
}
var greaterThan10 = greaterThan(10);
console.log(greaterThan10(11)); // true
// 使用高階函數來修改其餘函數
function noisy(f) {
return function(arg){
console.log("calling with",arg);
var val = f(arg);
console.log("called with",arg,"-got",val);
return val;
}
}
noisy(Boolean)(0);
// calling with 0
// called with 0 -got false
// 使用高階函數來實現新的控制流
function unless(test,then) {
if(!test){
then();
}
}
function repeat(times,body){
for(var i = 0;i < times;i++){
body(i);
}
}
repeat(3,function(n){
unless(n % 2,function (){
console.log(n,"is even");
})
})
複製代碼
咱們在前面定義的函數noisy會把參數傳遞給另外一個函數使用,這會致使一個至關嚴重的問題。
function noisy(f) {
return function(arg){
console.log("calling with",arg);
var val = f(arg);
console.log("called with",arg,"-got",val);
return val;
}
}
複製代碼
若是函數f接受多個參數,那麼該函數只能接受第一個參數。函數f沒有辦法知道調用者傳遞給noisy的參數個數。
JavaScript函數的apply方法能夠解決這個問題。
function transparentWrapping(f){
return function(){
return f.apply(null,arguments);
}
}
複製代碼
這裏的transparentWrapping函數沒有什麼實際用處,但該函數返回的內部函數能夠將用戶指定的參數所有傳遞給f。
[
{
"name": "Emma de Milliano",
"sex": "f",
"born": 1876,
"died": 1956,
"father": "Petrus de Milliano",
"mother": "Sophia van Damme"
},
{
"name": "Carolus",
"sex": "m",
"born": 1832,
"died": 1905,
"father": "Carel Haverbeke",
"mother": "Maria van Brussel"
}
... and so on
]
複製代碼
這種格式是JSON格式,即JavaScript Object Notation的縮寫。該格式普遍用於數據存儲和web通訊。
全部屬性名都必須用雙引號括起來,並且只能使用簡單的數據表達式,不能填寫函數調用、變量以及任何含有實際計算過程的代碼。
JavaScript提供了JSON.stringify函數,用於將數據轉換成該格式。還有JSON.parse函數,用於將該格式數據轉換成原有的數據類型。
var string = JSON.stringify({ name: "X",born: 1980});
console.log(string); // {"name":"X","born":1980}
console.log(JSON.parse(string).born); // 1980
複製代碼
function filter(array,test){
var passed = [];
for(var i = 0; i < array.length;i++){
if(test(array[i])){
passed.push(array[i]);
}
}
return passed;
}
console.log(filter(ancestry,function(person) {
return person.born > 1990 && person.born < 1925
}))
複製代碼
該函數使用test函數做爲參數來實現過濾操做。咱們對數組中的每一個元素調用test函數,並經過返回值來肯定當前元素是否知足條件。
與forEach同樣,filter函數也是數組中提供的一個標準方法。本例中定義的函數只是用於展現內部實現原理。
console.log(ancestry.filter(function (person){
return person.father == "Carel Haverbeke";
}))
複製代碼
map方法能夠對數組中的每一個元素調用函數,而後利用返回值來構建一個新的數組,實現轉換數組的操做。
function map(array,transform){
var mapped = [];
for(var i = 0;i < array.length;i++){
mapped.push(transform(array[i]);
}
return mapped;
}
var overNinety = ancestry.filter(function(person){
return person.died - person.born > 90;
})
console.log(map(overNinety,function(person){
return person.name;
}))
複製代碼
與forEach和filter同樣,map也是數組中的一個標準方法。
根據整個數組計算出一個值。
reduce函數包含三個參數:數組、執行合併操做的函數和初始值。
function reduce(array,combine,start) {
var current = start;
for(var i = 0;i < array.length;i++){
current = combine(current,array[i]);
}
return current;
}
console.log(reduce([1,2,3,4],function(a,b){
return a+b;
},0))
複製代碼
數組中有一個標準的reduce方法,固然和咱們上面看到的那個函數一致,能夠簡化合並操做。
console.log(ancestry.reduce(function(min,cur){
if(cur.born < min.born){
return cur;
}else{
return min;
}
}))
複製代碼
// 在不使用高階函數的狀況下,實現以上示例
var min = ancestry[0];
for(var i = 1;i < ancestry.length;i++){
var cur = ancestry[i];
if(cur.born < min.born){
min = cur;
}
}
console.log(min);
複製代碼
這段代碼中多了一些變量,雖然多了兩行代碼,但代碼邏輯仍是很容易讓人理解的。
當你遇到須要組合函數的狀況時,高階函數的價值就突顯出來了。舉個例子,編寫一段代碼,找出數據集中男人和女人的平均年齡。
function average(array) {
function plus(a,b){
return a+b;
}
return array.reduce(plus) / array.length;
}
function age(p){
return p.died -p.born;
}
function male(p){
return p.sex == "m";
}
function female(p){
return p.sex == "f";
}
console.log(average(ancestry.filter(male).map(age)));
console.log(average(ancestry.filter(female).map(age)));
複製代碼
這段代碼並無將邏輯放到整個循環體中,而是將邏輯巧妙地組合成了咱們所關注的幾個方面:判斷性別、計算年齡和計算平均數。
咱們能夠採用這種方式編寫出邏輯清晰的代碼。不過,編寫這樣的代碼也是有代價的。
將函數傳遞給forEach來處理數組迭代任務的確十分方便並且易於閱讀。但JavaScript中函數調用卻比簡單的循環結構代價更高。
當程序執行很慢的時候,問題每每只是嵌套最深的循環體中的一小部分代碼引發的。
// 構建一個對象,將祖輩的姓名與表示人的對象關聯起來
var byName = {};
ancestry.forEach(function(person){
byName[person.name] = person;
})
console.log(byName["Philibert Haverbeke"]);
// 編寫reduceAncestors函數,用於從家譜樹中提煉出一個值。
function reduceAncestors(person,f,defaultValue){
function valueFor(person){
if(person == null){
return defaultValue;
}else{
return f(person,valueFor(byName[person.mother]),valueFor(byName[person.father]));
}
}
return valueFor(person);
}
function sharedDNA(person,formMother,formFather){
if(person.name == "Pauwels van Haverbeke"){
return 1;
}else{
return (formMother + formFather) / 2;
}
}
var ph = byName["Philibert Haverbeke"];
console.log(reduceAncestors(ph,sharedDNA,0) / 4);
// 找出知足特定條件的祖先比例,好比能夠查找年齡超過70歲的人
function countAncestors(person,test){
function combine(person,formMother,formFather){
var thisOneCounts = test(person);
return fromMather + formFather + (thisOneCounts ? 1 : 0);
}
return reduceAncestors(person,combine,0);
}
function longLivingPercentage(person){
var all = countAncestors(person,function(person){
return true;
})
var longLiving = countAncestors(person,function(person){
return (person.died - person.born) >= 70;
})
return longLiving / all;
}
console.log(longLivingPercentage(byName(["Emile Haverbeke"]));
複製代碼
每一個函數都有一個bind方法,該方法能夠用來建立新的函數,稱爲綁定函數。
var theSet = ["Carel Haverbeke","Maria van Brussel","Donald Duke"];
function isInSet(set,person){
return set.indexOf(person.name) > -1;
}
console.log(ancestry.filter(function(person){
return isInset(theSet,person);
}))
console.log(ancestry.filter(isInSet.bind(null,theSet))); // same result
複製代碼
調用bind會返回一個新的函數,該函數調用isInSet時會將theSet做爲第一個參數,並將傳遞給該函數的剩餘參數一塊兒傳遞給isInSet。
將函數類型的值傳遞給其餘函數不只十分有用,並且仍是JavaScript中一個重要的功能。咱們能夠在編寫函數的時候把某些特定的操做預留出來,並在真正的函數調用中將具體的操做做爲函數傳遞進來,實現完整的計算過程。
數組中提供了不少實用的高階函數,其中forEach用於遍歷數組元素,實現某些特定的功能。filter用於過濾掉一些元素,構造一個新數組。map會構建一個新數組,並經過一個函數處理每一個元素,將處理結果放入新數組中。reduce則將數組元素最終概括成一個值。
函數對象有一個apply方法,咱們能夠經過該方法調用函數,並使用數組來指定函數參數。另外還有一個bind方法,它用於建立一個新函數,並預先肯定其中一部分參數。
// 1.數組降維
// 結合使用reduce與concat方法,將輸入的二維數組(數組的數組)中的元素提取出來,並存放到一個一維數組中
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
// 2.計算母子年齡差
function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}
var byName = {};
ancestry.forEach(function(person) {
byName[person.name] = person;
});
var differences = ancestry.filter(function(person) {
return byName[person.mother] != null;
}).map(function(person) {
return person.born - byName[person.mother].born;
});
console.log(average(differences));
// 3.計算平均壽命
function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}
function groupBy(array, groupOf) {
var groups = {};
array.forEach(function(element) {
var groupName = groupOf(element);
if (groupName in groups)
groups[groupName].push(element);
else
groups[groupName] = [element];
});
return groups;
}
var byCentury = groupBy(ancestry, function(person) {
return Math.ceil(person.died / 100);
});
for (var century in byCentury) {
var ages = byCentury[century].map(function(person) {
return person.died - person.born;
});
console.log(century + ": " + average(ages));
}
// 4.使用every和some方法
function every(array, predicate) {
for (var i = 0; i < array.length; i++) {
if (!predicate(array[i]))
return false;
}
return true;
}
function some(array, predicate) {
for (var i = 0; i < array.length; i++) {
if (predicate(array[i]))
return true;
}
return false;
}
console.log(every([NaN, NaN, NaN], isNaN));
// → true
console.log(every([NaN, NaN, 4], isNaN));
// → false
console.log(some([NaN, 3, 4], isNaN));
// → true
console.log(some([2, 3, 4], isNaN));
// → false
複製代碼
方法只是引用了函數值的屬性。如下是一個簡單的方法:
var rabbit = {};
rabbit.speak = function(line){
console.log("The rabbit says '" + line + "'");
}
rabbit.speak("I'm alive."); // The rabbit says 'I'm alive.' 複製代碼
方法一般會在對象被調用時執行一些操做。將函數做爲對象的方法調用時,會找到對象中對應的屬性並直接調用。在調用object.method()時,對象中的一個特殊變量this會指向當前方法所屬的對象。
function speak(line){
console.log("The " + this.type + " rabbit says '" + line + "'");
}
var whiteRabbit = { type: "white",speak: speak };
var fatRabbit = { type: "fat",speak: speak };
whiteRabbit.speak("Oh my ears and whiskers, " + "how late it's getting!");
fatRabbit.speak("I could sure use a carrot right now.");
// The white rabbit says 'Oh my ears and whiskers, how late it's getting!' // The fat rabbit says 'I could sure use a carrot right now.' 複製代碼
這段代碼使用了關鍵字this來輸出正在說話的兔子的種類。咱們回想一下apply和bind方法,這兩個方法接受的第一個參數能夠用來模擬對象中方法的調用。這兩個方法會把第一個參數複製給this。
函數有一個call方法,相似於apply方法。該方法也能夠用於函數調用,但該方法會像普通函數同樣接受參數,咱們不須要將參數放到數組中。和apply和bind方法同樣,你也能夠向call方法傳遞一個特定的this值。
speak.apply(fatRabbit,["Burp!"]); // The fat rabbit says 'Burp!'
speak.call({type: "old"},"Oh my."); // The old rabbit says 'Oh my.'
複製代碼
咱們來仔細看看如下這段代碼。
var empty = {};
console.log(empty.toString); // ƒ toString() { [native code] }
console.log(empty.toString()); // [object Object]
複製代碼
每一個對象除了擁有本身的屬性外,幾乎都包含一個原型(prototype)。原型是另外一個對象,是對象的一個屬性來源。當開發人員訪問一個對象不包含的屬性時,就會從對象原型中搜索屬性,接着是原型的原型,以此類推。
那麼空對象的原型是什麼呢?是Object.prototype,它是全部對象中原型的父原型。
console.log(Object.getPrototypeOf({}) == Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null
複製代碼
JavaScript對象原型的關係是一種樹形結構,整個樹形結構的根部就是Object.prototype。Object.prototype提供了一些能夠在全部對象中使用的方法。好比說,toString方法能夠將一個對象轉換成其字符串表示形式。
許多對象並不直接將Object.prototype做爲其原型,而會使用另外一個原型對象,用於提供對象本身的默認屬性。函數繼承自Function.prototype,而數組繼承自Array.prototype。
console.log(Object.getPrototypeOf(isNaN) == Function.prototype); // true
console.log(Object.getPrototypeOf([]) == Array.prototype); // true
複製代碼
對於這樣的原型對象來講,其自身也包含了一個原型對象,一般狀況下是Object.prototype,因此說,這些原型對象能夠間接提供toString這樣的方法。
var protoRabbit = {
speak: function(line){
console.log("The " + this.type + " rabbit says '" + line +"'");
}
}
var killerRabbit = Object.create(protoRabbit);
killerRabbit.type = "killer";
killerRabbit.speak("SKREEEE!"); // The killerrabbit says 'SKREEEE!'
複製代碼
原型對象protoRabbit是一個容器,用於包含全部兔子對象的公有屬性。每一個獨立的兔子對象(好比killerRabbit)能夠包含其自身屬性(好比本例中的type屬性),也能夠派生其原型對象中公有的屬性。
在JavaScript中,調用函數以前添加一個關鍵字new則表示調用其構造函數。構造函數中包含了指向新對象的變量this,除非構造函數顯式地返回了另外一個對象的值,不然構造函數會返回這個新建立的對象。
經過關鍵字new建立的對象稱之爲構造函數的實例。
這裏給出一個簡單的用於建立rabbit的構造函數。構造函數的名稱通常以大寫字母開頭。
function Rabbit(type){
this.type = type;
}
var killerRabbit = new Rabbit("killer");
var blackRabbit = new Rabbit("black");
console.log(blackRabbit.type); // black
複製代碼
對於構造函數來講(實際上,對全部函數適用),都會自動得到一個名爲prototype的屬性。在默認狀況下,該屬性是一個普通的派生自Object.prototype的空對象。全部使用特定構造函數建立的對象都會將構造函數的prototype屬性做爲其原型。所以,咱們能夠很容易地爲全部使用Rabbit構造函數建立的對象添加speak方法。
Rabbit.prototype.speak = function(line){
console.log("The " + this.type + " rabbit says '" + line +"'");
}
blackRabbit.speak("Doom...");
複製代碼
構造函數其實就是函數,所以其實際原型是Function.prototype。而構造函數的prototype屬性則是其所建立的實例的原型,而非構造函數自身的原型。
當你向一個對象添加屬性時,不管該屬性是否已經存在於對象原型中,該屬性都會被添加到這個對象中去,並做爲對象本身的屬使用。若是原型中存在同名屬性,那麼在調用該屬性時,就不會再調用原型中的那個屬性了,轉而調用咱們添加到對象中的屬性。但原型自己不會被修改。
Rbbit.prototype.teeth = "small";
console.log(killerRabbit.teeth); // small
killerRabbit.teeth = "long, sharp, and bloody";
console.log(blackRabbit.teeth); // small
console.log(killerRabbit.teeth); // long, sharp, and bloody
console.log(Rabbit.prototype.teeth); // small
複製代碼
覆蓋原型中存在的屬性是頗有用的一個特性。
咱們也能夠爲標準函數和數組原型提供一個不一樣於Object原型的toString方法。
console.log(Array.prototype.toString == Object.prototype.toString); // false
console.log([1,2].toString()); // 1,2
複製代碼
直接使用數組調用Object.prototype.toString則會產生一個徹底不一樣的字符串。
console.log(Object.prototype.toString.call([1,2])); // [object Array]
ps:檢測對象類型的最佳方式
複製代碼
咱們隨時均可以使用原型對象添加新的屬性和方法。
Rabbit.prototype.dance = function(){
console.log("The " + this.type + " rabbit dances a jig.");
}
killerRabbit.dance(); // The killer rabbit dances a jig.
複製代碼
回顧第4章中的示例:
var map = {};
function storePhi(event,phi){
map[event] = phi;
}
storePhi("pizza",0.069);
storePhi("touched tree",-0.081);
複製代碼
咱們可使用for/in循環遍歷對象中全部的phi係數,並使用in操做符測試對象是否包含對應的屬性。但不幸的是,這種方式會到對象的原型中尋找屬性。
Object.prototype.nonsense = "hi";
for(var name in map){
console.log(name);
}
// pizza
// touched tree
// nonsense
console.log("nonsense" in map); // true
console.log("toString" in map); // true
複製代碼
toString並無出如今for/in循環中,而使用in運算符測試時則返回true。這是由於JavaScript會區分「可枚舉(enumerable)」與「不可枚舉(nonenumerable)」屬性。
咱們建立並賦予對象的全部屬性都是可枚舉的。而Object.prototype中的標準屬性都不可枚舉,所以這些標準屬性不會出如今for/in循環中。
咱們可使用Object.defineProperty函數定義本身的不可枚舉屬性。
Object.defineProperty(Object.prototype,"hiddenNonsense",{
enumerable: false,
value: "hi"
})
for(var name in map){
console.log(name);
}
// pizza
// touched tree
console.log(map.hiddenNonsense); // hi
複製代碼
常規的in運算符會認爲Object.prototype中的屬性存在於咱們的對象中。而對象的hasOwnProperty方法會告知咱們對象自身是否包含某個屬性,而不會搜索其原型。
console.log(map.hasOwnProperty("toString")); // false
複製代碼
當你擔憂某些人(裝載到你程序中的某些其餘代碼)會干擾基礎對象的原型時,我建議這樣使用for/in循環:
for(var name in map){
if(map.hasOwnProperty(name)){
// ... this is an own property
}
}
複製代碼
咱們可使用Object.create函數並根據特定原型來建立對象。你能夠傳遞null做爲原型,並建立一個無原型對象。
var map = Object.create(null);
map["pizza"] = 0.069;
console.log("toString" in map); // false
console.log("pizza" in map); // true
複製代碼
當編寫一段代碼時,咱們可使用包含特定接口的對象進行工做。在這個例子中是toString方法,只要對象支持這些接口,咱們就能夠將這些對象插入代碼中,並保證代碼正常工做。
咱們將這種技術稱爲多態(polymorphism)。雖然在整個過程當中沒有修改任何東西的形狀,但咱們仍是這麼稱呼這種技術。咱們能夠利用多態來操做不一樣類型的值,只要這些值支持所需的接口便可。
咱們來經過一個稍微複雜的例子來深刻了解一下多態和麪向對象的編程思想。咱們編寫一個程序,將一個由表格單元格組成的二維數組轉化成字符串,該字符串包含了與二維數組對應且佈局規整的表格,要求每一列筆直整齊,每一行也要保證對齊。
首先,程序會計算每列的最小寬度和每行的最大高度,並保存到數組中。變量rows是一個二維數組,其中的每一個數組元素用來表示一個單元格組成的行。
function rowHeights(rows){
return rows.map(function(row){
return row.reduce(function(max,cell){
return Math.max(max,cell.minHeight());
},0);
});
}
function colWidths(rows){
return rows[0].map(function(_,i){
return rows.reduce(function(max,row){
return Math.max(max,row[i].minWidth());
},0);
});
}
複製代碼
下面是繪製表格的代碼:
function drawTable(rows) {
var heights = rowHeights(rows);
var widths = colWidths(rows);
function drawLine(blocks,lineNo){
return blocks.map(function (block){
return block[lineNo]
}).join(" ");
}
function drawRow(row,rowNum) {
var blocks = row.map(function(_,lineNo){
return drawLine(blocks,lineNo);
}).join("\n");
}
return rows.map(drawRow).join("\n");
}
複製代碼
如今,咱們來編寫用於建立文本單元格的構造函數,實現表格的單元格接口。
function repeat(string,times){
var result = "";
for(var i = 0; i < times; i++){
result += string;
}
return result;
}
function TextCell(text){
this.text = text.split("\n");
}
TextCell.prototype.minWidth = function (){
return this.text.reduce(function (width,line){
return Math.max(width,line.length);
},0)
}
TextCell.prototype.minHeight = function (){
return this.text.length;
}
TextCell.prototype.draw = function(width,height){
var result = [];
for(var i = 0;i < height;i++){
var line = this.text[i] || "";
result.push(line + repeat(" ",width - line.height));
}
return result;
}
複製代碼
讓咱們來使用編寫好的程序來建立一個5*5的棋盤。
var rows = [];
for(var i = 0; i < 5; i++){
var row = [];
for(var j = 0; j < 5;j++){
if((j + i) % 2 == 0){
row.push(new TextCell("##"));
}else{
row.push(new TextCell(" "));
}
}
rows.push(row);
}
console.log(drawTable(rows));
複製代碼
在對象中,get或set方法用於指定屬性的讀取函數和修改函數,讀取或修改屬性時會自動調用這些函數。
var pile = {
elements: ["eggshell","orange peel","worm"],
get height(){
return this.elements.length;
},
set height(value){
console.log("Ignoring attempt to set height to",value"); } } console.log(pile.height); // 3 pile.height = 100; // Ignoring attempt to set height to",value
複製代碼
天天不定時更新~,歡迎批評指正~