【JS 口袋書】第 1 和 2 章:JS簡介及基礎

做者:valentinogagliardi
譯者:前端小智
來源:github

阿里雲最近在作活動,低至2折,有興趣能夠看看:
https://promotion.aliyun.com/...html


爲了保證的可讀性,本文采用意譯而非直譯。前端

第1章:JS 簡介

什麼是JavaScript

JS 是一種用於 web 的腳本語言。JS 誕生於 1995 年,由 Brendan Eich 一手建立,用於向web頁面添加交互性。那時的互聯網還處於起步階段,咱們今天看到的大多數花哨的網頁在那時候還只是一個夢。git

在項目經理的催促下,Brendan 只有 10 天的時間來建立一種能夠在瀏覽器中運行的動態、靈活的語言。他寫出了 JavaScript,一種有點奇怪的編程語言,它參考了 Java、C 和 Scheme。JS 一直名聲很差,由於它從一開始就有不少怪異的地方。但儘管如此,它仍是在名人堂佔據了一席之地,並一直挺到了今天。github

如今,JS 被用來建立整個應用程序,稱爲SPA(單頁應用程序)。隨着使用量的增長,JS 生態系統也經歷了寒武紀大爆發。我們今天用於開發 JS 的大多數 Web 工具和庫,不少用 JS 寫的。JS 也被用除前端方面的領域。使用 Node.js 我們能夠建立服務器端和物聯網應用程序,工業設備,以及更多。但最重要的是,單頁應用程序是 JS 最突出的用法之一。web

在單頁面應用中,JS 負責全部的事情,使 UI 流暢,無需任何頁面刷新。從用戶的角度來看,這是對傳統 web 應用程序的巨大改進。可是,能力越大,責任越大: JS 對於大多數移動設備來講是一個沉重的負擔,在設計和構建時應該格外當心。、編程

爲何要學 JavaScript

今天學習 JS 並不意味着對變量和函數的膚淺理解:還有不少。JS 開發人員知道閉包、thisnew、原型系統和更新的特性。JS 一年比一年流行,功能也逐漸完善。如今幾乎每一個前端開發人員的工做都須要 JS 知識。招聘經理尋找的不是會使用 JQ (說到jQuery,它彷佛正在慢慢消亡) 的。segmentfault

大多數狀況下,你也須要解及學習 TypeScript, 強調類型的 JS。前端開發人員應該要理解 JS 的特性,並可以編寫慣用的、結構良好的 JS 代碼。JS 正在迅速傳播,即便你不喜歡這種語言,在這一點上忽視它也可能對你的職業生涯不利。數組

第2章:JS 基礎

JS 目前有 7 種基本類型,以下:瀏覽器

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Object
  • Symbol(ES6)

除了 Object 是複雜數據類型外,其它的 6 種是 JS 的基本數據類型。每一個 JS 類型都有一個對應的表示,能夠在我們的代碼中使用,好比字符串:服務器

var string = "Hello John";

數字:

var age = 33;

說到這裏,JS 也有算術運算:

運算符 運算名
+ 加法
++ 自增
* 乘法
** 指數
-
-- 自減
/
% 取除

在 JS 中,可使用 var 關鍵字將值存儲在變量中,這是聲明變量的最兼容方法:

var greet = "Hello";
var year = 89;
var not = false;

這裏說的兼容,是由於在 ES6 中咱們還有兩個選擇: letconst。舊的瀏覽器可能不支持這些新的關鍵字,除非使用「轉置器」,不然可能會遇到錯誤。在新的瀏覽器中,建議都 letconst 。主要有兩個好處:

  • letconst 都有本身的塊做用域
  • const 不能從新分配,也不能從新聲明

塊做用域是指用 letconst 聲明的變量與在封閉或外部中聲明的相同變量名不重疊。例如:

let name = "前端小智";

{
  let name = "王大冶";
  console.log(name); // "王大冶"
}

console.log(name); // "前端小智"

這裏的 name 彷佛是重複的,但其實是兩個不一樣的變量在本身的做用域裏。const 具備相同的行爲:

const name = "前端小智";

{
  const name = "王大冶";
  console.log(name); // "王大冶"
}

console.log(name); // "前端小智"

var 的行爲就與 letconst 不同了。

var name = "前端小智";

{
  var name = "王大冶";
  console.log(name); // "王大冶"
}

console.log(name); // "王大冶"

正如前端所說,const 不能被從新分配,也不能在同一個做用域中從新聲明。若是你嘗試從新聲明一個 const,會獲得 "SyntaxError: Identifier has already been declared"。若是將某個值從新賦值給同一個 const,會獲得 "TypeError: Assignment to constant variable" 錯誤。

