调试时变量十六进制_JavaScript(1) 变量&类型&运算符
前置知识
JavaScript 的书写形式
1. 行内式
直接嵌入到 html 元素内部
<input type="button" value="点我一下" onclick="alert('haha')">
注意, JS 中字符串常量可以使用单引号表示, 也可以 使用双引号表示.
HTML 中推荐使用双引号, JS 中推荐使用单引号.
2. 内嵌式
写到 script 标签中
<script> alert("haha");script>
3. 外部式
写到单独的 .js 文件中
<script src="hello.js">script>
alert("hehe");
注意, 这种情况下 script 标签中间不能写代码. 必须空着(写了代码也不会执行).
适合代码多的情况.
注释
单行注释 // [建议使用]
多行注释 /* */
// 我是单行注释/* 我是多行注释 我是多行注释 我是多行注释*/
VSCode 中使用 ctrl + / 切换注释.
多行注释不能嵌套. 形如这种代码就会报错
/* /* 我是多行注释 我是多行注释 我是多行注释 */ */
输入输出
输入: prompt
弹出一个输入框
// 弹出一个输入框prompt("请输入您的姓名:");
输出: alert
弹出一个警示对话框, 输出结果
// 弹出一个输出框alert("hello");
输出: console.log
在控制台打印一个日志(供程序员看)
// 向控制台输出日志
console.log("这是一条日志");
注意: 在 VSCode 中直接输入 "log" 再按 tab 键, 就可以快速输入 console.log
需要打开浏览器的开发者工具(F12) => Console 标签页 才能看到结果.
这样的输出一般不是给普通用户看的, 而是程序员来看的.
重要概念: 日志
日志是程序员调试程序的重要手段
去医院看病, 医生会让患者做各种检查, 血常规, 尿常规, B超, CT等... 此时得到一堆检测结果. 这些结果普通人看不懂, 但是医生能看懂, 并且医生要通过这些信息来诊断病情.
这些检测结果就是医生的 "日志"
PS: 相比于医生来说, 程序猿多一个终极大招, "重启下试试".
重要概念: .
console 是一个 js 中的 "对象"
. 表示取对象中的某个属性或者方法. 可以直观理解成 "的"
console.log 就可以理解成: 使用 "控制台" 对象 "的" log 方法.
变量
变量是什么
是保存数据的一种方式, 是把数据保存到内存中.
变量就是一个存放数据的盒子, 可以通过变量名来访问到变量的数据
内存中可以有很多的 "盒子", 就需要通过一定的方式来找到这个变量.
就好像一个酒店中有很多房间, 需要根据房间号码就能找到具体的房间内容.
但是光通过内存编号来找变量比较麻烦(汇编语言就是这么干的), 于是我们可以给变量起一些通俗易懂的名字, 就是变量名.
变量的使用
创建变量(变量定义/变量声明/变量初始化)
var name = 'zhangsan';
var age = 20;
var 是 JS 中的关键字, 表示这是一个变量.
= 在 JS 中表示 "赋值", 相当于把数据放到内存的盒子中. = 两侧建议有一个空格
每个语句最后带有一个 ; 结尾. JS 中可以省略 ; 但是建议还是加上.
注意, 此处的 ; 为英文分号. JS 中所有的标点都是英文标点.
初始化的值如果是字符串, 那么就要使用单引号或者双引号引起来.
初始化的值如果是数字, 那么直接赋值即可.
使用变量
console.log(age); // 读取变量内容
age = 30; // 修改变量内容
为啥动漫中的角色都是要先喊出技能名字再真正释放技能?
就是因为变量要先声明才能使用.
代码示例: 弹框提示用户输入信息, 再弹框显示.
var name = prompt("请输入姓名:");
var age = prompt("请输入年龄:");
var score = prompt("请输入分数");
alert("您的姓名是: " + name);
alert("您的年龄是: " + age);
alert("您的分数是: " + score);
也可以把三个输出内容合并成一次弹框
var name = prompt("请输入姓名:");
var age = prompt("请输入年龄:");
var score = prompt("请输入分数");
alert("您的姓名是: " + name + "\n" + "您的年龄是: " + age + "\n" + "您的分数是: " + score + "\n");
+
表示字符串拼接, 也就是把两个字符串首尾相接变成一个字符串.
\n
表示换行
命名规范
1) 硬性规范: 必须严格遵守, 不遵守就会有语法错误
由字母, 数字, 下划线, $ 美元符 构成.
大小写敏感, hello 和 Hello 是两个不同的变量.
不能以数字开头.
不能是一些关键字. (var, for, if, else, name 等)
注意 name 在 js 中是一个已经被创建的变量了, 不能随便使用. (后面讲到 BOM API 时会具体解释).
2) 软性规范: 建议遵守, 能够提高代码的可读性.
变量名要有意义, 见名知意.
驼峰命名法. userName, countNum 等
变量使用名词词性的词命名, 后面学习函数则使用动词词性命名.
关于变量命名的常见风格:
a) 驼峰命名法(camel case), 形如 userName, countNum; Java, JS, Go 等语言常用用
b) 蛇形命名法(snake case), 形如 user_name, count_num; C, C++, Python 等语言常用.
c) 脊柱命名法(spinal case), 形如 user-name, count-num; CSS 中较常见.
d) 匈牙利命名法 (HN case), 形如 sUserName, iCountNum; 早期的程序常见 (Windows API), 现在少见.
e) 驼峰蛇形命名法, 形如 User_Name, Count_Name; 不太常见.
为啥有这么多种呢? 主要还是历史原因.
注意事项
1) JS 中允许同时创建多个变量
var userName = 'zhangsan', age = 18;
2) 如果创建变量的时候没有初始化值, 结果为 undefined
var sex;
console.log(sex);
输出结果
undefined
undefined 在 JS 中是一个特殊的存在, 表示未定义的.
3) 如果直接使用未定义的变量, 程序会报错
console.log(score);
如果程序执行过程中报错, 则不会继续解释执行后面的代码.
4) 不创建变量而是直接进行赋值, 此时可以正常使用, 但是不推荐这样用. (变量会变成 "全局变量", 后面再讲)
score = 10;
console.log(score);
5) 变量名, 函数名, 类名等统称为 "标识符". 命名规则都是一样的. 不能是关键字或者保留字
关键字: 在 JS 中有特定含义的单词. 这些关键字不用去特意背, 常用的后面都会用到.
break
case
catch
continue
default
delete
do
else
finally
for
function
if
in
instanceof
new
return
switch
this
throw
try
typeof
var
void
while
with
代码示例: 交换两个变量
var a = 10;
var b = 20;
// 创建一个临时变量
var tmp = a;
a = b;
b = tmp;
console.log(a);
console.log(b);
类型
为什么要有类型
计算机能够存储的数据种类是多样的.
1) 不同的变量类型占用的存储空间不同.
如果是一个 10, 100 这样的整数, 在内存中不需要占用多少空间.
如果是一个很长的字符串(表示一篇文章), 就可能需要几 KB 或者 几 MB 的空间.
2) 不同的变量类型支持的运算(操作存在差异)
数字能进行加减乘除
字符串则不能.
注意: 无论是啥类型的变量, 在内存中都是按照二进制的方式来存储的.
理解 动态类型
1) JS 的变量类型是程序运行过程中才确定的(运行到 = 语句才会确定类型)
var a = 10; // 数字
var b = "hehe"; // 字符串
2) 随着程序运行, 变量的类型可能会发生改变.
var a = 10; // 数字
a = "hehe"; // 字符串
这一点和 C Java 这种静态类型语言差异较大.
C, C++, Java, Go 等语言是静态类型语言. 一个变量在创建的时候类型就确定了, 不能在运行时发生改变.
如果尝试改变, 就会直接编译报错.
基本数据类型
JS 中内置的几种类型
number: 数字. 不区分整数和小数.
boolean: true 真, false 假.
string: 字符串类型.
undefined: 只有唯一的值 undefined. 表示未定义的值.
null: 只有唯一的值 null. 表示空值.
使用 typeof 检测变量的类型
typeof 是一个关键字. 可以用于获取变量的类型
typeof 变量
var a = 10;
console.log(typeof a);
var b = 'hehe';
console.log(typeof b);
var c = true;
console.log(typeof c);
var d = undefined;
console.log(typeof d);
var e = null;
console.log(typeof e);
结果
number
string
boolean
undefined
object
注意, null 的类型为 object. 这其实是一个设计时候的 Bug. (按理说要有一个专门的 null 类型).
后来指定 JS 标准的大佬们专门讨论过是否要修复这个 Bug, 但是为了保证兼容性, 决定还是将错就错下去.
var f = prompt("请输入年龄: ");
console.log(typeof f); // string
注意: prompt 返回的结果类型是 string
number 数字类型
JS 中不区分整数和浮点数, 统一都使用 "数字类型" 来表示.
数字进制表示
计算机中都是使用二进制来表示数据, 而人平时都是使用十进制.
因为二进制在使用过程中不太方便(01太多会看花眼).
所以在日常使用二进制数字时往往使用 八进制 和 十六进制 来表示二进制数字.
var a = 07; // 八进制整数, 以 0 开头
var b = 0xa; // 十六进制整数, 以 0x 开头
var c = 0b10; // 二进制整数, 以 0b 开头
注意:
一个八进制数字对应三个二进制数字
一个十六进制数字对应四个二进制数字. (两个十六进制数字就是一个字节)
各种进制之间的转换, 不需要手工计算, 直接使用计算器即可.
科学计数法
表示一些特别大或者特别小的数字时就会用到科学计数法
var max = Number.MAX_VALUE;
console.log(max);
var min = Number.MIN_VALUE;
console.log(min)
1.7976931348623157e+308
5e-324
其中 e 表示以 10 为底, 后面的数字表示 10 的多少次幂.
var num = 1.5e4;
console.log(num);
15000
特殊的数字值
Infinity: 无穷大, 大于任何数字. 表示数字已经超过了 JS 能表示的范围.
-Infinity: 负无穷大, 小于任何数字. 表示数字已经超过了 JS 能表示的范围.
NaN: 表示当前的结果不是一个数字.
var max = Number.MAX_VALUE;
// 得到 Infinity
console.log(max * 2);
// 得到 -Infinity
console.log(-max * 2);
// 得到 NaN
console.log('hehe' - 10);
注意:
负无穷大 和 无穷小 不是一回事. 无穷小指无限趋近与 0, 值为
1 / Infinity
'hehe' + 10 得到的不是 NaN, 而是 'hehe10', 会把数字隐式转成字符串, 再进行字符串拼接.
可以使用 isNaN 函数判定是不是一个非数字.
console.log(isNaN(10)); // false
console.log(isNaN('hehe' - 10)); // true
string 字符串类型
基本规则
字符串字面值需要使用引号引起来, 单引号双引号均可.
var a = "haha";
var b = 'hehe';
var c = hehe; // 运行出错
如果字符串中本来已经包含引号咋办?
var msg = "My name is "zhangsan""; // 出错
var msg = "My name is \"zhangsan\""; // 正确, 使用转义字符. \" 来表示字符串内部的引号.
var msg = "My name is 'zhangsan'"; // 正确, 搭配使用单双引号
var msg = 'My name is "zhangsan"'; // 正确, 搭配使用单双引号
转义字符
有些字符不方便直接输入, 于是要通过一些特殊方式来表示.
\n
\\
\'
\"
\t
求长度
使用 String 的 length 属性即可
var a = 'hehe';
console.log(a.length);
var b = '哈哈';
console.log(b.length);
结果:
4
2
单位为字符的数量
理解 字节 vs 字符
一个字节就是 8 个二进制位.
一个字符可能包含多个字节.
如英文字母, 阿拉伯数字这种字符可能是由一个字节表示的(参考 ASCII 码表)
如汉字, 则是由多个字节表示的(具体表示方式取决于实际的编码方式).
字符串拼接
使用 + 进行拼接
var a = "my name is ";
var b = "zhangsan";
console.log(a + b);
注意, 数字和字符串也可以进行拼接
var c = "my score is ";
var d = 100;
console.log(c + d);
注意, 要认准相加的变量到底是字符串还是数字
console.log(100 + 100); // 200
console.log('100' + 100); // 100100
boolean 布尔类型
表示 "真" 和 "假"
boolean 原本是数学中的概念 (布尔代数).
在计算机中 boolean 意义重大, 往往要搭配条件/循环完成逻辑判断.
Boolean 参与运算时当做 1 和 0 来看待.
console.log(true + 1);
console.log(false + 1)
这样的操作其实是不科学的. 实际开发中不应该这么写.
undefined 未定义数据类型
如果一个变量没有被初始化过, 结果就是 undefined, 是 undefined 类型
var a;
console.log(a)
undefined 和字符串进行相加, 结果进行字符串拼接
console.log(a + "10"); // undefined10
undefined 和数字进行相加, 结果为 NaN
console.log(a + 10);
null 空值类型
null 表示当前的变量是一个 "空值".
var b = null;
console.log(b + 10); // 10
console.log(b + "10"); // null10
注意:
null 和 undefined 都表示取值非法的情况, 但是侧重点不同.
null 表示当前的值为空. (相当于有一个空的盒子)
undefined 表示当前的变量未定义. (相当于连盒子都没有)
数据类型转换
转为字符串
toString
String()
+
拼接 [重要]
var num = 10;
var a = num.toString();
console.log(typeof a);
var b = String(a);
console.log(typeof b);
var c = "" + num;
console.log(typeof c);
此处的 + 操作称为 "隐式类型转换"
转为数字
parseInt
parseFloat
Number
隐式转换 ( - * /)
var str = "10";
var a = parseInt(str);
console.log(typeof a);
var b = parseFloat(str);
console.log(typeof b);
var c = Number(str);
console.log(typeof c);
var d = str - 0;
console.log(typeof d);
如果针对一个小数进行调用 parseInt, 则会直接截断, 而不会四舍五入
var str = "10.9"
parseInt(str); // 结果为 10
如果数字后面带单位, 转成整数时会自动忽略末尾的单位.
console.log(parseInt("1000px")); // 1000
但是如果数字前面带字母, 则会失败
console.log(parseInt("aaa1000"));
案例: 两个数相加
注意类型转换.
var a = prompt("请输入第一个数字: ");
var b = prompt("请输入第二个数字: ");
var result = parseFloat(a) + parseFloat(b);
alert("结果为: " + result);
计算相加的时候, 因为 String 也能加, 所以执行字符串拼接操作.
计算相减的时候,因为 String 不能相减, 就会隐式转换成 Number, 再相减.
转为布尔类型
Boolean()
空值被转成 false, 其余都是 true
空值指的是: '', 0, NaN, null, undefined
console.log(Boolean(''));
console.log(Boolean(0));
console.log(Boolean(NaN));
console.log(Boolean(undefined));
console.log(Boolean(null));
类型小结
JS 是一个动态弱类型的编程语言.
动态指的是类型可以在运行时发生改变
弱类型指的是允许隐式类型转换.
动态类型不见得是好事. 所以才有后来的 TypeScript
运算符
也叫做操作符. 用于完成一些基本运算
算术运算符
+
-
*
/
%
var a = 10;
var b = 20;
var result1 = a + b;
var result2 = a - b;
var result3 = a * b;
var result4 = a / b;
var result5 = a % b;
console.log(result1);
console.log(result2);
console.log(result3);
console.log(result4);
console.log(result5);
注意:
和 C 语言不通, 整数除以整数, 结果是小数.
浮点数存储存在精度问题, 不能针对浮点数进行比较.
console.log(1.1*1.1) // 1.2100000000000002
console.log(1.1 * 1.1 == 1.21) // false
精度问题源自于浮点数在计算机中的存储方式. JS 按照 IEEE 574 标准的约定来存储浮点数.
当把浮点数转成二进制表示的时候, 就可能出现误差.
重要概念: 表达式
把包含变量以及若干运算符构成的一个语句就称为表达式.
表达式的计算结果称为 "返回值"
形如: 1 + 1
1 * 10
1+2*3
等.
赋值运算符 & 复合赋值运算符
=
+=
-=
*=
/=
%=
赋值运算符我们已经用过很多次了.
注意区分 "初始化" 和 "赋值"
初始化: 变量之前没定义过, 创建变量的过程.
赋值: 变量已经被创建了, 修改了变量的值.
var num = 10; // 初始化
num = 20; // 赋值
这就好比本来你是单身, 后来谈了个女盆友, 这个就是 "初始化".
后来分手了, 又谈了个新的, 相当于本来有女盆友又换了一个, 就是 "赋值".
在 JS 中, 使用 var 声明的变量可以被初始化多次.
var num = 10;
var num = 20; // 不会报错, 相当于赋值.
这样的操作本质上是不科学的, 在 ES6 中引入的 let 限制了这个问题.
复合赋值运算本质上是一种简化写法.
var a = 10;
var b = 20;
// 等价于 a = a + b;
a += b;
console.log(a); // 结果为 30
自增自减运算符
++: 自增1
--: 自减1
注意: 放在变量之前, 表示先进行自增, 再返回结果; 放在变量之后, 表示先返回结果, 然后再自增.
var num = 10;
// var result = ++num;
var result = num++;
console.log("num: " + num);
console.log("result: " + result)
如果不获取前置自增或者后置自增的返回值, 那么效果是等价的.
事实上由于这俩东西太容易混淆, 像 Python 和 Go 这样的语言都废除了这样的写法.
一般我们不建议写出形如这样的代码:
var result = num++ + 10;
var result2 = num++ + num++ + num++'
写代码是为了给人看的. 尤其是写给三个月后的自己看的. 不要给自己挖坑.
比较运算符
<
>
<=
>=
==
比较相等(会进行隐式类型转换)!=
===
比较相等(不会进行隐式类型转换)!==
所有比较运算符的表达式返回值都是 Boolean.
console.log(3 < 5); // true
console.log(3 > 5); // false
console.log(3 <= 3); // true
console.log(3 >= 3); // true
console.log(3 == 3); // true
console.log(3 != 3); // false
console.log(3 == "3"); // true
console.log(3 === "3"); // false
注意体会 == 和 === 直接的区别.
使用 == 比较数字和字符串的时候, 会隐式把字符串转成数字, 再比较.
使用 === 比较的时候, 不会隐式转换. 如果类型不相同, 就直接认为是 false.
逻辑运算符
用于计算多个 boolean 表达式的值.
&& 与: 一假则假
|| 或: 一真则真
! 非
console.log(3 < 5 && 1 < 2); // true
console.log(3 < 5 || 1 > 2); // true
console.log(!1 < 2); // false
短路求值
当多个表达式进行逻辑运算时, 如果左侧表达式已经可以确定最终结果, 则不计算右侧表达式的值.
对于逻辑与来说, 如果左侧表达式的值为 真, 则返回表达式2 ; 如果左侧表达式为假, 则不对右侧表达式求值.
对于逻辑或来说, 如果左侧表达式值为假, 则返回表达式2; 如果左侧表达式为真, 则不对右侧表达式求值.
console.log(123 && 456); // 456
console.log(0 && 123); // 0
console.log(0 || 456); // 456
console.log(123 || 456); // 123
短路求值练习
var a = 0;
var b = 1;
var c = 2;
console.log(a++ && b++ && c++); // 1, 1, 2
// console.log(a++ || b++ || c++); // 1, 2, 2
console.log(a + ", " + b + ", " + c);
短路求值这样的语法是一个比较常见的语法, 很多编程语言都遵守.
位运算 (选学)
& 按位与
| 按位或
~ 按位取反
^ 按位异或
var a = 10;
var b = 20;
console.log((a & b).toString(2)); // 0
console.log((a | b).toString(2)); // 11110
console.log((a ^ b).toString(2)); // 11110
console.log((~a).toString(2)); // -1011
注意: JS 中的数字都是按照 64 位浮点数的方式来存的. 进行位运算的时候会先被转成 32 位整数(补码表示), 再参与运算. (超过32位的数字会被丢弃)
参考 MDN:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
移位运算 (选学)
按位移动会先将操作数转换为大端字节序顺序(big-endian order)的32位整数,并返回与左操作数相同类型的结果。右操作数应小于 32位,否则只有最低 5 个字节会被使用
<< 左移
>>
有符号右移(算术右移)>>>
无符号右移(逻辑右移)
1) 左移: 该操作符会将第一个操作数向左移动指定的位数。向左被移出的位被丢弃,右侧用 0 补充
9 (base 10): 00000000000000000000000000001001 (base 2) --------------------------------9 << 2 (base 10): 00000000000000000000000000100100 (base 2) = 36 (base 10)
2) 有符号右移
9 (base 10): 00000000000000000000000000001001 (base 2) --------------------------------9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10) -9 (base 10): 11111111111111111111111111110111 (base 2) ---------------------------------9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)
3) 无符号右移
9 (base 10): 00000000000000000000000000001001 (base 2) --------------------------------9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10) -9 (base 10): 11111111111111111111111111110111 (base 2) ---------------------------------9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)
注意: 左移一位相当于 * 2, 有符号右移一位相当于 / 2
参考 MDN:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
关于位运算和移位运算, 对于新手来说是相当不友好的, 需要熟练掌握二进制计算规则以及数据在内存中的存储规则才能理解清楚. 这部分内容只是在特定场景会用到, 大家有个印象即可.
运算符优先级
参考 MDN, 上面有一个详细的列表
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
注意: 运算符优先级千万别背. 实际使用的时候把运算符优先级搞不准的就多加括号即可.
实际开发时也不会写特别复杂的表达式.
上一篇: Java实现的日期处理类完整实例
下一篇: Android实现自定义圆形进度条