Javascript是動態的,弱類型的,解釋執行的程序設計語言。javascript
Javascript極其靈活,支持多種程序設計範式:面向對象、指令式、函數式。JavaSCript最初被用於瀏覽器腳本,如今已是全部主流瀏覽器的默認腳本語言。瀏覽器腳本的做用包括用戶交互、DOM操做、以及與服務器通訊。Node.js的流行已經將JavaScript從瀏覽器端引入到服務器端,並顯示出卓越的併發性能。java
Javascript 由 Netscape 瀏覽器首次引入到Web文檔中。Javascript的出現使web文檔變得更像動態的App,而不是靜態的文檔。如今主流的瀏覽器都有javascript支持。Javascript核心被標準化爲ECMAScript。web
JavaScript做爲一門通用編程語言(general purpose),其語法、內置對象、以及工具庫須要大量的精力才能掌握,但JavaScript有着直觀的語法,是一門很是容易上手的編程語言。本章中介紹JavaScript最基本的語法,而後編寫一個簡單的Javascript程序。編程
使用你喜歡的文本編輯器,創建一個javascript文件(後綴命名爲.js便可),輸入如下的內容:設計模式
console.log('Hello, world!')
console
爲多數JavaScript的宿主環境都會提供的全局對象,它的log
方法用來輸出(到標準輸出)一個字符串。爲了運行這段程序,能夠打開Chrome瀏覽器的開發者工具(右上角的設置按鈕->更多工具->開發者工具),進入Console頁面。數組
拷貝這段代碼進去,按下回車,你會看到在控制檯的輸出:Hello, world!
。瀏覽器
Javascript 是一門弱類型和動態類型的語言。弱類型意味着容許隱式類型轉換,不一樣類型的變量間能夠直接賦值和運算;動態類型則意味着只在運行時纔會進行類型檢查,因而包含錯誤的文件仍然可以獲得執行,直到運行至包含類型錯誤的語句,JavaScript程序才異常退出。服務器
Javascript中包含5種基本類型(primitive types
):閉包
字符串(String
):其取值爲字符序列。字符串能夠用單引號或雙引號分隔,兩者徹底等價。併發
var str1 = "str1", str2 = '"str1" is a string';
數字(Number
):它是實數?整數?無符號數?這些都不須要考慮!Javascript中,數字就是數字!
var n = 1; n = n/3; // 0.33333...
布爾類型(Boolean
):只有兩種取值:true
, false
。
空(Null
):該類型只有一個值可取,它就是null
。在多數Javascript運行時中,null是一種特殊的object
。
未定義(Undefined
):該類型也只有一個值可取,它就是undefined
。有趣的是,你能夠把一個變量聲明爲未定義:
var foo = undefined;
除了基本數據類型,還有一種對象(object
)類型,Object是JavaScript中全部其餘對象的原型(若是你熟知Java等基於類繼承的語言,能夠理解爲Object是其餘全部對象的公共父類)。日期、數組、函數都是JavaScript內置的特殊對象。ECMAScript將對象定義爲屬性的集合(Collection),建立一個對象的語法就像建立一個集合:
var student = {name: '小明', age: 23};
咱們看到:
全部的變量都是以var
聲明的,若是你忘記了寫var
,那麼這個變量將成爲整個運行時的全局變量。
另外,Javascript與C++使用一樣的註釋風格,即/**/
用於多行註釋,//
用於單行註釋。
Javascript是面向對象的編程語言,對象是javascript中最重要的概念。上一節咱們經過一對大括號建立了一個對象:
var student = {name: '小明', age: 23};
若是你熟悉構造函數,JavaScript也支持這種方式:
function Student(name){ this.name = name; this.age = 23; } var student = new Student('小明'); student.school = 'PKU';
在Javascript中,經過this來訪問本身的屬性。有趣的是,訪問屬性前不需聲明:
若是直接讀取未聲明屬性,會獲得undefined
;
若是直接寫入未聲明屬性,則會聲明並用指定的值初始化該屬性。
若是你熟悉設計模式,你可能會須要這樣的對象建立方式:
function StudentFactory(_school){ var school = _school; this.create = function(name, age){ return { name: name, age : age, school: school } } } var factory = new StudentFactory('PKU'); var student = factory.create('小明', 23);
上述代碼中,首先聲明瞭一個工廠對象(function
是一種特殊的對象),並用school
來配置該工廠。此後用該工廠生產一個學生對象:小明
。
如今,你已經熟悉了Javascript中對象的建立。若是你曾開發過C++或者Java,你可能會關心Javascript中如何進行繼承,以及實現多態。
不錯,Javascript是面嚮對象語言。但沒有類的聲明和實例化機制,Javascript使用原型繼承的方式(prototype
)。看到下面的例子也許就清楚了:
// file: prototype.js var Person = { sayhi : function(){ console.log("hi! I'm", this.name) } }; function Student(name){ this.name = name; this.school = 'pku'; } Student.prototype = Person; var student = new Student('alice'); // hi! I'm alice student.sayhi();
上述代碼中,首先聲明瞭Person
對象,他有一個sayhi
屬性,該屬性的值爲function
類型(還記得嗎?函數是一種特殊的對象)。而後將構造函數Student
的原型設爲Person
,便實現了對象繼承。
prototype
對象是個模板,要實例化的對象都以這個模板爲基礎(屬性和方法都被傳遞給那個類的全部實例)。prototype
是能夠傳遞的,最終造成原型鏈。
JavaScript 最使人感興趣的可能莫過於函數其實是功能完整的對象,經過function
來聲明函數。在JavaScript中的函數是一級公民,這意味着函數和其餘變量同樣,能夠被傳參、賦值、以及返回。函數有以下幾個默認屬性:
1.length
:參數個數
function func(arg1, arg2){} // 2 console.log(func.length)
2.toString
:這是全部對象共有的方法,將會輸出函數的源代碼
// function func(arg1, arg2){} console.log(func.toString())
在函數體中,JavaScript提供了一個特殊對象 arguments
,它是當前函數被調用時傳入的參數數組,經過訪問arguments
不只能夠訪問全部實參,還能夠得到實參的數目,從而實現其餘編程語言中可變參數的機制:
/ file: arguments.js function sayHi(){ if(arguments.length == 1) console.log("I'm", arguments[0]); if(arguments.length == 2) console.log("I'm", arguments[0], 'aged', arguments[1]); } // I'm alice sayHi('alice'); // I'm alice aged 23 sayHi('alice', 23);
Javascript並未提供函數重載、默認參數機制。但咱們能夠直接訪問
arguments
參數列表,sayHi
其實模仿了函數重載。
call
和apply
是函數對象的兩個函數屬性,通常用於對象冒充、包裝或者代理。可能在教學中不會用到太多,但在JS庫的開發中會常常用到。這兩個函數的功能均爲用一個對象去調用一個函數,而該函數並不是該對象的屬性,例如:
// file: call.js function sayHi(age){ console.log("I'm", this.name, 'aged', age) } var student = { name: 'alice' }; // I'm alice aged 23 sayHi.call(student, 23)
call的第一個參數是用做this
的對象。其餘參數都直接傳遞給函數自身。既然如此,若是咱們但願實現一個本身的log
函數,來包裝console.log
:
// file: log.js function log(){ var str = ''; for(var i = 0; i < arguments.length; i++){ str += ' ' + arguments[i] } console.log(str); } // a b c log('a', 'b', 'c');
這裏咱們不能使用call
函數,由於咱們不知道參數個數。這即是apply
的用武之地:
// file: log.js function log(){ console.log.apply(this, arguments) } // a b c log('a', 'b', 'c');
apply
接受的第二個參數爲參數數組,而非call
的參數列表。這便爲上述的狀況提供了便利。
因Javascript最初運行於瀏覽器端,與服務器的通訊必然要異步執行(不然,將會阻塞主控制流,此時瀏覽器會不響應用戶操做)。因而,Javascript天生就是異步的,Node.js
也是採用異步事件而大幅提升I/O密集型任務的效率。
異步是計算機在執行任務的過程當中,某些任務能夠獨立於主控制流執行,使得主控制流得以繼續執行,是一種非阻塞的控制方案。
在Javascript中有多種異步的實現方案,它們無一例外地須要回調函數。回調函數就是將函數做爲參數傳遞到其餘代碼(例如:某一個異步任務),這一設計容許了該異步任務完成時執行傳入的函數。例如:
// file: async.js function taskFinished(){ console.log('Task finished!') } setTimeout(taskFinished, 3000);
上述代碼中,首先定義了一個任務完成時須要執行的函數taskFinished
。setTimeout
是Javascript提供的一個全局對象(還記得嗎?函數是一種特殊的對象),它是一個計時器,在到達指定的時間後調用某個函數。上述例子中,在5000
毫秒後運行taskFinished
函數。
Javascript的變量做用域不一樣於其餘的主流編程語言。包括變量的定義方式、做用域的劃分、做用持續時間等。
在javascript中使用賦值語句便可定義一個對象,而不須要對象聲明(也不須要指定變量的類型)。使用var
能夠定義局部變量,而省略var
則能夠定義全局變量。例如:
// 定義局部變量 foo var foo = 'bar' // 定義全局變量 bar bar = 'foo
上述的全局變量做用域不是當前對象,也不是當前文件,而是整個運行時進程!實際項目中,應儘可能避免引入全局變量。衆所周知,全局變量會使得代碼高度耦合、難以複用和維護、複雜化團隊協做。
那麼,局部變量的做用域是怎樣的呢?局部做用域即當前函數,不一樣於C++或Java的當前代碼塊(以大括號分隔)。正由於javascript的這一點特殊性質,引出了javascript中的一個重要概念:閉包(closure)。閉包是指是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即便已經離開了它最初被建立的環境。
閉包之因此如此重要,是由於它完成了javascript的諸多面向對象特性:數據封裝、運行時多態。咱們看一個閉包:
// file: closure.js function Count(){ var num=0; this.add = function(){ console.log(num++); } } var c = new Count(); c.add(); c.add();
Count
是函數,而函數是對象,對象能夠有屬性和方法。add
是方法屬性(類型爲方法的屬性),而num
則只是函數的局部變量。
首先,咱們定義一個構造函數Count
,並在其中定義一個局部變量num
。接着,咱們調用該構造函數建立一個對象c,並調用兩次它的add
方法。不出所料,程序的輸出將是:
0 1
你可能已經注意到:第二次調用add
方法時,add
中引用的num
仍然保持着上次調用後的值。事實上,num
是被add
方法引用的一個自由變量,其做用域會一直跟隨add
而存在。
不要混淆局部變量和對象屬性。這裏的num
是局部變量,其做用域仍然存在是閉包現象,而非對象屬性。對象屬性須要用this
關鍵字來定義。
若是你但願進一步學習Javascript,Mozilla Developer和W3C School: Javascript教程都提供了很好文檔。若是你願意深究Javascript語法規則,請參考ECMAScript標準:ECMAScript-262。
本文由Harttle創做,轉載需署名做者且註明文章出處