1. JS 有哪些数据类型?
根据 JavaScript 中的变量类型传递方式,分为基本数据类型和引用数据类型两大类七种。
基本数据类型包括Undefined、Null、Boolean、Number、String、Symbol (ES6新增)六种。
引用数据类型只有Object一种,主要包括对象、数组和函数。
判断数据类型采用typeof
操作符,有两种语法:
typeof 123;//语法一
const FG = 123;
typeof FG;//语法二
typeof(null) //返回 object;
null == undefined //返回true,因为undefined派生自null;
null === undefined //返回false。
2. 基本数据类型和引用数据类型有什么区别?
(1)两者作为函数的参数进行传递时:
基本数据类型传入的是数据的副本,原数据的更改不会影响传入后的数据。
引用数据类型传入的是数据的引用地址,原数据的更改会影响传入后的数据。
(2)两者在内存中的存储位置:
基本数据类型存储在栈中。
引用数据类型在栈中存储了指针,该指针指向的数据实体存储在堆中。
3. 判断数据类型的方法有哪些?
(1)利用typeof
可以判断数据的类型;
(2)A instanceof
B可以用来判断A是否为B的实例,但它不能检测 null 和 undefined;
(3)B.constructor == A
可以判断A是否为B的原型,但constructor检测 Object与instanceof不一样,还可以处理基本数据类型的检测。
不过函数的 constructor 是不稳定的,这个主要体现在把类的原型进行重写,在重写的过程中很有可能出现把之前的constructor给覆盖了,这样检测出来的结果就是不准确的。
(4)Object.prototype.toString.call()
Object.prototype.toString.call()
是最准确最常用的方式。
4. 与深拷贝有何区别?如何实现?
浅拷贝只复制指向某个对象的指针,而不复制对象本身。浅拷贝的实现方式有:
(1)Object.assign()
:需注意的是目标对象只有一层的时候,是深拷贝;
(2)扩展运算符;
深拷贝就是在拷贝数据的时候,将数据的所有引用结构都拷贝一份。深拷贝的实现方式有:
(1)手写遍历递归赋值;
(2)结合使用JSON.parse()
和JSON.stringify()
方法。
5. let、const的区别是什么?
var
、let
、const
都是用于声明变量或函数的关键字。其区别在于:
var | let | const | |
---|---|---|---|
作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
作用域内声明提升 | 有 | 无(暂时性死区) | 无 |
是否可重复声明 | 是 | 否 | 否 |
是否可重复赋值 | 是 | 是 | 否(常量) |
初始化时是否必需赋值 | 否 | 否 | 是 |
6. 什么是执行上下文和执行栈?
变量或函数的执行上下文,决定了它们的行为以及可以访问哪些数据。每个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存在于这个对象上(如DOM中全局上下文关联的便是window
对象)。
每个函数调用都有自己的上下文。当代码执行流进入函数时,函数的上下文被推到一个执行栈中。在函数执行完之后,执行栈会弹出该函数上下文,在其上的所有变量和函数都会被销毁,并将控制权返还给之前的执行上下文。 JS的执行流就是通过这个执行栈进行控制的。参考 前端进阶面试题详细解答
7. 什么是作用域和作用域链?
作用域可以理解为一个独立的地盘,可以理解为标识符所能生效的范围。作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。ES6中有全局作用域、函数作用域和块级作用域三层概念。
当一个变量在当前块级作用域中未被定义时,会向父级作用域(创建该函数的那个父级作用域)寻找。如果父级仍未找到,就会再一层一层向上寻找,直到找到全局作用域为止。这种一层一层的关系,就是作用域链 。
8. 作用域和执行上下文的区别是什么?
(1)函数的执行上下文只在函数被调用时生成,而其作用域在创建时已经生成;
(2)函数的作用域会包含若干个执行上下文(有可能是零个,当函数未被调用时)。
9. this指向的各种情况都有什么?
this的指向只有在调用时才能被确定,因为this
是执行上下文的一部分。
(1)全局作用域中的函数:其内部this
指向window
:
var a = 1;
function fn(){
console.log(this.a)
}
fn() //输出1
(2)对象内部的函数:其内部this
指向对象本身:
var a = 1;
var obj = {
a:2,
fn:function(){
console.log(this.a)
}
}
obj.fn() //输出2
(3)构造函数:其内部this
指向生成的实例:
function createP(name,age){
this.name = name //this.name指向P
this.age = age //this.age指向P
}
var p = new createP("老李",46)
(4)由apply
、call
、bind
改造的函数:其this
指向第一个参数:
function add(c,d){
return this.a + this.b + c + d
}
var o = {
a:1