JS 变量声明和数据类型的总结及需要注意的细节

在本篇文章中,将阅读到的内容

  1. 声明变量的常用命令
  2. 数据类型的概述以及判断的方法
  3. null,undefined 和布尔值
  4. 数值
  5. 字符串
  6. 对象
  7. 函数
  8. 数组
  9. symbol

1. 声明变量的常用命令

在ES5中声明变量的方法只有两种:var命令和function命令
在ES6中添加了四种:let、const、import和class

var let const这三种声明变量的方法,经常进行比较
在这里插入图片描述
上述名词解释:

变量提升:var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

暂时性死区:ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。在代码块内,使用let或者const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

关于const,还有一个需要理解的点是const声明的变量是否可以改变

2. 数据类型的概述以及判断的方法

本节内容:
2.1 类型概述
2.2 判断方法

2.1 类型概述
在这里插入图片描述
基本类型由于其占据空间固定,是简单的数据段为了便于提升变量查询速度,因为需要频繁使用,所以将其存储在中,即按值访问

引用类型由于其值的大小会改变,所以不能将其存放在栈中,否则会降低变量查询速度,因此,将指向中该实体的起始地址的指针存储在栈中,在解释器寻找引用值的时候,会首先检索在栈中的地址,取得地址之后从堆中获取实体。引用类型除 Object 外,还包括 Function 、Array、RegExp、Date 等等。

2.2 判断方法

2.2.1 方法一: typeof

typeof是一个用于判断类型的一元操作符。

使用方法:

typeof []

在这里插入图片描述
在这里插入图片描述

2.2.2 方法二:instanceof

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype属性。其意思就是判断对象是否是某一数据类型的实例。

使用方法:

object instanceof constructor
console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true   
console.log([] instanceof Object);                    // true

instanceof只能正确判断引用数据类型,但是不能判断基本数据类型。

null和undefined进行判断的时候会报错。

注意console.log([] instanceof Object); // true进行判断的时候也是true。

2.2.3 方法三:constructor

constructor有两个作用,一个是判断数据类型,二是对象实例通过constructor对象访问其构造函数。

使用方法:

console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true

但是需要注意的点,如果一个对象改变了其原型,constructor就不能用来判断数据类型了。

2.2.4 Object.prototype.toString.call()

使用Object对象的原型方法toString来判断数据类型。

var a = Object.prototype.toString;
 
console.log(a.call(2));
console.log(a.call(true));
console.log(a.call('str'));
console.log(a.call([]));
console.log(a.call(function(){}));
console.log(a.call({}));
console.log(a.call(undefined));
console.log(a.call(null));

注意这里需要调用原型上的方法,不然array function等作为Object的实例,其实都已经对toString进行了重写。

2.2.5 判断数组的方式的总结

(1)Object.prototype.toString.call()

Object.prototype.toString.call([])=== 'Array';

(2)原型链

obj.__proto__ === Array.prototype;

(3)Array.isArray()

Array.isArrray(obj);

(4)instanceof

obj instanceof Array

3. null,undefined 和布尔值

本节内容:
3.1 null && undefined
3.2 布尔值

3.1 null && undefined

null和undefined都可以表示‘没有’。在if语句中都会被转为false。

undefined == null
// true
undefined === null
// false

null是一个表示“空”的对象,转为数值时为0;undefined是一个表示"此处无定义"的原始值,转为数值时为NaN。

Number(null) // 0
5 + null // 5

Number(undefined) // NaN
5 + undefined // NaN

二者的使用场景:

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

3.2 布尔值

undefined
null
false
0
NaN
""或’’(空字符串)

在if中都会被转为false。

4. 数值

本节内容
4.1 数值存储的底层原理
4.2 数值的范围
4.3 数值的表示方式和进制表示
4.4 正零和负零
4.5 NaN
4.6 常用关于数值的方法

4.1 数值存储的底层原理

JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,1与1.0是相同的,是同一个数。

1 === 1.0 // true

JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数)。容易造成混淆的是,某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,转成32位整数,然后再进行运算。是通过二进制的方式进行存储的,然后再转化为十进制数。

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