const name = "前端小智";
const name = "王大冶";

// SyntaxError: Identifier 'name' has already been declared

下面代碼也會拋出錯誤:

const name = "前端小智";
name = "王大冶";

// TypeError: Assignment to constant variable.

可是,請注意,這裏所說的 「cons 不能從新分配,也不能從新聲明」 時,並不意味着const 是不可變的。

這是初學者都會遇到的問題。事實上,任何稍微複雜一點的 JS 數據結構,如數組或對象,即便在分配給 const 時,它們的值或者屬性值是可變的,不可變是指這些複雜對象的內存地址。

const person = {
  name: "前端小智",
  age: 21
};

person.name = "王大冶";

console.log(person);

// {name: "王大冶", age: 21}

const 對象中的不可變是指什麼? 下面是數組:

const list = [1, 1, 3, 2, 5];

list.shift();

console.log(list); // [ 1, 3, 2, 5 ]

一樣,不是不可變。 有人說 「const 是不可變」 時,請給他看這些例子。 如今回到基礎。 除了獨立變量以外,還可使用字面量的方式聲明數組:

var array = ["Hello", 89, false, true];

0 開始的索引能夠訪問數組元素:

var array = ["Hello", 89, false, true];
var first = array[0]; // "Hello"

幾乎全部 JS 實體都附加了一些函數,稱爲方法。舉兩個例子,數組有不少處理自身的方法

var array = ["Hello", 89, false, true];

array.push(99);
array.shift();

console.log(array); // [ 89, false, true, 99 ];

對於字符串也是同樣的:

var string = "John";
console.log(string.toUpperCase()); // JOHN

在第 5 章中,你會知道這些方法從何而來,但這裏有一個提示:它們分別在 Array.prototype 和 String.prototype 上定義。除了方法以外,還有一些屬性對於提取關於字符串長度的信息很是有用:

var string = "John";
console.log(string.length); // 4

或者數組的長度:

var array = ["Hello", 89, false, true];

array.push(99);
array.shift();

console.log(array.length); // 4

這些屬性有些特殊,由於它們被稱爲 "getters"/"setters"。 你能夠想象一個給定的字符串就像一個附加了一堆方法和屬性的對象。當訪問數組的長度時,你只需調用相應的 gettersetter 函數用於設置操做:

var array = {
  value: ["Hello", 89, false, true],
  push: function(element) {
    //
  },
  shift: function() {
    //
  },
  get length() {
    // gets the length
  },
  set length(newLen) {
    // sets the length
  }
};

// Getter call
var len = array.length

// Setter call
array.length = 50;

如今,我們已經奠基了基礎,讓咱們仔細看看對象,它是最重要的 JS 類型之一。

站在一個對象的肩膀上

Object 是 JS 中最重要的類型,所以幾乎全部其餘實體均可以從中派生。 例如,函數和數組是專用對象。 JS 中的對象是鍵/值對的容器,如如下示例(字面量形式):

var obj = {
  name: "John",
  age: 33
};

還有另外一種建立對象的方法,但它不多見,性能低,請避免使用這種形式:

var obj = new Object({
  name: "John",
  age: 33
});

正如你所看到的,對象是保存值的一種方便方法,稍後能夠經過訪問相應的屬性來檢索這些值:

var obj = {
  name: "前端小智",
  age: 26
};

console.log(obj.name); // "前端小智"

我們還能夠添加新屬性、刪除或更改它們

var obj = {
  name: "前端小智",
  age: 26
};

obj.address = "王大冶";
delete obj.name;
obj.age = 18;

對象的鍵也能夠是字符串,在本例中,咱們使用方括號符號訪問屬性:

var obj = {
  name: "前端小智",
  age: 26,
  "complex key": "stuff"
};

console.log(obj["complex key"]); // "stuff"

可是,表示法更常見,除非鍵是複雜的字符串,不然應該選擇傳統的屬性訪問:

var obj = {
  name: "前端小智",
  age: 26
};

console.log(obj.name); // "前端小智"

這是我們全部須要知道的基本知識,但在 第5章,咱們將看到 JS 對象是很是強大的,能夠作更多。如今來看看 JS 函數。

5 種不一樣的 JS 函數

幾乎每種編程語言都有函數,JS 也不例外。函數是可重用的代碼段。考慮如下示例

function hello(message) {
  console.log(message);
}

hello("Hello");

function sum(a, b) {
  return a + b;
}

var sum = sum(2, 6);

第一個函數打印一個字符串,第二個函數向外部世界返回一個值。正如你所看到的,函數能夠接受參數,列在函數「簽名」中:

