整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript基礎語法(一)

          類有非常多的語言,例如中文、英語、法語等。實際上,計算機也有很多語言,例如C、C++、Java等。簡單來說,JavaScript就是眾多計算機語言(也叫編程語言)中的一種。跟人類語言類似,計算機語言也有一些共性,例如我們可以將用C語言寫的代碼轉化為JavaScript代碼,這就像將英語翻譯成中文一樣,雖然語言不一樣了,但是表達出來的意思是一樣的。

          當我們把JavaScript學完,再去學另外一門語言(如C、Java等),就會變得非常容易。因為兩門計算機語言之間,是有非常多共性的。因此,認真把JavaScript學了,以后再想去學其他編程語言就會變得非常輕松,何樂而不為呢?

          我們都知道,學習任何一門人類語言,都得學這門語言的詞匯、語法、結構等。同樣的,想要學習一門編程語言,也需要學習類似的東西。只不過呢,這些在編程語言中不是叫詞匯、語法、結構,而是叫變量、表達式、運算符等。

          在本文中我們主要學習JavaScript以下三個基礎語法。

          • 常量與變量
          • 數據類型
          • 運算符

          常量與變量

          變量

          在JavaScript中,每一條語句都是以英文分號(;)作為結束符。每一條語句都有它特定的功能,跟英語每一句話都有它表達的意思是一樣的道理。在JavaScript中,變量與常量就像是英語中的詞匯。

          使用變量,首先得進行命名;一般名字不會改變但是會改變值。

          在JavaScript中,給一個變量命名,我們需要遵循以下兩個方面:

          • 變量由字母、下劃線、$或數字組成,并且第一個字母必須是“字母、下劃線或$”
          • 變量不能是系統關鍵字和保留字

          在JavaScript中,如果想要使用一個變量,我們一般需要進行兩步:

          • 變量的聲明
          • 變量的賦值

          所有JavaScript變量都是由var聲明。在這一點上,JavaScript跟C、Java這些是不同的。

          常量

          在JavaScript中,常量指的是一個不能改變的量。也就是說,常量的值從定義開始就是固定的,一直到程序結束都不會改變。

          常量,形象地說,就像千百年來約定俗成的名稱,這個名稱是定下來的,不能隨便改變。在JavaScript中,我們可以把常量看成是一種特殊的變量,之所以特殊,是因為它的值是不會變的。一般情況下,常量名全部大寫,別人一看就知道這個值很特殊,有特殊用途,如:

          var DEBUG = 1;

          我們都知道,程序是會變化的,因此變量比常量有用得多。常量在JavaScript中用得比較少。我們簡單了解常量是這么一回事就行了,不需要做過多的深入了解。

          數據類型

          數據類型可以分為兩種,一種是“基本數據類型”,另外一種是“引用數據類型”。其中,基本數據類型只有一個值,而引用數據類型可以含有多個值。

          在JavaScript中,基本數據類型有五種:數字、字符串、布爾值、未定義值和空值。而常見的引用數據類型有兩種:數組、對象。這一節,我們先來介紹基本數據類型。后面章節會逐漸介紹“數組”和“對象”這兩種引用數據類型。

          運算符

          在JavaScript中,要完成各種各樣的運算,是離不開運算符的。運算符用于將一個或幾個值進行運算從而得出所需要的結果值。就像我們數學上,也需要加減乘除這些運算符才可以運算。不過對于JavaScript來說,我們需要遵循計算機語言運算的一套方法。

          在JavaScript中,運算符指的是“變量”或“值”進行運算操作的符號。在JavaScript中,常見的運算符有五種:

          算術運算符

          賦值運算符

          比較運算符

          邏輯運算符

          條件運算符

          cJinja 是一個使用cpp編寫的輕量html模版解析庫,依賴 ejson 來實現模版的數據替換(在jinja中稱為context,上下文)。模版的語法基本與django jinja一致,功能還算豐富。源碼僅有700行,適合學習,覺得不錯的點個star吧。

          (該程序為 https://github.com/HuangHongkai/tinyserver 中的一個模塊)

          編譯

          使用cmake來編譯,windows和linux下均可編譯。推薦使用clion作為IDE。

          編譯成功后在build目錄下會有libcjinja.a和cjinja_test.exe這2個文件。libcjinja.a是靜態庫,cjinja_test.exe是一個簡單的測試程序。

          運行測試程序后會出現output.html(該文件是tmp.html解析后的結果。)

          已經完成的功能

          • 變量,例如 {{ var }}
          • 變量索引訪問,例如 {{ var.var2 }} {{ var[2] }} {{ var[2].key.value[2] }},其中**[]** 表示對數組(類似python的list)進行索引, . 表示對object進行索引(類似與python的dict)
          • 表達式計算(包括字符串拼接) ,例如{{ 1*1+2-3*var }} {{ 1+1*2-3/4 }} {{ "asdfsf"+var }}
          • for-endfor對列表進行迭代, 例如 {% for var in list %} {% endfor %}
          • for-endfor對對象進行迭代,例如 {% for key,value in object %} {% endfor %} 或者 {% for key in object %}{% endfor %} 或者 {% for ,value in object %} {% endfor %}
          • if-else-endif 語句, 其中if的條件支持四則運算,簡單的比較(!= == )等,例如 {% if 1+1==2 %}aaa{% else %}bbb{%endif %}
          • 模版包含,嵌套其他的模版文件{% include 'other.html' %}
          • 模版語法錯誤提示

          需要注意,表達式之間不能含有空格,例如{{ 1 + 1 }}是非法的,而{{ 1+1 }}是合法的。

          使用方法

          1. 變量和變量索引

          簡單的例子如下,

          HtmlTemplate html("username:{{ username }}\n"
           "parm.list[1][2]: {{parm.list[1][2] }} \n"
           "parm.key: {{ parm.key }}",
           1); // 參數1表示傳入的是模版字符串,0表示傳入的是文件名,默認為0
          JSONObject obj = {
           {"username", 1234},
           {"parm", {
           {"key", "cde"},
           {"list", {1, {1,2.3, "abcd"}, "hahaha"}},
           }}
          };
          html.setValue(obj);
          cout << html.render() << endl << endl;
          /* 運行后打印如下
          username:1234
          parm.list[1]: abcd 
          parm.key: cde
          */
          

          HtmlTemplate是一個庫的主要類,構造函數為

          explicit HtmlTemplate(const string& str, int flag = 0); // flag=0是str表示文件路徑,不為0是表示傳入的模版字符串

          其中str參數為字符串,可以表示html模板原始串,也可也表示為文件的路徑,flag默認為0。

          setValue 方法表示傳入數據給模版對象。

          render() 方法表示將模版解析成字符串。

          JSONObject來源于 ejson 庫,用來模擬python的dict,構造函數也比較容易看懂。

          2. 列表迭代

          HtmlTemplate html("{% for x in list %}{{ x }}\n{%endfor%}"
           "此時x已經是臨時變量了,不可以在打印了 {{x}}\n"
           , 1);
          JSONObject obj = OBJECT(
           KEYVALUE("list", LIST(1,2,3,4,5))
          );
          cout << html.setValue(obj).render() << endl << endl;
          /*運行后輸出如下
          1
          2
          3
          4
          5
          此時x已經是臨時變量了,不可以在打印了 
          */
          

          注意到在迭代過程中x是作為臨時變量,在外部的話是無法打印出來的。

          3. 字典迭代

          HtmlTemplate html("{% for key in dict %}迭代1: 字典的key值為 {{ key }}\n{% endfor %}"
           "{% for key,value in dict %}迭代2: 字典的key值為 {{ key }}, value值為 {{ value}}\n{% endfor %}"
           "{% for ,value in dict %}迭代3: 字典的value值為 {{ value }}\n{% endfor %}", 1);
          JSONObject obj = OBJECT(
           KEYVALUE("dict", OBJECT(
           KEYVALUE("key1", "value1"),
           KEYVALUE("key2", 1234),
           KEYVALUE("key3", nullptr),
           ))
          );
          cout << html.setValue(obj).render() << endl << endl;
          /*運行后輸出
          迭代1: 字典的key值為 key1
          迭代1: 字典的key值為 key2
          迭代1: 字典的key值為 key3
          迭代2: 字典的key值為 key1, value值為 value1
          迭代2: 字典的key值為 key2, value值為 1234
          迭代2: 字典的key值為 key3, value值為 null
          迭代3: 字典的value值為 value1
          迭代3: 字典的value值為 1234
          迭代3: 字典的value值為 null
          */
          

          4. 字符串拼接與表達式計算

          HtmlTemplate html("{{ a+b+c+\"444\" }}\n"
           "{{x}} * {{y}} + 2 * 3 - 4 / {{x}} = {{ x*y+2*3-4/x }}\n",
           1);
          JSONObject obj = OBJECT(
           KEYVALUE("a", "111"),
           KEYVALUE("b", "222"),
           KEYVALUE("c", "333"),
           KEYVALUE("x", 12),
           KEYVALUE("y", 34)
           );
          cout << html.setValue(obj).render() << endl << endl;
          /*運行后輸出
          111222333444
          12 * 34 + 2 * 3 - 4 / 12 = 413.667
          */
          

          5. if-else-endif語句

          HtmlTemplate html("{% if 1==1 %} 1==1 成立 {% else %} 1==1不成立 {%endif %}\n"
           "{% if !x %} x為空 {% else %} x不為空 {%endif %}\n"
           "{% if x==2 %} x==2 成立 {% endif %}\n"
           "{% if x+1!=2 %} x+1!=2 成立 {% endif %}\n"
           "{% if x<3 %} x<3 成立 {% endif %}\n"
           "{% if x>1 %} x>1 成立 {% endif %}\n"
           "{% if str==\"abcd\" %} str為abcd {% endif %}\n"
           "{% if 1 %} 常量表達式1 {% endif %}\n"
           "{% if 0 %} 常量表達式0,此處不會輸出 {%endif%}", 1);
          JSONObject obj = {
           {"x", 2},
           {"str", "abcd"}
          };
          cout << html.setValue(obj).render() << endl;
          /*運行后輸出
           1==1 成立 
           x不為空 
           x==2 成立 
           x+1!=2 成立 
           x<3 成立 
           x>1 成立 
           str為abcd 
           常量表達式1 
          */
          

          6.for與if嵌套使用

           HtmlTemplate html("{%for x in list%}"
           "{%if x %}"
           "{% for y in list2%}"
           "{{x}} * {{y}} = {{ x*y }}\n"
           "{% endfor %}"
           "{% else %}"
           "x的值為空\n"
           "{%endif%}"
           "{% endfor%}", 1);
          JSONObject obj = OBJECT(
           KEYVALUE("list", LIST(1,2,3,4,5)),
           KEYVALUE("list2", LIST(1,2,3)),
          );
          cout << html.setValue(obj).render() << endl << endl;
          /*運行后輸出
          1 * 1 = 1
          1 * 2 = 2
          1 * 3 = 3
          2 * 1 = 2
          2 * 2 = 4
          2 * 3 = 6
          3 * 1 = 3
          3 * 2 = 6
          3 * 3 = 9
          4 * 1 = 4
          4 * 2 = 8
          4 * 3 = 12
          5 * 1 = 5
          5 * 2 = 10
          5 * 3 = 15
          */
          

          7.模版文件作為輸出

          HtmlTemplate html("tmpl.html");
          JSONObject context = OBJECT(
           ...
          );
          FILE* f = fopen("output.html", "w"); // 寫入到文件中
          string&& str = html.setValue(context).render();
          fwrite(str.c_str(), 1, str.size(), f);
          fclose(f);
          /*運行后,代開當前目錄的tmpl.html文件作為輸入,輸出文件為output.html*/
          /*如果tmpl.html不存在則拋出異常*/
          

          8. 異常處理

          HtmlTemplate html("{% if 1 %} xxx ", 1);
          // 不傳入context
          try {
           cout << html.render() << endl;
          } catch(exception& e) {
           cerr << e.what() << endl;
          }
          cout << endl;
          

          運行后終端上打印如下,

          會提示異常的類名,異常文件所在位置,代碼行數,以及一些錯誤的信息。

          討論

          1. 實現一個簡單的表達式計算器用什么方法比較好?(例如 {{ 2.3*3+4/5*x }} 這類表達式)

          我分享一下我自己的方法,有什么更好的方法一起討論一下。

          • 第一步,先把數據和符號提取出來放入到數組中,輸入類型全部設為double。例如上面那個表達式,符號提取出來是{*, /, *}, 數據提取出來是{2.3, 3, 4, 5, x}
          • 這一步位于__parse_var這個函數,比較簡單不詳細討論。
          • 第二步,先計算乘除法,結果放入棧中,在對棧中元素計算加減法(按照我們平常計算表達式的思路先乘除后加減),這一步實現如下(其中運用到C語言的宏和C++11的匿名函數)
          double cJinja::HtmlTemplate::calculator(vector<any>& number, vector<char>& op) {
           // 例如下表達式會成為
           // 1 - 2 - 3 + 2 *3 * 4 - 4*5
           // vector<char> op = { '-', '-', '+', '*', '*', '-', '*' };
           // vector<any> number = { 1, 2, 3, 2, 3, 4, 4, 5 };
           if (number.size() != op.size() + 1)
           throwException(TemplateParseException, "運算符號數和操作數不匹配");
           /* 定義計算器的內部函數 */
           auto calc = [](any& var1, double var2, char op) -> double{
           // var2 + var1
           // var2 * var1
           // var2 - var1
           // var2 / var1
           // 注意順序
          #define CALC(op2) \
           if(#op2[0] == op) { \
           if (var1.type() == typeid(int)) \
           return var2 op2 static_cast<double>(any_cast<int>(var1)); \
           else if (var1.type() == typeid(float)) \
           return var2 op2 static_cast<double>(any_cast<float>(var1)) ; \
           else if (var1.type() == typeid(double)) \
           return var2 op2 static_cast<double>(any_cast<double>(var1)) ; \
           }
           CALC(+);
           CALC(-);
           CALC(*);
           CALC(/);
           throwException(TemplateParseException, "不允許對空指針進行運算");
          #undef CALC
           };
           vector<double> num_stack; // 計算中間結果存儲棧
           num_stack.push_back(calc(number[0], 0, '+')); // 獲取值 number[i+1] + 0 (加法運算零元為0,乘法運算零元為1)
           /* 計算 * / 法*/
           for (size_t i = 0; i < op.size(); i++) {
           if (op[i] == '+' || op[i] == '-') {
           num_stack.push_back(calc(number[i + 1], 0, '+')); // number[i+1] + 0
           }
           else if (op[i] == '*' || op[i] == '/') {
           double var1 = num_stack.back(); num_stack.pop_back();
           num_stack.push_back(calc(number[i + 1], var1, op[i])); // var1/number[i+1] 或者是 var1/number[i+1]
           } else
           throwException(TemplateParseException, str_format("非法操作符 %d", op[i]));
           }
           /* 計算 + - 法*/
           double result = num_stack[0];
           size_t i = 1;
           for (auto& ch : op) {
           if (ch == '+') {
           result += num_stack[i++];
           } else if(ch == '-') {
           result -= num_stack[i++];
           }
           }
           return result;
          }
          

          2. 拋出異常包含更多的信息

          我定義了一個throwException宏,如下

          #define throwException(Exception, ...) { \
           std::cerr << "[" << #Exception << "] : FILE: " << string(__FILE__).substr(string(__FILE__).find_last_of('/') + 1) << " LINE: " << __LINE__ << " FUNCTION: " <<__FUNCTION__ << std::endl; \
           throw Exception(__VA_ARGS__); \
           }
          

          其中__FILE__ 為文件名,__LINE__ 為當前代碼行數,這些都是C中的內置宏,__VA_ARGS__是可變參數,對應于宏函數參數中的....

          日常工作計算中,我們如履薄冰,但是 JavaScript 總能給我們這樣那樣的 surprise~

          1. 0.1 + 0.2 = ?
          2. 1 - 0.9 = ?

          如果小伙伴給出內心的結果:

          1. 0.1 + 0.2 = 0.3
          2. 1 - 0.9 = 0.1

          那么小伙伴會被事實狠狠地扇臉:

          console.log(0.1 + 0.2); // 0.30000000000000004
          console.log(1 - 0.9); // 0.09999999999999998

          為什么會出現這種情況呢?咱們一探究竟!

          三 問題復現

          返回目錄

          下面,我們會通過探討 IEEE 754 標準,以及 JavaScript 加減的計算過程,來復現問題。

          3.1 根源:IEEE 754 標準

          返回目錄

          JavaScript 里面的數字采用 IEEE 754 標準的 64 位雙精度浮點數。該規范定義了浮點數的格式,對于 64 位的浮點數在內存中表示,最高的 1 位是符號為,接著的 11 位是指數,剩下的 52 位為有效數字,具體:

          • 第 0 位:符號位。用 s 表示,0 表示為正數,1 表示為負數;
          • 第 1 - 11 位:存儲指數部分。用 e 表示;
          • 第 12 - 63 位:存儲小數部分(即有效數字)。用 f 表示。



          符號位決定一個數的正負,指數部分決定數值的大小,小數部分決定數值的精度。

          IEEE 754 規定,有效數字第一位默認總是 1,不保存在 64 位浮點數之中。

          也就是說,有效數字總是 1.XX......XX的形式,其中 XX......XX 的部分保存在 64 位浮點數之中,最長可能為 52 位。

          因此,JavaScript 提供的有效數字最長為 53 個二進制位(64 位浮點的后 52 位 + 有效數字第一位的 1)。

          3.2 復現:計算過程

          返回目錄

          通過 JavaScript 計算 0.1 + 0.2 時,會發生什么?

          1、 將 0.1 和 0.2 換成二進制表示:

          0.1 -> 0.0001100110011001...(無限)
          0.2 -> 0.0011001100110011...(無限

          浮點數用二進制表達式是無窮的

          我自己是一名從事了多年開發的web前端老程序員,目前辭職在做自己的web前端私人定制課程,今年年初我花了一個月整理了一份最適合2019年學習的web前端學習干貨,各種框架都有整理,送給每一位前端小伙伴,想要獲取的可以關注我的頭條號并在后臺私信我:前端,即可免費獲取。

          2、 因為 IEEE 754 標準的 64 位雙精度浮點數的小數部分最多支持 53 位二進制位,所以兩者相加之后得到二進制為:

          0.0100110011001100110011001100110011001100110011001100

          因為浮點數小數位的限制,這個二進制數字被截斷了,用這個二進制數轉換成十進制,就成了 0.30000000000000004,從而在進行算數計算時產生誤差。

          3.3 擴展:數字安全

          返回目錄

          在看完上面小數的計算不精確后,jsliang 覺得有必要再聊聊整數,因為整數同樣存在一些問題:

          console.log(19571992547450991);
          // 19571992547450990
          
          console.log(19571992547450991 === 19571992547450994);
          // true

          是不是很驚奇!

          因為 JavaScript 中 Number 類型統一按浮點數處理,整數也不能逃避這個問題:

          // 最大值
          const MaxNumber = Math.pow(2, 53) - 1;
          console.log(MaxNumber); // 9007199254740991
          console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
          
          // 最小值
          const MinNumber = -(Math.pow(2, 53) - 1);
          console.log(MinNumber); // -9007199254740991
          console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991

          即整數的安全范圍是: [-9007199254740991, 9007199254740991]。

          超過這個范圍的,就存在被舍去的精度問題。

          當然,這個問題并不僅僅存在于 JavaScript 中,幾乎所有采用了 IEEE-745 標準的編程語言,都會有這個問題,只不過在很多其他語言中已經封裝好了方法來避免精度的問題。

          • PHP Float 浮點型 - Manual
          • Java 您的小數點到哪里去了? - Brian Goetz

          而因為 JavaScript 是一門弱類型的語言,從設計思想上就沒有對浮點數有個嚴格的數據類型,所以精度誤差的問題就顯得格外突出。

          到此為止,我們可以看到 JavaScript 在處理數字類型的操作時,可能會產生一些問題。

          事實上,工作中還真會有問題!

          某天我處理了一個工作表格的計算,然后第二天被告知線上有問題,之后被產品小姐姐問話:

          • 為什么小學生都能做出的小數計算,你們計算機算不了呢?

          默哀三秒,產生上面的找到探索,最終找到下面的解決方案。

          四 解決問題

          返回目錄

          下面嘗試通過各種方式來解決浮點數計算的問題。

          4.1 toFixed()

          返回目錄

          toFixed() 方法使用定點表示法來格式化一個數值。

          • 《toFixed - MDN》

          語法:numObj.toFixed(digits)

          參數:digits。小數點后數字的個數;介于 0 到 20(包括)之間,實現環境可能支持更大范圍。如果忽略該參數,則默認為 0。

          const num = 12345.6789;
          
          num.toFixed(); // '12346':進行四舍五入,不包括小數部分。
          num.toFixed(1); // '12345.7':進行四舍五入,保留小數點后 1 個數字。
          num.toFixed(6); // '12345.678900':保留小數點后 6 個數字,長度不足時用 0 填充。
          (1.23e+20).toFixed(2); // 123000000000000000000.00 科學計數法變成正常數字類型

          toFixed() 得出的結果是 String 類型,記得轉換 Number 類型。

          toFixed() 方法使用定點表示法來格式化一個數,會對結果進行四舍五入。

          通過 toFixed() 我們可以解決一些問題:

          原加減乘數:

          console.log(1.0 - 0.9);
          // 0.09999999999999998
          
          console.log(0.3 / 0.1);
          // 2.9999999999999996
          
          console.log(9.7 * 100);
          // 969.9999999999999
          
          console.log(2.22 + 0.1);
          // 2.3200000000000003

          使用 toFixed():

          // 公式:parseFloat((數學表達式).toFixed(digits));
          // toFixed() 精度參數須在 0 與20 之間
          
          parseFloat((1.0 - 0.9).toFixed(10));
          // 0.1 
          
          parseFloat((0.3 / 0.1).toFixed(10));
          // 3 
          
          parseFloat((9.7 * 100).toFixed(10));
          // 970
          
          parseFloat((2.22 + 0.1).toFixed(10));
          // 2.32

          那么,講到這里,問題來了:

          • parseFloat(1.005.toFixed(2))

          會得到什么呢,你的反應是不是 1.01 ?

          然而并不是,結果是:1。

          這么說的話,enm...摔!o(╥﹏╥)o

          toFixed() 被證明了也不是最保險的解決方式。

          4.2 手寫簡易加減乘除

          返回目錄

          既然 JavaScript 自帶的方法不能自救,那么我們只能換個思路:

          • 將 JavaScript 的小數部分轉成字符串進行計算
          /**
           * @name 檢測數據是否超限
           * @param {Number} number 
           */
          const checkSafeNumber = (number) => {
           if (number > Number.MAX_SAFE_INTEGER || number < Number.MIN_SAFE_INTEGER) {
           console.log(`數字 ${number} 超限,請注意風險!`);
           }
          };
          
          /**
           * @name 修正數據
           * @param {Number} number 需要修正的數字
           * @param {Number} precision 端正的位數
           */
          const revise = (number, precision = 12) => {
           return +parseFloat(number.toPrecision(precision));
          }
          
          /**
           * @name 獲取小數點后面的長度
           * @param {Number} 需要轉換的數字
           */
          const digitLength = (number) => {
           return (number.toString().split('.')[1] || '').length;
          };
          
          /**
           * @name 將數字的小數點去掉
           * @param {Number} 需要轉換的數字
           */
          const floatToInt = (number) => {
           return Number(number.toString().replace('.', ''));
          };
          
          /**
           * @name 精度計算乘法
           * @param {Number} arg1 乘數 1
           * @param {Number} arg2 乘數 2
           */
          const multiplication = (arg1, arg2) => {
           const baseNum = digitLength(arg1) + digitLength(arg2);
           const result = floatToInt(arg1) * floatToInt(arg2);
           checkSafeNumber(result);
           return result / Math.pow(10, baseNum);
           // 整數安全范圍內的兩個整數進行除法是沒問題的
           // 如果有,證明給我看
          };
          
          console.log('------\n乘法:');
          console.log(9.7 * 100); // 969.9999999999999
          console.log(multiplication(9.7, 100)); // 970
          
          console.log(0.01 * 0.07); // 0.0007000000000000001
          console.log(multiplication(0.01, 0.07)); // 0.0007
          
          console.log(1207.41 * 100); // 120741.00000000001
          console.log(multiplication(1207.41, 100)); // 0.0007
          
          /**
           * @name 精度計算加法
           * @description JavaScript 的加法結果存在誤差,兩個浮點數 0.1 + 0.2 !== 0.3,使用這方法能去除誤差。
           * @param {Number} arg1 加數 1
           * @param {Number} arg2 加數 2
           * @return arg1 + arg2
           */
          const add = (arg1, arg2) => {
           const baseNum = Math.pow(10, Math.max(digitLength(arg1), digitLength(arg2)));
           return (multiplication(arg1, baseNum) + multiplication(arg2, baseNum)) / baseNum;
          }
          
          console.log('------\n加法:');
          console.log(1.001 + 0.003); // 1.0039999999999998
          console.log(add(1.001, 0.003)); // 1.004
          
          console.log(3.001 + 0.07); // 3.0709999999999997
          console.log(add(3.001, 0.07)); // 3.071
          
          /**
           * @name 精度計算減法
           * @param {Number} arg1 減數 1
           * @param {Number} arg2 減數 2
           */
          const subtraction = (arg1, arg2) => {
           const baseNum = Math.pow(10, Math.max(digitLength(arg1), digitLength(arg2)));
           return (multiplication(arg1, baseNum) - multiplication(arg2, baseNum)) / baseNum;
          };
          
          console.log('------\n減法:');
          console.log(0.3 - 0.1); // 0.19999999999999998
          console.log(subtraction(0.3, 0.1)); // 0.2
          
          /**
           * @name 精度計算除法
           * @param {Number} arg1 除數 1
           * @param {Number} arg2 除數 2
           */
          const division = (arg1, arg2) => {
           const baseNum = Math.pow(10, Math.max(digitLength(arg1), digitLength(arg2)));
           return multiplication(arg1, baseNum) / multiplication(arg2, baseNum);
          };
          
          console.log('------\n除法:');
          console.log(0.3 / 0.1); // 2.9999999999999996
          console.log(division(0.3, 0.1)); // 3
          
          console.log(1.21 / 1.1); // 1.0999999999999999
          console.log(division(1.21, 1.1)); // 1.1
          
          console.log(1.02 / 1.1); // 0.9272727272727272
          console.log(division(1.02, 1.1)); // 數字 9272727272727272 超限,請注意風險!0.9272727272727272
          
          console.log(1207.41 / 100); // 12.074100000000001
          console.log(division(1207.41, 100)); // 12.0741
          
          /**
           * @name 按指定位數四舍五入
           * @param {Number} number 需要取舍的數字
           * @param {Number} ratio 精確到多少位小數
           */
          const round = (number, ratio) => {
           const baseNum = Math.pow(10, ratio);
           return division(Math.round(multiplication(number, baseNum)), baseNum);
           // Math.round() 進行小數點后一位四舍五入是否有問題,如果有,請證明出來
           // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/round
          }
          
          console.log('------\n四舍五入:');
          console.log(0.105.toFixed(2)); // '0.10'
          console.log(round(0.105, 2)); // 0.11
          
          console.log(1.335.toFixed(2)); // '1.33'
          console.log(round(1.335, 2)); // 1.34
          
          console.log(-round(2.5, 0)); // -3
          console.log(-round(20.51, 0)); // -21

          在這份代碼中,我們先通過石錘乘法的計算,通過將數字轉成整數進行計算,從而產生了 安全 的數據。

          JavaScript 整數運算會不會出問題呢?

          乘法計算好后,假設乘法已經沒問題,然后通過乘法推出 加法、減法 以及 除法 這三則運算。

          最后,通過乘法和除法做出四舍五入的規則。

          JavaScript Math.round() 產生的數字會不會有問題呢、

          這樣,我們就搞定了兩個數的加減乘除和四舍五入(保留指定的長度),那么,里面會不會有問題呢?

          如果有,請例舉出來。

          如果沒有,那么你能不能依據上面兩個數的加減乘除,實現三個數甚至多個數的加減乘除?

          五 現成框架

          返回目錄

          這么重要的計算,如果自己寫的話你總會感覺惶惶不安,感覺充滿著危機。

          所以很多時候,我們可以使用大佬們寫好的 JavaScript 計算庫,因為這些問題大佬已經幫我們進行了大量的測試了,大大減少了我們手寫存在的問題,所以我們可以調用別人寫好的類庫。

          下面推薦幾款不錯的類庫:

          • Math.js。

          Math.js 是一個用于 JavaScript 和 Node.js 的擴展數學庫。

          它具有支持符號計算的靈活表達式解析器,大量內置函數和常量,并提供了集成的解決方案來處理不同的數據類型,例如數字,大數,復數,分數,單位和矩陣。

          強大且易于使用。

          • decimal.js

          JavaScript 的任意精度的十進制類型。

          • big.js

          一個小型,快速,易于使用的庫,用于任意精度的十進制算術運算。

          • bignumber.js

          一個用于任意精度算術的 JavaScript 庫。

          最后的最后,值得一提的是:如果對數字的計算非常嚴格,或許你可以將參數丟給后端,讓后端進行計算,再返回給你結果。

          例如涉及到比特幣、商城商品價格等的計算~


          作者:jsliang
          鏈接:https://juejin.im/post/5ddc7fa66fb9a07ad665b1f0


          主站蜘蛛池模板: 美女视频一区二区| 国产成人精品一区二三区熟女| 亚洲制服中文字幕第一区| 日韩在线视频一区二区三区 | 色婷婷AV一区二区三区浪潮| 一区二区三区精密机械| 成人精品一区二区三区中文字幕 | 国产美女av在线一区| 午夜影院一区二区| 日产亚洲一区二区三区| 爆乳熟妇一区二区三区霸乳| 日韩精品国产一区| 国产女人乱人伦精品一区二区| 亚洲国产日韩在线一区| 亚洲一区二区三区乱码在线欧洲| 国产午夜精品一区二区三区嫩草| 精品国产毛片一区二区无码 | 国产在线精品一区二区高清不卡| 国产一区二区在线观看视频| 婷婷亚洲综合一区二区| 波多野结衣一区在线观看| 人妻无码一区二区三区四区| 日本精品一区二区三区在线视频一 | 精品欧美一区二区在线观看| 日本无码一区二区三区白峰美| 亚洲中文字幕一区精品自拍 | 人妖在线精品一区二区三区| 久久毛片一区二区| 日韩一区二区三区电影在线观看 | 日韩熟女精品一区二区三区| 国产美女av在线一区| 日韩人妻不卡一区二区三区| 国产一区二区好的精华液| 日本人真淫视频一区二区三区| 中文字幕无码一区二区三区本日| 麻豆AV一区二区三区| 一区二区三区四区在线观看视频| 中文字幕永久一区二区三区在线观看 | 精品一区二区三区视频| 无码日韩精品一区二区三区免费| 国产精品成人免费一区二区|