前端教學講義:JS基礎

講義內容:JS 誕生的背景、基本類型、運算符java

如下內容只涉及 ES5 標準,ES6 增長的新內容能夠在網上查找到。node

JS 誕生的背景

上世紀 90 年代網景公司開發的瀏覽器獨步天下typescript

一個叫作 Brendan Eich 的工程師受命於開發一款腳本語言,來加強瀏覽器的功能。編程

這名工程師花費了 10 天時間設計出了第一個版本,名叫 LiveScript。數組

後來由於當時 Java 正紅,公司將其更名爲 JavaScript,因此 JS 和 Java 其實沒有什麼關係。瀏覽器

這名工程師在設計語言之初有幾個目標閉包

  • 簡單易用,不須要專業的計算機知識就能使用
  • 由於我的的喜好,因此增長對函數式編程的支持
  • 由於公司壓力,因此也要支持面向對象

後來 JS 由於市場認同,戰勝了 VBScript,Java plugin 等一系列加強瀏覽器的方案,成了事實標準。函數式編程

網景公司倒閉以後,JS 的標準制定由 ECMA(歐洲電腦製造商協會)制定,因此又叫作 ECMAScript, 新的標準叫作 ES六、ES七、ES2018 … 每一年會收集各類語言規範建議,經過 草案、stage一、stage2 等幾個階段,並在瀏覽器中有實現後會成爲正式標準。函數

因此 JS 具備如下特色:this

  • 向下兼容,包括兼容 bug(typeof null,大數)
  • 奇怪、蹩腳的規範,從技術上來講缺點不少
  • 運用場景普遍
  • 迭代速度快,

會在項目裏使用還在草案階段的新語法

變種、超集、子集 不少(coffee script, typescript, Action Script, flow, AssembleScript)

基本類型

JS 中的基本類型有如下幾種:

  • undefined
  • null
  • Boolean
  • Number
  • String

另外有三種引用類型:

  • Function
  • Array
  • Object

和 Java 同樣,全部類型都是基於 Object(除了 undefined ?)

經過 「var」關鍵字聲明一個變量,方法和大多數語言同樣。

JS 是動態語言,變量不用區分類型,不一樣類型之間的變量能夠相互賦值。

var foo = 1;
var bar;
var a, b, c;
bar = foo = 「abcd」
undefined 和 null

當一個值沒有初始值的時候,默認值爲 undefined。

undefined 在 JS 中是個語言設計的缺陷例子。

undefined 的行爲在大多數狀況下和 null 同樣,都是不可訪問的值,可是在 typeof 的時候結果不一樣

var und;
var nl = null;
typeof und === ’undefined’
typeof nl == ‘object’

數字

Boolean, Number,String 的行爲和大多數語言同樣。

能夠直接聲明也能夠經過構造函數、工廠模式聲明,三者沒什麼區別。

var a = 123
var a = new Number(123)
var a = Number(123)

在 JS 中 數字的取值範圍是 -253 - 1 到 253 - 1,範圍小於 long 型,而且計算時有精度問題

1.2 / 0.2
// 5.999999999999999

聲明數字的方式和其它語言相似,科學計數法、八進制、十六進制

var a = 1e3
// 1000
var a = 0o12
// 10
var a = 0xa
// 10

經過 「toString」方法能夠轉換成字符串,並設置進制

var a = 1000
a.toString(10)
// 「1000」
a.toString(8)
// 「1750」
a.toString(16)
// 「3e8」

在 JS 中,有兩個特殊的數字,NaN 和 Infinity。

當字符串或者其餘類型轉換成數字失敗的時候,就會返回 NaN,可是 NaN 的類型依然是 number。

NaN 和任何數字計算後,結果都是 NaN。

當數字除以 0 的時候,就會返回 Infinity。

Number(undefined)
// NaN
Number(null) // 我也很費解,爲何 null 轉換成數字就是 0
// 0 
Number("asdf")
// NaN
10 / 0
//Infinity

字符串

在 JS 中字符串是可變長,並且能夠經過 「+」 操做符作拼接。

字符串在內容中應該是雙字節儲存,能夠經過索引訪問每一個字符,而不是每一個 byte。

