们都知道,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小时内与您取得联系。