Skip to content

JavaScript腳本

小程序的主要開發語言是JavaScript,開發者使用JavaScript來開發業務邏輯以及調用小程序的API來完成業務需求。

ECMAScript

在大部分開發者看來,ECMAScript和JavaScript表達的是同一種含義,但是嚴格的說,兩者的意義是不同的ECMAScript是一種由Ecma國際通過ECMA-262標準化的腳本程序設計語言, JavaScript是ECMAScript的一種實現理解JavaScript是ECMAScript一種實現後,可以幫助開發者理解小程序中的JavaScript同瀏覽器中的JavaScript以及NodeJS中的JavaScript是不相同的。
ECMA-262規定了ECMAScript語言的幾個重要組成部分:
1. 類型
2. 語法
3. 語句
4. 關鍵字
5. 操作符
6. 對象
瀏覽器中JavaScript構成如下圖所示:
瀏覽器中的JavaScript是由ECMAScript和BOM(瀏覽器對象模型)以及DOM(文件對象模型)組成的,Web前端開發者會很熟悉這兩個對象模型,它使得開發者可以去操作瀏覽器的一些表現,比如修改URL、修改頁面呈現、記錄數據等等NodeJS中JavaScript構成如下圖所示:
NodeJS中的JavaScript是由ECMAScript和NPM以及Native模塊組成,NodeJS的開發者會非常熟悉NPM的包管理系統,通過各種拓展包來快速的實現一些功能,同時通過使用一些原生的模塊例如FS、HTTP、OS等等來擁有一些語言本身所不具有的能力。
那麼,同開發者所熟悉的這兩個環境是不同的,小程序中JavaScript構成如下圖所示。
小程序中的JavaScript是由ECMAScript以及小程序框架和小程序API來實現的同瀏覽器中的JavaScript相比沒有BOM以及DOM對象,所以類似JQuery、Zepto這種瀏覽器類別庫是無法在小程序中運行起來的,同樣的缺少Native模塊和NPM包管理的機制,小程序中無法加載原生庫,也無法直接使用大部分的NPM包。

小程序的執行環境

明白了小程序中的JavaScript同瀏覽器以及NodeJS有所不同後,開發者還需要注意到另外一個問題,不同的平臺的小程序的腳本執行環境也是有所區別的
小程序現時可以運行在三大平臺:
  • iOS平臺,包括iOS9、 iOS10、iOS11
  • Android平臺
  • 小程序IDE
這種區別主要是體現三大平臺實現的ECMAScript的標準有所不同截止到當前一共有七個版本的ECMAScript標準,現時開發者大部分使用的是ECMAScript 5和ECMAScript 6的標準,但是在小程序中, iOS9和iOS10所使用的運行環境並沒有完全的相容到ECMAScript 6標準,一些ECMAScript 6中規定的語法和關鍵字是沒有的或者同標準是有所不同的,例如:
  • 箭頭函數
  • let const
  • 模版字串
  • ...
所以一些開發者會發現有些代碼在舊的手機作業系統上出現一些語法錯誤為了幫助開發者解决這類問題,小程序IDE提供語法轉碼工具幫助開發者,將ECMAScript 6代碼轉為ECMAScript 5代碼,從而在所有的環境都能得到很好的執行。

開發者需要在項目設定中,勾選ES6轉ES5開啟此功能。

模組化

瀏覽器中,所有JavaScript是在運行在同一個作用域下的,定義的參數或者方法可以被後續加載的腳本訪問或者改寫同瀏覽器不同,小程序中可以將任何一個JavaScript文件作為一個模塊,通過module.exports或者exports對外暴露介面。

請看是一個簡單模塊示例,B.js引用模塊A,並使用A暴露的multiplyBy2方法完成一個變量乘以2的操作,如下代碼所示:

js
// moduleA.js
module.exports = function( value ){
  return value * 2;
}
js
// B.js

//Reference module A in B.js
var multiplyBy2 = require('./moduleA')
var result = multiplyBy2(4)

下方代碼在需要使用這些模塊的文件中,使用require(path)將公共代碼引入:

js
var common = require('common.js')
Page({
  helloMINA: function() {
    common.sayHello('MINA')
  },
  goodbyeMINA: function() {
    common.sayGoodbye('MINA')
  }
})

腳本的執行順序

瀏覽器中,腳本嚴格按照加載的順序執行,如下代碼所示:

js
<html>
<head>
  <!-- a.js
  console.log('a.js')
   -->
  <script src ="a.js"></script>
  <script>
    console.log('inline script')
  </script>

  <!-- b.js
  console.log('b.js')
   -->
  <script src ="b.js"></script>
</head>
</html>

以上代碼的輸出是:

js
a.js
inline script
b.js

而在小程序中的腳本執行順序有所不同小程序的執行的入口文件是app.js並且會根據其中require的模塊順序决定文件的運行順序,下方代碼是一個app.js示例。

js
/* a.js
console.log('a.js')
*/
var a = require('./a.js')
console.log('app.js')

/* b.js
console.log('b.js')
*/
var b = require('./b.js')

以上代碼的輸出順序是:

js
a.js
app.js
b.js

當app.js執行結束後,小程序會按照開發者在app.json中定義的pages的順序,逐一執行,如下代碼所示:

js
{
  "pages": [
    "pages/index/index",
    "pages/log/log",
    "pages/result/result"
  ],
  "window": {}
}
js
// app.js
console.log('app.js')
js
// pages/index/index
console.log('pages/index/index')
js
// pages/log/log
console.log('pages/log/log')
js
// pages/result/result
console.log('pages/result/result')

以上文件執行後輸出的結果如下:

js
app.js
pages/index/index
pages/log/log
pages/result/result

作用域

同瀏覽器中運行的腳本文件有所不同,小程序的腳本的作用域同NodeJS更為相似。

在文件中聲明的變量和函數只在該文件中有效,不同的文件中可以聲明相同名字的變量和函數,不會互相影響,如下代碼所示:

js
// a.js
//Define local variables
var localValue = 'a'
js
// b.js
// b.js cannot access the variables defined in a.js file:
//Define local variable console.log (localValue) //An error triggered: b.js cannot access the variables defined in a.js

當需要使用全域變量的時,通過使用全域函數getApp()獲取全域的實例,並設定相關屬性值,來達到設定全域變量的目的,如下代碼所示:

js
// a.js
//Access global variable
var global = getApp()
global.globalValue = 'globalValue'
js
// b.js
//Access global variable
var global = getApp()
console.log(global.globalValue) 
// Output globalValue

需要注意的是,上述示例只有在a.js比b.js先執行才有效,當需要保證全域的數據可以在任何文件中安全的被使用到,那麼可以在App()中進行設定,如下代碼所示:

js
// app.js
App({
  globalData: 1
})
js
// a.js
//Local variable
var localValue = 'a'

//Get global variable
var app = getApp()

//Modify global variable
app.globalData++  // The value of globalData is 2 after execution
js
// b.js
//Define other local variables, which will not affect the variables in a.js
var localValue = 'b'

// If a.js is executed first, the value outputted should be 2
console.log(getApp().globalData)