昨天写了自己这三个月以来的面试总结,收到了许多人的喜欢,其实我更多的是把这次找工作当作一个全面复习巩固的过程,复习资料的获取一方面是自己总结,另一方是查看其他人总结的比较全面的内容,另外如果时间允许的情况下,还是很喜欢看书的;
数据类型判断以及变量声明在js中是最基础的了,在基础内容中,我会标注一些大家容易忽略的点;
javascript数据类型
类型区分
基本类型
- number 数字类型 包含整数和浮点数
- string 字符串类型
- boolean 布尔类型
- null 表示空值,通常是已声明的变量未有明确的值的含义
- undefined 未定义,使用var声明了变量,但未给变量初始化值,那么这个变量的值就是undefined
- symbol js新增类型,键值对存储方式,具有唯一性
引用类型
引用类型的大类可分为Object类型,进行细分可以分为Object、Array、Date、Function等;
数据存储方式
正式因为数据存储方式,所以才会有了引用类型的身拷贝和浅拷贝;
在js的存储中,js有两种的数据存储方式,基本类型是存储在栈中,引用类型是存储在堆中;
栈内存
基本数据类型存储在栈内存中,是按照值进行访问的;
var a = '1'; //变量对象
var b = 3 //变量对象
var str = '我是变量'
当访问a的时候,会直接进行值的访问
当变量的值修改的时候,系统会重新为其赋值,开辟了一个新的空间存储值,变量名直接等于变量值,而被修改的原始变量值,会在内存回收中进行回收;
堆存储
引用类型的值保存在堆内存中的对象,值的大小不固定,栈内存中存放地址,指向堆内存只能够的对象,按照引用进行访问;
var obj = {
a:2,
b:33
}
var arr = [
2,3,4
]
js不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。
因此 当我们访问引用类型的时候,是先找到当前存储值的地址,然后在通过地址访问到数据的值;
值的复制
基本类型
变量中的数据发生拷贝的时候,系统会自动为新的变量分配一个值,最后这些变量都是相互不影响的;
var name ='mfy'
var editName = name;
editName = 'yyy'
复制后的值是系统重新分配的,不会影响到原始被复制的值;
引用类型复制
引用类型的复制又分为深复制和浅复制;
此时我们只说浅复制;
var personalMsg ={
name:'mfy',
age:22,
offer:'fy'
}
var clonePer = personalMsg;
personalMsg.name = 'yyy'
- 当我们改变子对象(复制出来的新对象)时,父对象(被拷贝的对象)也会跟着改变的拷贝,称为浅拷贝。也就是说,子对象和父对象在浅拷贝的时候他们指向同一个内存的对象。
- 深度拷贝就是把父对象拷贝到子对象上,而且两者的内存和以后的操作都互不影响的拷贝!
类型转换
当我们在使用某些数据变量时候,会自动发生一些类型的转换;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
- 存在-的时候会将两边非数字类型转化为数字类型
- 从前往后计算模式
==比较
- x、y均为number
1 == 1 //true - 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
主要用于基本数据类型比较,包含基本数据类型
对于null 是浏览器一直存在的这个问题
typeof null //"object"
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)
定义在函数内部
var 定义声明的变量是可以进行变量提升的,因此第一个打印出undefind
console.log("全局1:"+golbalA)
var golbalA = 5;
console.log("全局2:"+golbalA)
function a (){
console.log(golbalA)
}
a()
全局定义的变量在函数中如果无同名的变量也可以访问;
console.log("全局1:"+golbalA)
var golbalA = 5;
console.log("全局2:"+golbalA)
function a (){
var golbalA =2222
console.log(golbalA)
}
a()
- 定义在全局的变量能够在函数作用内访问
- 定义在函数内部的变量只能函数内部访问
let
定义在全局中
console.log("全局1"+letA)
let letA = "global";
console.log(letA)
直接告诉我们不能够进行使用,在变量未初始化之前
定义在函数内部中
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)
此时就是用let定义在块级作用域的变量,无法在块级以外进行修改;而var是可以的;
let的暂时性死区
暂时性死区是经常忽略的问题,
var a =222
if(true){
a = 33;
let a =44
}
const
定义常量
const a = 3333;
console.log(a)
a= 444
由const定义的变量是无法进行更改的
定义变量
const a;
console.log(a)
修改定义的值
const定义的基础类型是无法修改的,但是定义引用数据类型的时候;
const obj ={
a:22,
b:444
}
console.log(JSON.stringify(obj) )
obj.a = 'mayy'
console.log(JSON.stringify(obj) )
const 的暂时性死区
if(true){
a=3;
const a=5
}
const 总结
- const 定义的基本类型常量一旦定义无法修改
- const 声明变量必须进行定义
- const 存在暂时性死区
变量提升
变量提升其中可以去排列组合测试下🥳🥳
- var 存在变量提升,允许在声明前使用,let、const不能够在进行使用
- var的变量会挂载到windos上,而let、const不会挂载;
- let、const只能进行一次变量声明,后面不可以在进行声明同名字的变量
- const的变量一经赋值,将不会在进行使用,除了引用类型除外;
面试常见问题总结
js数据类型
- 基本类型和引用类型的区别
- 存储方式 堆、栈存储区别和联系
null和undefined的区别- 类型方式判断
typeof、instanceof - 手写
instanceof实现 - 类型转换:
隐式转换和强制转换 - 引用类型复制、拷贝、深浅拷贝
变量声明
- 变量声明而生成的作用域
暂时性死区的含义以及生成方式let、const生成的作用域区间- 常见
let 、const使用方式
参考文档
js隐式类型转换 《javascript 高级程序设计(第四版)》