let n1 = 0.1, n2 = 0.2
console.log(n1 + n2)  // 0.30000000000000004

0.1 + 0.2 === 0.3
// false

0.3 / 0.1
// 2.9999999999999996

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

可以使用toFixed(num) 可以把Number四舍五入为指定小数位数的数字。

4.2 数值的范围

2的1024次方到2-1023次方(开区间)

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

Math.pow(2, 1024) // Infinity

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

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

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

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

4.3 数值的表示方式和进制表示

方法一: 科学计数法

以下两种情况,JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。

1)小数点前的数字多于21位。

1234567890123456789012
// 1.2345678901234568e+21

123456789012345678901
// 1234567890123456800002)小数点后的零多于5个。

// 小数点后紧跟5个以上的零,
// 就自动转为科学计数法
0.0000003 // 3e-7

// 否则,就保持原来的字面形式
0.000003 // 0.000003

方法二: 进制

JavaScript 对整数提供四种进制的表示方法:十进制、十六进制、八进制、二进制。

十进制:没有前导0的数值。
八进制:有前缀0o或0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。
十六进制:有前缀0x或0X的数值。
二进制:有前缀0b或0B的数值。

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

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

4.4 正零和负零
avaScript 内部实际上存在2个0:一个是+0,一个是-0,区别就是64位浮点数表示法的符号位不同。它们是等价的。

-0 === +0 // true
0 === -0 // true
0 === +0 // true

几乎所有场合,正零和负零都会被当作正常的0。

+0 // 0
-0 // 0
(-0).toString() // '0'
(+0).toString() // '0'

唯一有区别的场合是,+0或-0当作分母,返回的值是不相等的。

(1 / +0) === (1 / -0) // false

上面的代码之所以出现这样结果,是因为除以正零得到+Infinity,除以负零得到-Infinity,这两者是不相等的。

4.5 NaN

表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。

需要注意的是,NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number,使用typeof运算符可以看得很清楚。

typeof NaN // 'number’

NaN不等于任何值,包括它本身。

NaN == NaN // false
NaN === NaN // false

NaN在布尔运算时被当作false。

Boolean(NaN) // false

NaN与任何数(包括它自己)的运算,得到的都是NaN。

NaN + 32 // NaN
NaN - 32 // NaN
NaN * 32 // NaN
NaN / 32 // NaN

4.6 常用关于数值的方法

1.parseInt()
用法一:用于将字符串转为整数

parseInt('123') // 123 

parseInt('   81') // 81 自动去除空格

字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分。
parseInt('8a') // 8
parseInt('12**') // 12
parseInt('12.34') // 12

parseInt('abc') // NaN

用法二: 进制转换

parseInt('1000') // 1000
// 等同于
parseInt('1000', 10) // 1000

parseInt('1000', 2) // 8
parseInt('1000', 6) // 216
parseInt('1000', 8) // 512

2.parseFloat()
parseFloat方法用于将一个字符串转为浮点数。

3.isNaN()
用于判断一个值是否为NaN

4.isFinite()
isFinite方法返回一个布尔值,表示某个值是否为正常的数值。

更多有关于数值的操作参考这里

5. 字符串

JavaScript 使用 Unicode 字符集。JavaScript 引擎内部,所有字符都用 Unicode 表示。

有时,文本里面包含一些不可打印的符号,比如 ASCII 码0到31的符号都无法打印出来,这时可以使用 Base64 编码,将它们转成可以打印的字符。另一个场景是,有时需要以文本格式传递二进制数据,那么也可以使用 Base64 编码。

所谓 Base64 就是一种编码方法,可以将任意值转成 0~9、A~Z、a-z、+和/这64个字符组成的可打印字符。使用它的主要目的,不是为了加密,而是为了不出现特殊字符,简化程序的处理。

JavaScript 原生提供两个 Base64 相关的方法。

btoa():任意值转为 Base64 编码
atob():Base64 编码转为原来的值
var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"

es6中关于字符串的扩展
es6中字符串的新增方法

6. 对象

本节内容:
6.1 对象的定义
6.2 对象属性的操作

6.1 对象的定义

对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。