在瀏覽器中編碼格式根據網頁的編碼格式而設置(在 node 中默認是 UTF8?)

在 JS 中,聲明一個字符串經過單引號或者雙引號均可以,兩者沒有區別。一般習慣是經過單引號訪問,由於能夠少按一個 shift。

var a = ‘123’
var b = 「123」
a == b
a[0]  // 經過索引訪問的結果仍然是個字符串
// 「1」

函數

函數在 JS 中是一等公民,能夠賦值給變量,有兩種聲明方式

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

第二種方法實際上是將一個「匿名函數」複製給了變量 plus。

這兩種方式在使用上沒太大區別,除了在「聲明前置」的場景下會有點區別

匿名函數還有一種方法叫作「當即執行函數」。

(function(a, b){ return a + b })(10, 20)
// 30

函數只會將 return 聲明的表達式的值返回出去,在函數沒有聲明 return 的時候,默認返回 undefined

function plus (a, b) { a + b }
plus(1, 2)
// undefined

函數和做用域
函數和變量同樣,能夠在任何地方聲明、使用,

每一個函數獨立出了一個做用域,做用域能夠互相嵌套。

函數做爲一個變量,自己也在父做用域下

function foo(b) {
    var a = 10
    // 這裏屬於 foo 的做用域,內部有 bar, b, a 三個變量
    function bar() {
        var c = 20;
        return a + b + c // 這裏屬於 bar 的做用域,由於在 foo 以內,因此能夠訪問 a 和 b
    }
    // 這裏不能訪問 c
    return bar();
}

因爲 JS 在設計的時候,怕用戶不理解變量須要先聲明再使用,因此對變量會有「聲明前置」,幫助你們提早聲明所須要的變量。

下面咱們用當即執行函數建立一個閉包來解釋什麼叫「聲明前置」

(function() {
    console.log(fn, foo);
    function fn(a, b){ return a + b };
    var foo = 1234;
    console.log(fn, foo);
})()
// function (a, b){ return a + b } undefined
// function (a, b){ return a + b } 1234

上面的代碼至關於:

(function() {
    function fn(a, b){ return a + b };
    var foo;
    console.log(fn, foo);•
    foo = 1234;
    console.log(fn, foo);
})()

若是咱們用匿名函數賦值給一個變量,那麼會有下面效果:

(function() {
    console.log(fn, foo);
    var fn = function(a, b){ return a + b };
    var foo = 1234;
    console.log(fn, foo);
})()

// undefined undefined
// function (a, b){ return a + b } 1234

上面的代碼至關於:

(function() {
    var fn, foo;
    console.log(fn, foo);
    fn = function(a, b) { return a + b};
    foo = 1234;
    console.log(fn, foo);
})()

數組

在 JS 中數組是可變的,並且數組內能夠聽任何值。

數組的長度是最後一個元素的索引加一。

若是訪問沒有設置的元素,將會返回 unfined

var array = [];
array[0] = 1;
array[2] = 「1234」;
array[4] = function(){};
// [1, undefined, 「1234」, function(){}]
array[1]
// undefined

js 數組中自帶了不少方法,方便咱們對數組作操做,好比說 map, reduce 等等

[1, 2, 3].map(function(d) { return d + 1 })
// [2, 3, 4]
[1, 2, 3].reduce(function(r, d) { return r + d }, 0)
// 6

若是想要刪除數組中的某個元素的話,咱們能夠用 delete 關鍵詞,或者直接將索引的位置設置成 undefined

var array = [1, 2, 3]
delete array[1]
// [1, undefined, 3]

若是咱們須要刪除數組中某個元素,而且縮短數組長度的話,就須要用到 splice。

var array = [1, 2, 3]
array.splice(1, 1)
console.log(array)
// [1, 3]

一些經常使用的數組方法還有 slice, forEach, find, every, includes 等等

對象

和 java 同樣,對象(Object) 是全部類型的基類。它相似 JSON 裏的鍵值對結構體,能夠動態的掛載任何屬性。

聲明的方法有兩種,{} 和 new

