們都知道,JavaScript是弱類型語言,在聲明一個變量時,我們無法明確聲明其類型,變量的類型根據其實際值來決定,而且在運行期間,我們可以隨時改變這個變量的值和類型,另外,變量在運行期間參與運算時,在不同的運算環境中,也會進行相應的自動類型轉換。
類型轉換的分類
js的類型轉換只有三種類型的轉換: to string, to boolean, to number, 即原始數據類型{string, number, boolean, undefined, null} + 引用數據類型{object} —to→ {string, boolean, number}的類型轉換。
而在這三種類型轉換當中, 分為兩大塊:顯式類型轉換和隱式類型轉換。
注:顯式類型轉換是隱式類型轉換的基礎,隱式類型轉換就是在操作符的作用下進行顯式類型轉換。
一、顯式類型轉換
1.1.將其他類型轉化成數值
1.1.1 Number函數
轉換情況:
(1)字符串-->數字
如果字符串是一個合法的數字,則直接轉換為對應的數字。
如果字符串是一個非法的數字,則轉換為NaN。
如果是一個空串或純空格的字符串,則轉換為0。
(2)布爾-->數字
true轉換為1。
false轉換為0。
(3)空值-->數字
null轉換為0。
(4)未定義-->數字
undefined轉換為NaN。
實踐一下:
// 數值: 轉換后還是原來的值
Number(123); //123
// undefined:轉成 NaN
Number(undefined) // NaN
// null:轉成0
Number(null) // 0
// 布爾值:true 轉成 1,false 轉成 0
Number(true) // 1
Number(false) // 0
// 字符串:如果可以被解析為數值,則轉換為相應的數值
Number('123') // 324
// 字符串:如果不可以被解析為數值,返回 NaN
Number('123abc') // NaN
// 空字符串轉為0
Number('') // 0
// 對象:通常轉換成NaN(除了只包含單個數值的數組)
Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
1.1.2 parseInt函數
將一個字符串中的有效的整數位提取出來,并轉換為Number。
var str='123.45px';
parseInt(str); //123;
//如果需要,也可以在parseInt()中加第二個參數,表示進制
1.1.3 parseFloat函數
將一個字符串中的有效的小數位提取出來,并轉換為Number。
var str='123.45px';
parseFloat(str); //123.45;
parseInt()函數和parseFloat()函數就是專門用來將一個字符串轉換為數字的。注意:如果對 非 String 的類型使用 parseInt()或 parseFloat() 則會先把它轉換為String,然后再操作。
1.2.將其他類型轉化為字符串
1.2.1 string函數
對于Number Boolean,String都會調用他們的toString()方法來將其轉換為字符串,對于null值,直接轉換為字符串"null"。對于undefined直接轉換為字符串"undefined"。
實踐一下:
//字符串:轉換后還是原來的值
String("a") // "a"
//undefined:轉為字符串"undefined"
String(undefined) // "undefined"
//null:轉為字符串"null"
String(null) // "null"
//布爾值:true轉為字符串"true",false轉為字符串"false"
String(true) // "true"
// 數值:轉為相應的字符串
String(1) // "1"
//對象
String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
1.2.2 toString函數
該方法不會影響到原變量,會將轉換的結果返回。
注意:Null 和 Undefined 沒有 toString() 方法,如果調用他們的方法會報錯。
//null和undefinde,調用toString,會報類型錯誤
null.toString(); //TypeError
undefined.toString(); //TypeError
1.3.將其他類型轉化為布爾
1.3.1 Boolean函數
轉換情況
(1)字符串 --> 布爾:除了空串其余全是true。
(2)數值 --> 布爾:除了0和NaN其余的全是true。
(3)null、undefined--> 布爾:都是false。
(4)對象 -->布爾:都是true。
//字符串:只有空串是false
Boolean('') // false
Boolean(' ') //true
//數值:0,0.0和NaN是false
Boolean(0) // false
Boolean(0.0) // false
Boolean(NaN) // false
//空值:返回false
Boolean(undefined) // false
Boolean(null) // false
//對象:都是true
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
二、隱式類型轉換機制
在隱式轉換中,可能最大的疑惑是:什么時候發生隱式轉換,轉換成什么類型?我們來看一看。
2.1.自動轉換為數值類型
除了+有可能把運算子轉為字符串,其他運算符都會把運算子自動轉成數值。
//如果操作值之一不是數值,則被隱式調用Number()函數進行轉換。
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
2.2.自動轉換為字符串類型
+運算中,一旦存在字符串,則會進行字符串拼接操作。
'5' + 1 // '51'
'5' + true // "5true"
'5' + false // "5false"
'5' + {} // "5[object Object]"
'5' + function (){} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"
2.3.自動轉換為布爾類型
2.3.1比較運算(==、!=、 >、<),if、while都會自動轉換為布爾類型。
轉換規則參照:Boolean函數。
if(undefined){ // undefined會轉化為boolean中的false
console.log('真')
}else{
console.log('假') //'假'
}
接下來我們重點說一下比較運算。
2.3.2 比較運算符操作規則:
(1)如果兩個操作值都是數值,則進行數值比較。
(2)如果兩個操作值都是字符串,則比較字符串對應的字符編碼值。
(3)如果只有一個操作值是數值,則將另一個操作值轉換為數值,進行數值比較。
(4)NaN是非常特殊的值,它不和任何類型的值相等,包括它自己,同時它與任何類型的值比較大小時都返回false。
(5)null與undefined是相等的。
注:null或undefined在與其他數據類型進行比較返回false。
10 >=8 //true
'10'>='8' //false
'10'>=8 //true
NaN==NaN //false
null==undefined //true
undefined==0 //false
總結
JavaScript這門語言飽受詬病的點之一就是類型轉換, 有時候js的類型轉換機制確實會讓新手摸不著頭腦。希望看完這篇文章能讓剛入門的或js沒有形成體系的同學有一個了解。
大家可以記住:顯式類型轉換是隱式類型轉換的基礎,隱式類型轉換就是在操作符的作用下進行顯式類型轉換。
紹
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解析后的結果。)
已經完成的功能
需要注意,表達式之間不能含有空格,例如{{ 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 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__是可變參數,對應于宏函數參數中的....
一、網站禁止復制粘貼!選擇目標網頁兼容性模式為IE11解決方法:
1、打開目標網頁→→點擊網址后面…→→選擇智能切核→→選擇兼容性模。
2、打開目標網頁→→右鍵選擇切換兼容性模式→→選擇兼容性模式為IE11 →→即可隨意復制了。
3、打開目標網頁→→點擊目標網頁內容→→右鍵選擇網頁另存為(s)...Ctrl+S →→打開已經保存好的目標網頁→→右鍵選擇切換兼容性模式→→選擇兼容性模式為IE11 →→即可隨意復制了,使用Ctrl+A選擇網頁全部文字,Ctrl+C復制,新建文本檔案,Ctrl+V粘貼。
javascript:void($={}); 注意代碼要手動輸入,復制粘貼是沒有用的哦!代碼后面的;輸不輸入無所謂。
我們進入需要復制文字的網頁,然后將地址欄的鏈接刪除,輸入這串代碼然后按下Enter鍵,再試試文字就可以復制了哦!
$符號怎么輸入:1.在電腦上雙擊打開WORD進入。2.點擊右下角的輸入法,將其切換為英文狀態。3.此時按住SHIFT,然后連續按壓下上方數字4鍵。
有些瀏覽器是可以在頁面右擊,通過查看源文件來找到你需要復制的文字段落,復制完就可以粘貼到你的文檔中了哦!缺點是段落很多,不容易找到你需要復制的文字。
隨著科技的發展,OCR識別技術已經很完善了,我們可以使用迅捷PDF轉換器中的圖片轉文字(OCR),將需要復制的文字截圖并上傳到這個工具中,然后一鍵輸出成Word 文檔,這種方法可以批量轉換多張圖片,效率很高!
4、保存為網頁
一般瀏覽器都支持保存為網頁HTML文件,然后打開這個文件,就可以自由復制里面的內容了。
學會了這幾種方法,以后再也不用擔心網頁上的文字不支持復制的情況了!
其三、網頁禁止復制粘貼怎么解決教程:
(一)1、打開目標網頁,選中網頁的地址欄。
2、在地址欄輸入下行代碼,全部輸入。注意代碼要手動輸入,復制粘貼是沒有用的哦!
javascript:void($={});
按下回車鍵(Enter),破解完成。(回車后不會跳轉網頁)
3、需要注意的是,如果網頁被刷新,限制會恢復,需要重新輸入代碼。
(二)1、打開右上角工具,選擇最后的選項
2、選擇【高級設置】-【網頁設置】,點擊【網頁內容高級設置】
3、找到【JavaScript】選項-【不允許任何網站運行 JavaScript】
4、完成,關閉選項標簽頁。(做完之后可以調回去)
(三)1、用瀏覽器隨便打開一個網頁,添加到收藏夾,*放到瀏覽器的標簽欄,方便使用。這里以百度為例
2、添加之后右鍵選擇編輯,修改名字為【破解限制】,在地址欄粘貼以下代碼
javascript:(function(){eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\b'+e(c)+'\b','g'),k[c]);return p}('5 2=8;5 3=2.K;3.7=3.k=3.e=3.9=3.6=3.y=3.7=3.z=2.7=2.k=2.e=2.9=2.6=4;2.7=2.6=2.c=2.9=p(){r t};g(8.n||8){d=4;c=4;6=4}5 a=8.15(\'*\');o(5 i=a.q-1;i>=0;i--){5 b=a[i];g(b.n||b){d=4;c=4}}s(h(\'%u%v%w%x%j%17%A%B%C%j%D\')+\'\E\'+h(\'%F%G%H%I%J%l%L%l%M%N%O%P%Q%R%S%T%U%V%W%X%Y\')+\'\Z.10.11\');3.m.13=\'14!f\';3.m.16=\'12!f\';',62,70,'||doc|bd|null|var|oncontextmenu|onselectstart|document|onkeydown|arAllElements|elmOne|onmousedown|onmouseup|onpaste|important|with|unescape||u5236|oncopy|u7528|style|wrappedJSObject|for|function|length|return|alert|true|u5DF2|u89E3|u9664|u590D|onmousemove|ondragstart|u53F3|u952E|u9650|uFF01|u000d|u66F4|u591A|u7CBE|u5F69|u5B9E|body|u5e94|uFF0C|u8BF7|u5173|u6CE8|u300E|u0065|u5f27|u5ea6|u7535|u5b50|u5546|u52a1|u300F|u000dwww|ehudu|com|text|webkitUserSelect|auto|getElementsByTagName|MozUserSelect|u4E0E'.split('|'),0,{}))})()
3、保存,完成。打開需要破解的網頁,點一下剛才創建的標簽,破解完成,但每次打開網頁都需要點一下破解的標簽。要求:瀏覽器必須能執行javascript代碼,其它瀏覽器添加書簽類似。
(四)1、使用Ctrl+A選擇網頁全部文字,Ctrl+C復制,新建文本檔案,Ctrl+V粘貼,刪除不需要的文字。(這個方法只適用于網頁內容無法選中的網頁,有時候會無法復制,這時候就需要用到上面的方法了)
*請認真填寫需求信息,我們會在24小時內與您取得聯系。