// a 和 b 是函數簽名中的參數
function sum(a, b) {
  return a + b;
}

我們能夠在調用函數時傳遞值:

// a and b are parameters in the function's signature
function sum(a, b) {
  return a + b;
}

// 2 和 6 是該函數的參數
var sum = sum(2, 6);

function 關鍵字聲明的 JS 函數是常規函數,與沒有主體的肩頭函數相反常規函數能夠呈現多種形式:

  • 命名函數
  • 匿名函數
  • 對象方法
  • 對象方法簡寫(ES 6)
  • IIFE(當即執行函數)

命名函數是最傳統的函數類型:

function sum(a, b) {
  return a + b;
}

另外一方面,匿名函數沒有名稱,能夠分配給一個變量供之後使用

var sum = function(a, b) {
  return a + b;
};

或者用做其餘函數中的回調:

var button = document.createElement("button");

button.addEventListener("click", function(event) {
  // do stuff
});

函數也能夠存在於對象中,這種稱爲該對象的方法

var widget = {
  showModal: function() {
    // do stuff
  }
};

widget.showModal();

常規函數在默認狀況下也會獲得一個 this 關鍵字,它能夠根據調用函數的方式賦予不一樣的含義。在第六章中,咱們將詳細探討這個主題。如今有一個簡單的規則:在一個對象內部運行的函數有 this 指向包含對象的指針

var widget = {
  html: "<div></div>",
  showModal: function() {
    console.log(this.html);
  }
};

widget.showModal(); // "<div></div>"

在 ES6 中,你也可使用對象方法簡寫:

var widget = {
  showModal() {
    // object method shortand
  }
};

widget.showModal();

最後,IIFE (當即執行的函數):

var IIFE = (function() {
  // what happens in an IIFE stays in the IIFE
})();

語法可能看起來有點奇怪,可是 IIFE 很是強大,在第4章會看到它們。除了常規函數外,還有箭頭函數,在 ES6 中添加。箭頭函數不使用 function 關鍵字,但它們具備類似的形式:

  • 命名箭頭函數
  • 匿名箭頭函數
  • 對象方法
  • IIFE 箭頭函數

箭頭函數很方便,但我建議不要過分使用它們。這是一個命名的箭頭函數。若是沒有參數,能夠省略 return 語句並使用圓括號

const arrow = () => console.log("Silly me");

若是你須要在箭頭函數中計算一些東西或者調用其餘函數,能夠用花括號包含一個主體

const arrow = () => {
  const a = callMe();
  const b = callYou();
  return a + b;
};

花括號也是定義對象的字面量形式,這並不意味着我們能夠作相似的事情:

const arrow = () => {
  a : "hello", 
  b: "world"
};

這是無效的語法。要從箭頭函數返回對象,可使用圓括號:

const arrow = () => ({
  a: "hello",
  b: "world"
});

console.log(arrow());
// { a: 'hello', b: 'world' }

或者使用 return 語句:

const arrow = () => {
  return {
    a: "hello",
    b: "world"
  };
};

與常規匿名函數同樣,也有匿名箭頭函數。這裏有一個做爲回調傳遞給另外一個函數

const arr = [1, 2, 3];

const res = arr.map(element => element + 1);

console.log(res); // [ 2, 3, 4 ]

它以 element 爲參數,併爲每一個數組元素返回 element +1。 如你所見,若是箭頭函數只有一個參數,則無需在其周圍加上括號:

const fun = singleParameter => singleParameter + 1;

但若是你須要更多的參數,括號是必需的:

const fun = (a, b) => a + b + 1;

箭頭函數也能夠做爲對象方法出現,可是它們的行爲與常規函數不一樣。在前一段介紹了 this 關鍵字,它是對運行函數的對象的引用。看成爲對象方法調用時,常規函數將 this 指向宿主對象

var widget = {
  html: "<div></div>",
  showModal: function() {
    console.log(this.html);
  }
};

widget.showModal(); // "<div></div>"

而箭頭函數中的 this 則指向徹底不一樣的東西:

var widget = {
  html: "<div></div>",
  showModal: () => console.log(this.html)
};

widget.showModal(); // undefined

所以,箭頭函數不太適合做爲對象方法,可是有一些有趣的用例,在本小冊中,我們將瞭解爲何以及什麼時候有效使用它們。 最後,來看一下 IIFE 箭頭函數:

(() => {
  console.log("aa");
})();

使人困惑的語法不是嗎? 接着我們將進入下一章。

傳遞參數