var a = {}
var b = new Object()
a.num = 1
a.str = "1234"
console.log(a)
// { num: 1, str: "1234"}

對象(Object)有點相似 Java 裏的 HashMap,但 Key 只能是字符串。能夠經過相似數組索引的方式來訪問對象上的鍵。

這種方法和點操做符是同樣的效果,當鍵名由一些特殊的字符組成的時候,咱們能夠經過索引方式訪問。

var obj = {value: 1234}
console.log(obj["value"])
// 1234

這樣聲明或者訪問會報錯:

var obj = { some-value: 1234 }
obj.some-value

咱們須要讓編譯器知道 some-value 是一個鍵名,因此須要將它們用引號括起來

var obj = { "some-value": 1234 }
obj["some-value"]

當一個對象中掛載了一個函數,在函數中能夠經過 this 來訪問對象內的其餘屬性,this 也能夠理解爲「執行上下文」

function fn () { console.log(this.value) }
var obj = { fn: fn, value: 1234 }
obj.fn() // 此時 fn 的執行上下文就是 obj
// 1234

var otherObj = { fn: fn, value: "abcd"}
otherObj.fn() // 此時 fn 的執行上下文就是 otherObj
// "abcd"

因爲函數和數組是全部類型的基類,因此能夠像對象同樣隨意的擴展屬性。

可是字面量(如數字、布爾值、字符串、null、undefined) 是不能隨意擴展的。

function plus(a, b) { return a + b}
plus.value = 1234
plus(12, plus.value)
// 1246
 
var array = []
array.value = 1234
console.log(array.value)
// 1234
 
var a = true;
a.value = 1234
console.log(a.value)
// undefined

運算符

JS 的運算符和其餘語言基本相似,就+-*%/這些,外加一些二進制操做符。

但由於 JS 弱類型的特性,有些場景是其餘語言沒有的,好比一個數字加一個字符串,結果是什麼?

1+"2"
// "12"

這種場景還能夠理解,數字和字符串相加的時候,數字經過調用 toString 被轉換成了字符串。

1 + {}
// "1[object Object]"

數字和對象相加的時候,對象和數字都調用了 toString,被轉換成了字符串

{} + []
// 0

可是空對象和空數組相加結果倒是 0,我也不知道爲何

var obj = {}
if (obj) console.log("obj is true")
// "obj is true"

obj == true
// false

上面這個例子中,obj 是個空對象,在 if 條件判斷中被看成真值,可是和 true 對比的時候卻返回 false。

JS 的隱式轉換規則很詭異,不少都是爲了兼容早期版本中錯誤的實現,這裏不必細究轉換規則是什麼。

咱們只須要記住之後用嚴格相等符(===)做對比,避免不一樣類型的變量相互運算。

"1" == 1
// true

"1" === 1
// false

循環

JS 的循環和其它相似,也都是 for, do..while 這種寫法。

可是,咱們通常不用 for 做循環,而是用數組方法 forEach,map(由於 for 寫起來很麻煩,並且還須要單獨聲明一個變量)

[1, 2, 3].forEach(function(value) {
  return value + 1;
})
// [2, 3, 4]

另外還能夠經過 for...in 來遍歷一個對象

var obj = {a:1, b:2, c: 3}
for (var key in obj) {
  console.log(key)
}
// "a"
// "b"
// "c"

但咱們通常也不這麼用,而是用 Object.keys 取得包含對象全部鍵的數組

var obj = {a:1, b:2, c: 3}
Object.keys(obj)
// ["a", "b", "c"]
 
Object.keys(obj).forEach(console.log)
// "a"
// "b"
// "c"

課後習題

第一題:JS 的運用領域都有哪些,做爲弱類型的語言執行效率如何,爲何?

第二題:typeof 關鍵字的返回值有幾種結果?

第三題:下面兩種寫法有什麼區別

{
  0: "a",
  1: "b",
  2: "c"
}

["a","b","c"]

第四題:下面代碼的運行時會不會報錯,this 指向哪裏?

function fn () { console.log(this.value) }
fn()

第五題:聲明的變量是如何被回收的

相關文章
相關標籤/搜索