var obj = {
  foo: 'Hello',
  bar: 'World'
};

6.2 对象属性的操作

  1. 属性的读取
var obj = {
  p: 'Hello World'
};

obj.p // "Hello World"
obj['p'] // "Hello World"
  1. 属性的赋值
    点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。
var obj = {};

obj.foo = 'Hello';
obj['bar'] = 'World';
  1. 属性的查看
    查看一个对象本身的所有属性,可以使用Object.keys方法。
var obj = {
  key1: 1,
  key2: 2
};

Object.keys(obj);
// ['key1', 'key2']
  1. 属性的删除
    delete命令用于删除对象的属性,删除成功后返回true。
var obj = { p: 1 };
Object.keys(obj) // ["p"]

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []
  1. 查看属性是否存在
    in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false。它的左边是一个字符串,表示属性名,右边是一个对象。
var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true 是继承属性 所以返回true
  1. 属性的遍历

for…in循环用来遍历一个对象的全部属性。

var obj = {a: 1, b: 2, c: 3};

for (var i in obj) {
  console.log('键名:', i);
  console.log('键值:', obj[i]);
}
// 键名: a
// 键值: 1
// 键名: b
// 键值: 2
// 键名: c
// 键值: 3

for…in循环有两个使用注意点。

它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
它不仅遍历对象自身的属性,还遍历继承的属性。
举例来说,对象都继承了toString属性,但是for…in循环不会遍历到这个属性。

var obj = {};

// toString 属性是存在的
obj.toString // toString() { [native code] }

for (var p in obj) {
  console.log(p);
} // 没有任何输出

上面代码中,对象obj继承了toString属性,该属性不会被for…in循环遍历到,因为它默认是“不可遍历”的。

7. 函数

本节内容:
7.1 函数的声明
7.2 arguments对象
7.3 闭包
7.4 立即调用的函数表达式(IIFE)

7.1 函数的声明方式:(三种)

方法一:function 命令

function print(s) {
  console.log(s);
}

方法二:函数表达式

var print = function(s) {
  console.log(s);
};

方式三:Function 构造函数

var add = new Function(
  'x',
  'y',
  'return x + y'
);

// 等同于
function add(x, y) {
  return x + y;
}

7.2 arguments对象

由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。

arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。

var f = function (one) {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
}

f(1, 2, 3)
// 1
// 2
// 3

正常模式下,arguments对象可以在运行时修改。

var f = function(a, b) {
  arguments[0] = 3;
  arguments[1] = 2;
  return a + b;
}

f(1, 1) // 5

需要注意的是,虽然arguments很像数组,但它是一个对象。数组专有的方法(比如slice和forEach),不能在arguments对象上直接使用。

7.3 闭包
闭包是一个函数的很重要的知识点,根据这篇文章好好理解

7.4 立即调用的函数表达式(IIFE)

根据 JavaScript 的语法,圆括号()跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。

有时,我们需要在定义函数之后,立即调用该函数。这时,你不能在函数的定义之后加上圆括号,这会产生语法错误。

function(){ /* code */ }();
// SyntaxError: Unexpected token (
产生这个错误的原因是,function这个关键字既可以当作语句,也可以当作表达式。

// 语句
function f() {}

// 表达式
var f = function f() {}
当作表达式时,函数可以定义后直接加圆括号调用。

var f = function f(){ return 1}();
f // 1

更多关于函数的内容

8. 数组

数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。

9. symbol

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

let s = Symbol();

typeof s
// "symbol"

注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

上面代码中,s1和s2是两个 Symbol 值。如果不加参数,它们在控制台的输出都是Symbol(),不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。

如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。

const obj = {
  toString() {
    return 'abc';
  }
};
const sym = Symbol(obj);
sym // Symbol(abc)

注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();

s1 === s2 // false

// 有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');

s1 === s2 // false

上面代码中,s1和s2都是Symbol函数的返回值,而且参数相同,但是它们是不相等的。

Symbol 值不能与其他类型的值进行运算,会报错。

let sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string

更多symbol内容参考这里

参考文章:
https://blog.csdn.net/saucxs/article/details/97236025
https://wangdoc.com/javascript/types/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值