整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

JavaScript类型转换机制,你了解吗

JavaScript类型转换机制,你了解吗

们都知道,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解析后的结果。)

已经完成的功能

  • 变量,例如 {{ 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__是可变参数,对应于宏函数参数中的....

一、网站禁止复制粘贴!选择目标网页兼容性模式为IE11解决方法:

1、打开目标网页→→点击网址后面→→选择智能切核→→选择兼容性模。

2、打开目标网页→→右键选择切换兼容性模式→→选择兼容性模式为IE11 →→即可随意复制了。

3、打开目标网页→→点击目标网页内容→→右键选择网页另存为(s)...Ctrl+S →→打开已经保存好的目标网页→→右键选择切换兼容性模式→→选择兼容性模式为IE11 →→即可随意复制了,使用Ctrl+A选择网页全部文字,Ctrl+C复制,新建文本档案,Ctrl+V粘贴。


其二、网站不让你复制文字?手动输入javascript:void($={}); 这串代码1秒解除限制方法:

1、JavaScripty代码 打开英文键盘输入下列代码。

javascript:void($={}); 注意代码要手动输入,复制粘贴是没有用的哦!代码后面的;输不输入无所谓。

我们进入需要复制文字的网页,然后将地址栏的链接删除,输入这串代码然后按下Enter键,再试试文字就可以复制了哦!

$符号怎么输入:1.在电脑上双击打开WORD进入。2.点击右下角的输入法,将其切换为英文状态。3.此时按住SHIFT,然后连续按压下上方数字4键。

2、浏览器查看源文件

有些浏览器是可以在页面右击,通过查看源文件来找到你需要复制的文字段落,复制完就可以粘贴到你的文档中了哦!缺点是段落很多,不容易找到你需要复制的文字。

3、OCR识别技术

随着科技的发展,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粘贴,删除不需要的文字。(这个方法只适用于网页内容无法选中的网页,有时候会无法复制,这时候就需要用到上面的方法了)