前端新手JS语法笔记2——数据类型和变量声明

217 阅读7分钟

一、数据类型

1.1 概述

JS中的数据类型

7种(大小写无所谓)

  • number 数字
  • string 字符串
  • bool 布尔
  • symbol 符号(用的很少)
  • undefined 空
  • null 空
  • 对象 object
  • 总结:四基两空一对象

以下不是数据类型(都是对象)

  • 数组
  • 函数
  • 日期

数据为什么需要类型?

数字和字符串 都是1,为什么要区分数字1和字符串'1'?

功能不同

  • 数字可以加减乘除,字符串不行
  • 字符串能表示电话号码,数字不行

存储形式不同

  • JS中,数字是64位浮点数的形式存储的
  • JS中,字符串是用类似UTF8形式存储的

1.2 null undefined 和 布尔值

1.2.1 布尔值

只有两个值,真 和 假

下列运算符会得到bool值

  • 两元逻辑运算符: && (And),|| (Or)
  • 否定运算 !value
  • 相等运算 1==2 ,1! = 2 ,3 ===4,3!==4
  • 比较运算 1>2 , 2≤4

如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true。除了false的五个叫做falsy值。

  • undefined
  • null
  • false
  • 0
  • NaN
  • ""''(空字符串)

布尔值往往用于程序流程的控制,经常与if语句配合使用。

if ('') {
  console.log('true');
}
// 没有任何输出

上面代码中,if命令后面的判断条件,预期应该是一个布尔值,所以 JavaScript 自动将空字符串,转为布尔值false,导致程序不会进入代码块,所以没有任何输出。

注意,空数组([])和空对象({})对应的布尔值,都是true

1.2.2 undefined 和 null 两种空类型

没有本质区别,有几个细节

  • 细节1:如果一个变量声明了,但是没有赋值,那么默认值就是undefined,而不是null
  • 细节2:如果一个函数,没有写return,那么默认return undefined,而不是null
  • 细节3:前端程序员习惯上,把非对象的空值写为undefined,把对象的空值写为null,但仅仅是习惯而已,可以不遵守

教程原文:

null表示空值,即该处的值现在为空。调用函数时,某个参数未设置任何值,这时就可以传入null,表示该参数为空。比如,某个函数接受引擎抛出的错误作为参数,如果运行过程中未出错,那么这个参数就会传入null,表示未发生错误。

undefined表示“未定义”,下面是返回undefined的典型场景。

// 变量声明了,但没有赋值
var i;
i // undefined

// 调用函数时,应该提供的参数没有提供,该参数等于 undefined
function f(x) {
  return x;
}
f() // undefined

// 对象没有赋值的属性
var  o = new Object();
o.p // undefined

// 函数没有返回值时,默认返回 undefined
function f() {}
f() // undefined

1.3 数值

1.3.1 整数和浮点数

JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,1与1.0是相同的,是同一个数。这就是说,JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数)。

由于浮点数不是精确的值,所以涉及小数的比较和运算要特别小心。

0.1 + 0.2 === 0.3
// false

0.3 / 0.1
// 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1)
// false

1.3.2 数值精度

JavaScript 浮点数的64个二进制位,也就是64位浮点数,从最左边开始,是这样组成的。

  • 第1位:符号位,0表示正数,1表示负数
  • 第2位到第12位(共11位):指数部分
  • 第13位到第64位(共52位):小数部分(即有效数字)

指数部分一共有11个二进制位,因此大小范围就是0到2047,除以二分给正负,所以是 -1023~1024

指数部分一共有11个二进制位,因此大小范围就是0到2047。IEEE 754 规定,如果指数部分的值在0到2047之间(不含两个端点),那么有效数字的第一位默认总是1,不保存在64位浮点数之中。也就是说,有效数字这时总是1.xx...xx的形式,其中xx..xx的部分保存在64位浮点数之中,最长可能为52位。因此,JavaScript 提供的有效数字最长为53个二进制位。

(-1)^符号位 * 1.xx...xx * 2^指数部分

上面公式是正常情况下(指数部分在0到2047之间),一个数在 JavaScript 内部实际的表示形式。

