javascript 数据类型+变量声明

268 阅读9分钟

昨天写了自己这三个月以来的面试总结,收到了许多人的喜欢,其实我更多的是把这次找工作当作一个全面复习巩固的过程,复习资料的获取一方面是自己总结,另一方是查看其他人总结的比较全面的内容,另外如果时间允许的情况下,还是很喜欢看书的;

       数据类型判断以及变量声明在js中是最基础的了,在基础内容中,我会标注一些大家容易忽略的点;

javascript数据类型

图1 数据类型脑图

类型区分

基本类型

  • number 数字类型 包含整数和浮点数
  • string 字符串类型
  • boolean 布尔类型
  • null 表示空值,通常是已声明的变量未有明确的值的含义
  • undefined 未定义,使用var声明了变量,但未给变量初始化值,那么这个变量的值就是undefined
  • symbol js新增类型,键值对存储方式,具有唯一性

引用类型

引用类型的大类可分为Object类型,进行细分可以分为Object、Array、Date、Function等;

数据存储方式

正式因为数据存储方式,所以才会有了引用类型的身拷贝和浅拷贝;
在js的存储中,js有两种的数据存储方式,基本类型是存储在栈中,引用类型是存储在堆中;

栈内存

基本数据类型存储在栈内存中,是按照值进行访问的;

var a = '1'; //变量对象
var b = 3 //变量对象
var str = '我是变量'
图2 基础类型存储方式

当访问a的时候,会直接进行值的访问
当变量的值修改的时候,系统会重新为其赋值,开辟了一个新的空间存储值,变量名直接等于变量值,而被修改的原始变量值,会在内存回收中进行回收;

堆存储

引用类型的值保存在堆内存中的对象,值的大小不固定,栈内存中存放地址,指向堆内存只能够的对象,按照引用进行访问;

var obj = {
  a:2,
  b:33
}
var arr = [
  2,3,4
]

js不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。

图3 堆内存存储数据方式

因此 当我们访问引用类型的时候,是先找到当前存储值的地址,然后在通过地址访问到数据的值;

值的复制

基本类型

变量中的数据发生拷贝的时候,系统会自动为新的变量分配一个值,最后这些变量都是相互不影响的;

var name ='mfy'
var editName = name;
editName = 'yyy'
图4 基本类型值的复制

复制后的值是系统重新分配的,不会影响到原始被复制的值;

引用类型复制

引用类型的复制又分为深复制和浅复制; 此时我们只说浅复制;

var personalMsg ={
  name:'mfy',
  age:22,
  offer:'fy'
}

var clonePer = personalMsg;
personalMsg.name = 'yyy'
图5 引用类型值的复制

深拷贝还是浅拷贝

  • 当我们改变子对象(复制出来的新对象)时,父对象(被拷贝的对象)也会跟着改变的拷贝,称为浅拷贝。也就是说,子对象和父对象在浅拷贝的时候他们指向同一个内存的对象。
  • 深度拷贝就是把父对象拷贝到子对象上,而且两者的内存和以后的操作都互不影响的拷贝!

类型转换

当我们在使用某些数据变量时候,会自动发生一些类型的转换;js本身为弱类型,通常在使用的时候,不小心就会出现类型转换;

运算符号的隐式转换

全部都是+号

  • 包含+ - 操作的时候进行转换
  • 字符串两边存在+的时候,相邻均被转换成string类型
1+'2' // '12'
1+3+'3' // '43'
'3'+1+2 //'312'
2+1+false // 3
2+1+true  // 4 
3+1+{a:2} // 4[object Object]
  • 保持从前向后的运算顺序
  • 存在字符串的时候,会将前后两个进行类型转换String
  • 如果+号存在则会将false 转化成0 true转化成1
  • 存在引用类型,会将其转化为toString格式,在进行相加

存在-号时候

+在字符串使用会当成链接符号,而-则是直接进行类型转换

1-'2' // -1
1+3-'3' //1 
'3'+1-2 // 29
2+1-false // 3
2+1-true  // 2
3+1-{a:2} // NaN
  • 存在-的时候会将两边非数字类型转化为数字类型
  • 从前往后计算模式

==比较

  1. x、y均为number 1 == 1 //true
  2. x 、y 类型不相同
1=='1' //true
1==true  //true
  • boolean的时候会被转化成 0/1 3.存在对象比较时候
1 =={a:3}  //false
1 == {valueOf:()=>{return 1}} //true
  • 存在对象时候,如果对象无valueOf属性,则直接比较
  • 存在valueOf的时候,则会比较valueOf内容的值;

判断类型的方式

typeof

主要用于基本数据类型比较,包含基本数据类型

图6 typeof判断数据类型

对于null 是浏览器一直存在的这个问题

typeof null //"object"

typeof判断引用类型,判断引用类型的时候失效了

图7 typeof判断引用类型失效

Object.prototype.toString.call(XXX) 引用类型判断

