javascript 面试题汇总
var、let、const的区别?什么是块级作用域?
- var:定义变量,有变量提升,可以跨块访问,没有块级作用域的概念,不能跨函数访问
- let:es6新增的定义变量的方式,没有变量提升,不可以重复声明,有块级作用域的概念,
- const: es6新增的定义常量的方式,没有变量提升,声明的变量不可修改,有块级作用域的概念。
最初在js中,只有全局作用域、函数作用域,没有块级作用域的概念,在es6时,新增了块级作用域的概念。块级作用域解决了在for循环中声明变量泄漏成全局变量的问题
JS中的数据类型有哪些?基本类型和引用类型的区别是什么?
JS中的数据类型分为两类:
- 简单值(基础类型/原始类型):简单值分为7中:String字符串类型、Boolean布尔值类型、Number数字类型、undefined、null、symbol符号类型,bigInt大数类型
- 复杂值(引用值/引用类型):复杂值就一种,object,像数组、函数、正则这些统统都是对象类型,函数的本质也是对象,其内部有一个特别的属性[[call]],有了该属性,则标识该对象是可调用的,也区分了普通对象和函数对象,也因此设计使得JS中可以实现高阶函数。
内存存储分为栈和堆:
- 栈内存:栈内存因其数据大小和生命周期的可预测性而易于管理和快速访问。栈支持快速的数据分配和销毁过程
- 堆内存:堆内存更加灵活,可以动态的分配和释放空间,适合存储生命周期长活着大小不确定的数据。是用堆内存可以有效地管理大量的数据,但相对于栈来说,其管理成本更高,访问速度也更慢。
对于简单值而言,其通常存储于栈内存,简单值的存储通常包括直接在栈内存中分配的数据空间,并且直接存储了数据的实际值
对于复杂值而言,具体的值是存储在堆内存离的,因为复杂值往往大小不固定,无法在栈区分配一个固定大小的内存,因此具体的数据放在堆里面,而栈区存储一个内存地址,通过该内存地址可以访问到堆区里面具体的数据。
JS中在调用函数时,通通都是值传递,而非引用传递。
- 访问方式:简单值是按值访问,也就是说,一个变量如果存储的是一个简单值,当访问这个变量的时候,得到的是对应值;复杂值虽然也是按值访问,但是由于值对应的是一个内存地址,不需要进一步获取地址值后对应的值。
- 比较方式:无论简单值还是复杂值,都是进行值的比较,不过由于复杂值对应的值是一个内存地址,因此,只有这个内存地址相同时,才会被认为是相等的。
- 动态属性: 对于复制值来说,可以动态的为其添加属性和方法,这一点是简单值做不到的,如果为简单值动态添加属性,不会报错,会静默失败,访问时返回的值为undefined,如果为简单值添加方法,则会报错
- 变量赋值:复杂值和简单值都是讲值复制一份然后赋值给另一个变量,不过由于复杂值复制的是内存地址,因此修改新的变量会对旧的有影响。
是否了解javascript中的包装类型?
包装对象就是当基本类型以对象的方式去使用时,js会转换成对应的包装类型,相当于new一个对象,内容和基本类型的内容一致,然后当操作完成再去访问的时候,这个临时对象会被销毁,然后在访问的时候就是undefined。
number、string、boolean都有对应的包装类型,有了基本包装类型,js中的基本类型值可以被当做对象来访问。
包装类型特征:
- 每个包装类型都映射到同名的基本类型
- 在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便数据操作。
- 操作基本类型值的语句一经执行完毕,就会立即销毁新建的包装对象。
JS中如何进行数据转化
类型转换分为凉凉:隐式转换和显性转换
- 隐式转换:当不同数据类型之间进行相互运算或者当对非布尔类型的数据求布尔值的时候,会发生隐式转换
- 显性转换:强制将一种类型转换为另一种类型,显性转换往往会使用到一些转换方法
类型 | 转换方法 | |
---|---|---|
数值类型 | Number()、parseInt()、parseFloat | a= +数据 |
布尔类型 | Boolean() | !! |
字符串类型 | toString()、string() | ''+ |
谈谈你对js中原型和原型链的理解
不同的语言,生产对象的方式并不相同,大致可以分为两类:
- 基于类生产对象。基于类生产,首先要书写一个类,然后通过类实例化对象。
- 基于原型生产对象。基于原型生产,就现有一个对象A,然后想要一个B,就从B克隆一份对象得到B,新的对象B可以添加新的属性和方法,对于B而言,对象A就是自己的原型对象
在设计js时,就选择了原型的方式来生产对象,所以,在js中,无论这个对象是如何书写的,该对象都有自己的原型对象。在ES6之前,因为Java等面向对象语言的盛行,设计者不仅添加了this、new等关键词,还是用构造函数(即不成文约定的函数首字母大写)来模拟类。构建函数本身就是函数,但是如果使用new 的方式来调用。执行机制则和普通函数不同,这也是函数二义性的由来。构造函数执行机制如下:
- 创建一个空的JS对象
- 设置该对象的原型对象,将该对象属性链接至构造函数的原型对象上
- 设置this的上下文
- 如果该函数没有返回对象,则返回this