文章内容主要是梳理JavaScript,总结一些前端基础知识,所以做个记录。
数据类型
JavaScript数据类型分为\textrm\color{red}{基本数据类型}和\textrm\color{red}{引用数据类型}
基本数据类型
目前有\textrm\color{red}{7}种基本数据类型;
- Null
- Undefined
- Number
- Boolean
- String
- Symbol (es6)
- Bigint (es10)
引用数据类型
在JavaScript里,我们常用的\textrm\color{red}{Function、Array、Date、RegExp、包装类型} 等都属于对象类型
- Object
堆栈以及数据存放
数据堆栈
在 JavaScript 中,每一个数据都需要有它所对应的内存空间。而内存空间主要分为两种:栈内存与堆内存。
- 基本数据类型存储在栈内存中,因为它的占用空间小、存储大小固定、运行效率高。
- 引用数据类型存储在堆内存中,因为它占据空间大、存储大小不固定、运行效率比栈内存低。
var a = 'abc'
var b = 10
var person = {
name : 'xiaoming',
age : 18
}
如例所示,变量a、b都是基本数据类型,存放在栈内存中。变量person是引用类型,它的值存放在堆内存中,
同时这个值的应用地址会保存 在栈内存中,用来引用这个对象。
赋值与传递
因为保存基本数据类型和引用数据类型的方式不同,所以从一个变量向另一个变量复制基本数据类型值和引用数据类型值时,也存在不同。
- 对于基本数据类型的值而言,我们总是通过值复制的方式来赋值和传递。
- 对于引用数据类型而言,我们总是通过引用复制的方式来赋值和传递。
var a = 'abc'
var b = 10
var c = b
b = 11
console.log(a,b,c) // abc 11 10
var person = {
age : 18
}
var person1 = person
person.age = 81
console.log(person.age, person1.age) // 81 81
如例所示:
10 是一个基本数据类型的值,变量 b 持有该值的一个副本,当我们将变量 b 的值赋给变量 c 时,
实际上是为变量c重新开辟一块栈内存空间,并且变量c也持有该值的一个副本,两个变量指向的内存
空间完全独立,互不影响。person是引用数据类型,它的值存放在堆内存中,当我们将变量person
赋值给person1时,实际上是复制了person的引用地址,此时person和person1都是指向同一块堆
内存空间。当代码执行person.age = 81 时,实际上时修改了引地址所指向的堆内存中的值,所以
输出的person的值也发生了变化。
数据类型判断
在 Javascript 中,我们判断数据类型常用的有三种方法 typeof instanceof Object.prototype.toString.call()
typeof
\textrm\color{red}{typeof}通常用来判断基本数据类型,对于对象类型除函数外都会判定为 \textrm\color{red}{object} ;对于\textrm\color{red}{null}是历史遗留问题。
typeof '123' // string
typeof 123 // number
typeof true // boolean
typeof undefined // undefined
typeof Symbol(1) // symbol
typeof BigInt(1) // bigint
typeof Function // function
typeof null // object
typeof [] // object
typeof {} // object
typeof new Date() // object
instanceof
\textrm\color{red}{instanceof}通常用来判断引用数据类型,判断一个变量是否为某个个对象的实例,是通过__proto__(隐式原型链一层一层往上找,能否找到对应构造函数的prototype所指向的原型对象)。
[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true
null instanceof Object // true
new Date() instanceof Object // true
function Person(){}
new Person() instanceof Person // true
Object.prototype.toString.call
\textrm\color{red}{Object.prototype.toString.call}通常用来判断JS原生数据类型,toString 是 Object 原型对象上的方法,如果 toString 方法没有重写的话,会返回 [Object type],其中 type 为对象的类型。
1、判断基本类型
Object.prototype.toString.call("字符串") // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(BigInt(1)) // "[object BigInt]"
2、判断原生引用类型
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call(new Date()) // "[object Date]"
Object.prototype.toString.call([]) // "[object Array]"
...
3、判断非原生对象
function Person(person) {
this.name = person.name
this.age = person.age
}
const person = new Person({age:18,name:'aa'})
Object.prototype.toString.call(person) // "[object Object]"
Object.prototype.toString.call很明显无法准确判断person为Person类的实例对象,所以不推荐
使用此方式判断非原生对象,非原生对象推荐使用instanceof来判断
constructor
\textrm\color{red}{constructor}除了null和undefined,既可以用来检测js的基本数据类型,也可以用来检测引用数据类型,前提是引用类型的原型不能修改。
"1".constructor === String // true
(1).constructor === Number // true
(false).constructor === Boolean // true
(new Date()).constructor === Date // true
({name:'test'}).constructor === Object // true
(function(){}).constructor === Function // true
([]).constructor === Array // true
// 改变原型后 constructor 无法正确检查数据类型
function Person(person) {
this.name = person.name
this.age = person.age
}
new Person({age:18,name:'aa'}).constructor === Person // true
function Creature(){}
Person.prototype = new Creature()
new Person({age:18,name:'aa'}).constructor === Creature // true
数据类型转换
显式类型转换
通过手动方式对数据类型进行转换,常用转换函数如下:
- 字符串类型:\textrm\color{red}{String(Object) 、 toString(radix)}
- 数值类型:\textrm\color{red}{Number(Object) 、 parseInt(string, radix)、 parseFloat(string)}
- Boolean类型:\textrm\color{red}{Boolean(object) }
\textrm\color{red}{toString(radix)} 转换规则:
- 在Javascript中,除undefined和null之外的所有类型的值都具有toString()方法。
对象 结果 String 返回对应的字符串 Number 返回数字的字符串 Boolean 如果Boolean为true,则返回"true",反之,"false" Function 返回函数声明的代码解构字符串,如: “function test( ) { [native code] }” Array 将Array元素使用逗号分割的方式拼接起来,如:[1,2,3] ==》"1,2,3" Date 返回可读标准日期和时间的字符串 RegExp 返回正则表达式直接量的字符串 Error 返回一个包含相关错误信息的字符串
\textrm\color{red}{String(mix)} 转换规则:
- mix为null,则返回字符串"null"
- mix为undefined,则返回字符串"undefined"
- 如果对象具有toString(),调用toString方法并且返回值为原始值,Javascript将这个值转换为字符串; 如果对象没有toString方法,并且返回值非原始值则调用valueOf方法,遵循前面规则转换并返回;如果toString或valueOf无法返回原始值,Javascript抛出异常。注:除undefined和null之外的所有类型的值都具有toString()方法。
- 日期对象直接调用toString()转换为字符串,不会调用valueOf
\textrm\color{red}{Number(mix)}
- 如果mix为Boolean类型,则true返回1,false返回0
- 如果mix为Number类型,则返回本身
- 如果mix为undefined,则返回 NaN
- 如果mix为纯数字字符串,则返回数字
- 如果min为空数组,则返回0
- 如果mix为空字符串,则返回0
- 如果mix为非数字、空字符串,则返回NaN
- 如果minx为对象,对象具有valueOf方法,则调用这个方法;没有则调用toString方法,遵循前面规则转换并返回;否则Javascript抛出异常。
\textrm\color{red}{parseInt(string, radix)}
- parseInt()会忽略string前面的空格,如果string不是数字字符或者负号,parseInt()就会返回NaN。
- parseInt()从string第一个字符开始解析,直到遇到非数字字符或解析完成,则返回解析成功的数字字符,如:“123abc”,会返回123,abc会被忽略;“12.5”,会返回12,.5会被忽略。
- 当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数,如果 string 以 "0x" 开头,且后跟数字字符,就会 将其当作一个十六进制整数;如果字符串以“0”开头且后跟数字字符,则根据ES版本将其当作一个八进制数(小于ECMAScript 5)或十进制数来解析。
\textrm\color{red}{parseFloat(string)}
- parseFloat()会忽略string前面的空格,如果string不是数字字符或者负号,parseFloat()就会返回NaN。
- parseFloat()从string第一个字符开始解析,直到遇到非数字字符或解析完成,则返回解析成功的数字字符,string字符串中的第一个小数点是有效的
- 只能解析十进制数据
\textrm\color{red}{Boolean(object)}
- 转换为false的数据包括:false、0、NaN、null、undefined、""
- 其他都为true
隐式类型转换
在js中,当运算符在进行运算时,如果两边数据类型不统一,Javascript编译器会自动将运算符两边的数据做一个数据类型转换,转成相同数据类型再进行计算;这种无需手动转换,由编译器自动转换的方式就称为隐式转换。
-
算术运算符:\textrm\color{red}{ +(加法) -(减法) *(乘法) /(除法) \%(取余) ++(递增) - -(递减)}
- "++"、"--"、"/"、"%"、"*"转换规则
- 如果操作数不是数字类型,则隐式调用Number()函数转换后在做计算
- 如果操作数无法转换为数字类型,则将操作数的值设为NaN
- "+"加法运算符转换规则
- 如果两个操作数都是字符串,则将字符串进行拼接
- 如果两个操作数中有一个是字符串,则将另外操作数转换为字符串在进行拼接
- 如果两个操作数都是数字,则进行算术加法运算
- 如果两个操作数既不是字符串也不是数字,则隐式调用Number()函数转换后在计算
- "++"、"--"、"/"、"%"、"*"转换规则
-
比较运算符:\textrm\color{red}{ >(大于) <(小于) >=(大于等于) <=(小于等于) }
- 如果两个操作数都是数字类型,则进行数值比较
- 如果两个操作数都是字符串,则将字符串的字符编码值转换为数字后进行比较
- 如果其中一个操作数是数字类型,则将另一个操作数转换为数字类型后进行比较
- 如果其中一个操作数是布尔类型类型,则转换为数字类型后按上面规则进行比较
- 如果操作数是对象,则按照对象转换规则,先调用valueOf()转换,如果valueof()返回的结果是原始值,则使用返回数据进行比较,否则在调用tostring()函数的返回结果进行比较
- NaN无法和任何操作数进行比较,包括NaN
/* 如果其中一个操作数是数字类型,则将另一个操作数转换为数字类型后进行比较 * 1、Number(true) = 1, * 2、1 >= 1 返回 true */ console.log(1 >= true) // true /* 如果其中一个操作数是数字类型,则将另一个操作数转换为数字类型后进行比较 * 1、Number("0") = 0, * 2、1 >= 0 返回 true */ console.log(1 >= "0") // true /* 如果其中一个操作数是数字类型,则将另一个操作数转换为数字类型后进行比较 * 1、Number(null) = 0, * 2、1 >= 0 返回 true */ console.log(1 >= null) // true /* NaN无法和任何操作数进行比较,包括NaN都会返回false * 1、{}.valueOf().toString()返回"[object Object]" * 2、Number("[object Object]") = NaN, * 3、1 >= NaN 返回 false */ console.log(1 >= {}) // false
-
相等和不等运算符:\textrm\color{red}{== !=}
- 如果其中一个操作数是数字类型,则通过隐式调用Number()函数转换后比较
- 如果其中一个操作数是布尔类型,则会先将布尔类型转换为number类型后进行比较
- 如果其中一个操作数是对象类型,则通过调用valueOf()后,将返回的结果按前面规则进行比较
- null与undefined是相等的
- 如果其中一个操作数等于NaN,则比较返回false
- 如果两个操作数都是对象,则比较的是它们所指向的是不是同一个实例对象
-
逻辑非运算符:\textrm\color{red}{!}
- 将其他数据类型使用Boolean()函数转换成布尔值
- 除0 、-0、NaN、undefined、null、""、false、document.all()外,其他数据类型转换都会返回true
/* 原理 * 1、[].valueOf().toString()返回""空字符串, * 2、然后Number("")得到 0 * 3、0 == 0 返回 true */ console.log([] == 0) // true /* 原理 * 1、逻辑非操作符优先级高于关系运算符,所以先将空数组转换为布尔值,[]转换为布尔值true * 2、!true 得到 false * 3、Number(false)得到 0 * 4、0 == 0 返回 true */ console.log(![] == 0) // true /* 原理 * 1、[].valueOf().toString()返回""空字符串, * 2、![] = false * 3、Number("")得到 0 * 4、Number(false) 得到 0 * 5、0 == 0 返回 true */ console.log([] == ![]) // true /* 原理 * 1、引用数据类型,值存在堆中,栈中存放的是引用的地址,所以返回false */ console.log({} == {}) // false /* 原理 * 1、左边:{}.valueOf().toString()返回"[object Object]", * 2、右边:!{} = false * 3、Number("[object Object]") = NaN * 4、Number(false) 得到 0 * 5、NaN == 0 返回 false */ console.log({} == !{}) // true