console.log(
  Object.prototype.toString.call(null),'\n', //[object Null] 
  Object.prototype.toString.call(11),'\n', // [object Number]
  Object.prototype.toString.call(false),'\n', // [object Boolean]
  Object.prototype.toString.call('333'),'\n',  // [object String]
  Object.prototype.toString.call([3,4,2,2]),'\n', // [object Array]
  Object.prototype.toString.call({a:3,b:44}),'\n', //  [object Object]
  Object.prototype.toString.call(new RegExp('.*?')),'\n',  //[object RegExp] 
  Object.prototype.toString.call(new Promise((resolve,reject)=>{resolve(2)})),'\n', //[object Promise]
)

此方式能够判断出不管是引用类型还是基本类型都能够判断出来

instanceof

此方式能够判断的是原型链上的链接关系

function Super (){
  this.name = 'myyu'
}
let superNew = new Super();

console.log(superNew instanceof Super) //true

console.log(superNew instanceof Object) //true

console.log(superNew instanceof Array) //false

比较常见的就是

写instanceof的实现

function myInstanceof(left,right){ 
  if(!right || !left) return false;  
  //左侧的实例进行查找
  let pro = left.__proto__;
  //获取右侧的原型
  let prototype = right.prototype;  
  while(pro){
    if(pro == prototype){
      return true
    }
    pro = pro.__proto__;
  }
  return false;
}

其他方式

判断数组

Array.isArray([1,2,3]) //判断是否是数组
isNaN(333) //false 
isNaN(underfined+1) //true

变量声明

作用域

作用域知识非常重要,内容比较多,这里只简单提及,具体的内容后续补充

变量声明的三种方式

var

能够定义在全局的变量

 console.log("全局1:"+golbalA)
 var golbalA = 5;
 console.log("全局2:"+golbalA)

图8 var变量声明

定义在函数内部

var 定义声明的变量是可以进行变量提升的,因此第一个打印出undefind

console.log("全局1:"+golbalA)
    var golbalA = 5;
    console.log("全局2:"+golbalA)
    function a (){
      console.log(golbalA)
    }
    a()
图7 var变量声明在函数内部

全局定义的变量在函数中如果无同名的变量也可以访问;

 console.log("全局1:"+golbalA)
    var golbalA = 5;
    console.log("全局2:"+golbalA)
    function a (){
      var golbalA =2222
      console.log(golbalA)
    }
    a()

图8 var函数内部自定义的变量
  • 定义在全局的变量能够在函数作用内访问
  • 定义在函数内部的变量只能函数内部访问

let

定义在全局中

  console.log("全局1"+letA)
  let letA = "global";
  console.log(letA)

直接告诉我们不能够进行使用,在变量未初始化之前

图9 let变量声明

定义在函数内部中

 let letA = "global";
  console.log(letA) //global
  function a(){ 
    console.log(letA)//global
  }
  a()
 let letA = "global";
  console.log(letA)//global
  function a(){
    let letA = 'function'
    console.log(letA) //function
  }
  a()
  • 定义在全局的变量能够全局访问
  • 定义在函数内部的变量外部不能访问

定义在块级作用域中

 if(true){
    let globala = 'globala'
    var globalb = 'globalb'
  }
  console.log('globala',globala)
  console.log('globalb',globalb)

图10 let变量声明

此时就是用let定义在块级作用域的变量,无法在块级以外进行修改;而var是可以的;

let的暂时性死区

暂时性死区是经常忽略的问题,

var a =222
 if(true){
   a = 33;
   let a =44
 }
图11 let存在变量声明
- 只要块级作用域内存在 let 命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。上面的代码中,块级作用域内 let 又声明了一个局部变量 a ,导致后者绑定这个块级作用域,所以在 let 声明变量前,对 a 赋值会报错。 - ES6明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

const

定义常量

 const a = 3333;
  console.log(a)
  a= 444
图12 const定义常量

由const定义的变量是无法进行更改的

定义变量

  const a;
  console.log(a)
图13 const定义常量必须赋值

修改定义的值

const定义的基础类型是无法修改的,但是定义引用数据类型的时候;

  const obj ={
    a:22,
    b:444
  }
  console.log(JSON.stringify(obj) )
  obj.a = 'mayy'
  console.log(JSON.stringify(obj) )
图14 const定义常量可以修改

const 的暂时性死区

if(true){
   a=3;
   const a=5
 }
图15 const存在暂时性死区

const 总结

  • const 定义的基本类型常量一旦定义无法修改
  • const 声明变量必须进行定义
  • const 存在暂时性死区

变量提升

变量提升其中可以去排列组合测试下🥳🥳

  • var 存在变量提升,允许在声明前使用,let、const不能够在进行使用
  • var的变量会挂载到windos上,而let、const不会挂载;
  • let、const只能进行一次变量声明,后面不可以在进行声明同名字的变量
  • const的变量一经赋值,将不会在进行使用,除了引用类型除外;

面试常见问题总结

js数据类型

  • 基本类型和引用类型的区别
  • 存储方式 堆、栈存储区别和联系
  • nullundefined 的区别
  • 类型方式判断typeof、instanceof
  • 手写instanceof实现
  • 类型转换:隐式转换强制转换
  • 引用类型复制、拷贝、深浅拷贝

变量声明

  • 变量声明而生成的作用域
  • 暂时性死区 的含义以及生成方式
  • letconst 生成的作用域区间
  • 常见let 、const使用方式

参考文档

js隐式类型转换 《javascript 高级程序设计(第四版)》