ECMAScript 中全部函數的參數都是按值傳遞的。也就是說,把函數外部的值複製給函數內部的參數,就和把值從一個變量複製到另外一個變量同樣。基本類型值的傳遞如同基本類型變量的複製同樣,而引用類型值的傳遞,則如同引用類型變量的複製同樣。有很多開發者在這一點上可能會感到困惑,由於訪問變量有按值和按引用兩種方式,而參數只能按值傳遞。

在向參數傳遞基本類型時,被傳遞的值會被複制給一個局部變量(即命名參數,或者用 ECMAScript 的概念來講,就是 arguments 對象中的一個元素)。在向參數傳遞引用類型的值時,會把這個值在內存中的地址複製給一個局部變量,所以這個局部變量的變化會反映在函數的外部。請看下面的例子:

function addTen(){
  num += 10;
  return num
}

var count = 20;
var result = addTen(count);
alert(count); // 20 沒有變化
alert(result); // 30

這裏的函數 addTen () 有一個參數 num ,而參數其實是函數的局部變量。在調用這個函數時,變量 count 做爲參數被傳遞給函數,這個變量的值是 20。因而,數值 20 被複制給參數 num 以便在 addTen() 中使用。 在函數內部,參數 num 的值被加上了 10,但這一變化不會影響函數外部的 count 變量。參數的值也將變成 30,從而反映函數內部的修改。固然,使用數值等基本類型值來講明按值傳遞參數比較簡單,但若是使用對象,那問題就不怎麼好理解了。再舉一個例子:

function setName (obj) {
  obj.name = '前端小智';
}

var person = new Object();
setName(person);
alert(person.name) // "前端小智"

以上代碼建立一個對象,並將其保存在了變量 person 中。而後,這個變量被傳遞到 setName() 函數中以後就被複制給了 obj。在這個函數內部, obj 和 person引用的是同一個對象。因而,當在函數內部爲 obj 添加 name 屬性後,函數外部的 person 也將有所反映;由於person指向的對象在堆內存中只有一個,並且是全局對象。

有不少開發者錯誤的認爲:在局部做用域中修改的對象會在全局做用域中反映出來,就說明參數是按引用傳遞。爲了證實對象是按值傳遞的,咱們再看一看下面這個通過修改的例子:

function setName(obj) {
  obj.name = '前端小智';
  obj = new Object();
  obj.name = '王大冶'
}
var person = new Object();
setName(person);
alert(person.name) // '前端小智'

這個例子與前一個例子的惟一區別,就是在 setName() 函數中添加了兩行代碼:一行代碼爲 obj 從新定義了一個對象,另外一行代碼爲該對象定義了一個帶有不一樣值的 name 屬性。在把 person 傳遞給 setName() 後,其 name 屬性被設置爲 ‘前端小智’。而後,又將一個新對象賦給變量 obj,同時將其 name 屬性設置爲 '王大冶'

若是 person 是按引用傳遞的,那麼 person 就會自動被修改成指向其 name 屬性爲 ‘王大冶'的新對象。可是原始的引用仍然保持不變。實際上,當在函數內部重寫 obj 時,這個變量引用就是一個局部對象了。而這個局部對象會在函數執行完畢後當即被銷燬。

總結

JS 具備七個稱爲 「類型」 的基本構建塊,其中 6 個也稱爲基本數據類型。 Object 自己就是一種類型,也是該語言最重要的實體。 對象是用於一對鍵/值的容器,而且能夠包含幾乎全部其餘 JS 的類型,包括函數。

與大多數其餘編程語言同樣,JS 有字符串、數字、函數、布爾值和一些稱爲 NullUndefined 的特殊類型。JS 中有兩種函數:箭頭函數和常規函數。它們都有各自的用法,根據場景使用它們。

思考

  • arguments 和 參數 之間有什麼區別?
  • JS 中有多少個基本類型
  • 什麼是常規的箭頭函數
  • 函數做爲對象方法運行時能夠訪問哪些特殊關鍵字?
  • 在 JS 中聲明變量有多少種方法

代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具Fundebug

原文:

https://github.com/valentinog...

https://github.com/valentinog...

交流

阿里雲最近在作活動,低至2折,有興趣能夠看看:https://promotion.aliyun.com/...

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

https://github.com/qq449245884/xiaozhi

由於篇幅的限制,今天的分享只到這裏。若是你們想了解更多的內容的話,能夠去掃一掃每篇文章最下面的二維碼,而後關注我們的微信公衆號,瞭解更多的資訊和有價值的內容。

clipboard.png

每次整理文章,通常都到2點才睡覺,一週4次左右,挺苦的,還望支持,給點鼓勵

相關文章
相關標籤/搜索