精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。

Math.pow(2, 53)
// 9007199254740992
 
Math.pow(2, 53) + 1
// 9007199254740992

Math.pow(2, 53) + 2
// 9007199254740994

Math.pow(2, 53) + 3
// 9007199254740996

Math.pow(2, 53) + 4
// 9007199254740996

上面代码中,大于2的53次方以后,整数运算的结果开始出现错误。所以,大于2的53次方的数值,都无法保持精度。由于2的53次方是一个16位的十进制数值,所以简单的法则就是,JavaScript 对15位的十进制数都可以精确处理。

Math.pow(2, 53)
// 9007199254740992

// 多出的三个有效数字,将无法保存
9007199254740992111
// 9007199254740992000

上面示例表明,大于2的53次方以后,多出来的有效数字(最后三位的111)都会无法保存,变成0。

1.3.3 数值范围

根据标准,64位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047(2的11次方减1)。也就是说,64位浮点数的指数部分的值最大为2047,分出一半表示负数,则 JavaScript 能够表示的数值范围为21024到2-1023(开区间),超出这个范围的数无法表示。

如果一个数大于等于2的1024次方,那么就会发生“正向溢出”,即 JavaScript 无法表示这么大的数,这时就会返回Infinity

Math.pow(2, 1024) // Infinity

如果一个数小于等于2的-1075次方(指数部分最小值-1023,再加上小数部分的52位),那么就会发生为“负向溢出”,即 JavaScript 无法表示这么小的数,这时会直接返回0。

Math.pow(2, -1075) // 0

下面是一个实际的例子。

var x = 0.5;

for(var i = 0; i < 25; i++) {
  x = x * x;
}

x // 0

上面代码中,对0.5连续做25次平方,由于最后结果太接近0,超出了可表示的范围,JavaScript 就直接将其转为0。

JavaScript 提供Number对象的MAX_VALUEMIN_VALUE属性,返回可以表示的具体的最大值和最小值。

Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324

1.3.4 数值的表示法

  • 整数 1
  • 小数 0.1
  • 科学计数法 1.23e4 (e4就是乘以10的4次方)
  • 八进制(用得少)0123,00123,0o123
  • 十六进制 0x3F 0X3F (用得少)
  • 二进制 0b11 或0B11 (用得少)

1.3.5 数值的进制

用字面量(literal)直接表示一个数值时,JavaScript 对整数提供四种进制的表示方法:十进制、十六进制、八进制、二进制。

  • 十进制:没有前导0的数值。
  • 八进制:有前缀0o0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。
  • 十六进制:有前缀0x0X的数值。
  • 二进制:有前缀0b0B的数值。

默认情况下,JavaScript 内部会自动将八进制、十六进制、二进制转为十进制。下面是一些例子。

0xff // 255
0o377 // 255
0b11 // 3

如果八进制、十六进制、二进制的数值里面,出现不属于该进制的数字,就会报错。

0xzz // 报错
0o88 // 报错
0b22 // 报错

上面代码中,十六进制出现了字母z、八进制出现数字8、二进制出现数字2,因此报错。

通常来说,有前导0的数值会被视为八进制,但是如果前导0后面有数字89,则该数值被视为十进制。

0888 // 888
0777 // 511

前导0表示八进制,处理时很容易造成混乱。ES5 的严格模式和 ES6,已经废除了这种表示法,但是浏览器为了兼容以前的代码,目前还继续支持这种表示法。

十进制转二进制方法:

  1. 31转成二进制,经过一番尝试发现:31= 02的5次方 + 12的四次方 + 12的3次方 + 12的平方 + 12 + 12的0次方
  2. 所以31(十进制) = 01111(二进制)

二进制转十进制

  1. 100011变成十进制,每一位乘以2的n次方,然后加起来
  2. 100011 = 2的五次方 + 2的1次方 +2的0次方=35

二进制转十六进制

用程序员计算器,或者自己算

1.3.6 特殊数值

  • 正0和负0,都等于0,但是是三个数(1除以-0等于负无穷)
  • 无穷大:Infiniti,+Infiniti,-Infiniti
  • 无法表示的数字:NaN(Not a Number),但他是一个数字 。(比如0除以0,返回NaN) NaN不等于任何值,包括NaN。

1.4 字符串

1.4.1 定义

字符串就是零个或多个排在一起的字符,放在单引号或双引号之中。(以及反引号)

'abc'
"abc"
`abc`

如果想在字符串里回车,那么可以直接用反引号

let s = `这样是
可以的
用反引号很容易做到`

1.4.2 转义

反斜杠(\)在字符串内有特殊含义,用来表示一些特殊字符,所以又称为转义符。

需要用反斜杠转义的特殊字符,主要有下面这些。

  • \0 :null(\u0000
  • \b :后退键(\u0008
  • \f :换页符(\u000C
  • \n :换行符(\u000A
  • \r :回车键(\u000D
  • \t :tab制表符(\u0009
  • \v :垂直制表符(\u000B
  • \' :单引号(\u0027
  • \" :双引号(\u0022
  • \\ :反斜杠(\u005C
  • \xHH : \x后面紧跟两个十六进制数(00FF),代表一个字符。HH对应该字符的 Unicode 码点,比如\xA9表示版权符号。这种方法也只能输出256种字符。
  • \uXXXX: \u后面紧跟四个十六进制数( 0000FFFF),代表一个字符。XXXX 对应该字符的 Unicode 码点,比如\u00A9表示版权符号。

上面这些字符前面加上反斜杠,都表示特殊含义。

1.4.3 字符串与数组

字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)。

var s = 'hello';
s[0] // "h"
s[1] // "e"
s[4] // "o"

// 直接对字符串使用方括号运算符
'hello'[1] // "e"

如果方括号中的数字超过字符串的长度,或者方括号中根本不是数字,则返回undefined

'abc'[3] // undefined
'abc'[-1] // undefined
'abc'['x'] // undefined

但是,字符串与数组的相似性仅此而已。实际上,无法改变字符串之中的单个字符。

var s = 'hello';

delete s[0];
s // "hello"

s[1] = 'a';
s // "hello"

s[5] = '!';
s // "hello"

上面代码表示,字符串内部的单个字符无法改变和增删,这些操作会默默地失败。

1.4.4 length属性

length属性返回字符串的长度,该属性也是无法改变的。

var s = 'hello';
s.length // 5

'\n\r\t'.length //3

''.length// 0

' '.length // 1

上面代码表示字符串的length属性无法改变,但是不会报错。

为什么字符串有属性?学完对象可以解答。

1.4.5 字符集

如何存字符?

用0-127表示所有符号,然后记录下用户点击了哪些符号

中国人开始用电脑了,怎么办?(之前不包含生僻字、繁体、日语、汉语)

中国国家标准局 —— 国标2312

生僻字怎么办?

微软出了GBK

网页里出现了藏文、泰文,怎么办?

万国码Unicode

优点

  • 已收录13万字符(大于16位),全世界通用
  • 以后还会继续扩充

缺点

  • 两个字节不够用,每个字符要三个及以上字节
  • 所有文件都扩大50%,不划算

UTF-8应运而生

UTF-8 是 Unicode 一种存储规则,也叫字符编码规则。

这就是如何存字符的

那就是记录编号,然后存编号

二、变量声明

三种声明方式

  • var a =1 过时,不要再用
  • let a =1 新的
  • const a =1 声明时必须赋值,且不能再改
  • a =1 这种声明方式是错的

let 声明

  • 遵循块作用域,适用范围不能超过{}
  • 不能重复声明
  • 可以赋值,也可以不赋值
  • 必须先声明再使用
  • 全局声明的let变量,不会变成window属性
  • for循环配合let 有奇效

const 声明

  • 跟let规则一样
  • 只有一条不同,声明时就要赋值,赋值后不能改

声明变量的同时,也指定了类型,但是值和类型都可以随时变化

类型转换

number变为string 👇

  • String(n)
  • n + ''

string变为number 👇

  • Number(s)
  • parseInt(s) / parseFloat(s)
  • s - 0

x变为bool 👇

  • VBoolean(x)
  • !!X

x变为string 👇

  • String(x)
  • x.toString()