1.JS中的数据类型和常用的判断数据类型的方法
基本数据类型
String 字符串 Number 数字 null 空 Boolean 布耳值 undefined 未定义的 symbol 唯一值 bigInt 大数字
引用数据类型
Object 对象 Array 数组 Date 时间 Function 函数 RegExp 正则
2.let var const的区别
用var声明的变量,存在变量的提升。 用let/const声明的变量,不存在变量的提升。 变量提升:变量在声明之前可以使用,并且代码不报错(预解析)。 用var声明的变量,允许重复声明。 用let/const声明的变量,不允许重复声明。 用var声明的变量,不存在块级作用域。 用let/const声明的变量,存在块级作用域。
3.js中的this指向
方法中谁调用,this就指向谁。(.前面是谁,this就指向谁) 如果没有人调用的时候this默认指向window。 构造函数中的this,指向通过这个构造函数创建出来的实例本身。 强制改变this指向:call/apply/bind。 call方法和apply方法声明后会直接调用。call跟的是一个参数集合,用逗号分割。apply跟的是一个数组。 bind不会直接的调用,需要用()(),的方法调用。bind本身不会去执行要被改变this指向的这个方法。而是会返回一个已经改变this指向的新方法。
4.箭头函数与普通函数的区别
箭头函数的this,始终指向父级的上下文。 箭头函数是一个匿名函数,不能作为构造函数使用,不能使用new。 箭头函数没有原型属性。 箭头函数内没有arguments,可以用展开运算符...解决。 arguments是一参数的集合、是一个伪数组。当不确定有几个参数的时候,用arguments来接收。 伪数组转数组 Array.from() / Array.prototype.slice.call()。
5.数组常用方法(至少6个详细说明)
forEach() 数组循环、遍历 --> 原生js every()法用于检测数组所有元素是否都符合指定条件。 (es6)every遍历数组每一项,每一项返回true, 则最终结果为true, 当任何一项返回false时,停止遍历,返回false。不改变原数组 不改变原数组的值。 concat()方法用于连接两个或多个数组或字符串。 pop()删除并返回数组的最后一个(删除元素) 改变原数组。 push()末尾添加元素, 返回新的长度 改变原数组。 shift()把数组的第一个元素删除,并返回第一个元素的值 改变原数组。 unshift()将参数添加到原数组开头,并返回数组的长度。 findIndex()获取元素在数组的索引下标。
6.原型和原型链
原型 每一个函数都有一个prototype,被称为显示原型,函数的实例对象有一个__proto__属性,被称为隐式原型。隐式原型__proto__指向构造函数的显示 原形prototype。每一个prototype显示原型都有一个constructor属性,指向其关联的的构造函数。 原型链 获取对象的属性的时候,如果本身没有这个属性,就会去其原型__proto__上找,如果还查找不到,就去找原型的原型,直到找到最顶层的Object.prototype, Object.prototype也有__proto__属性,属性值为null。这种层层寻找的机制就叫原型链。
7.作用域和闭包
作用域 全局作用域、函数(局部)作用域、块级作用域 函数作用域 --> 函数在执行的时候会产生一个独立的作用域。 块级作用域 ES新增的 --> {} 每一个{}就是一个块级作用域。 闭包 函数内部返回一个新的函数
function fn1(){
var a = 1;
return function () {
console.log(a);
}
}
闭包的有优缺点
优点 1.变量长期驻扎在内存中。 2.可以重复使用变量,并且不会造成变量的污染。 缺点 由于闭包会使得函数中的变量都被保存在内存中,内存消耗大,不能滥用闭包,否则会影响页面的性能。在IE中会导致内存的泄露。解决的方案是在退出函数 之前,将不使用的局部变量全部删除。
8.谈一下你理解的面向对象
面向对象和面向过程一样,都是一种编程思想(万物皆对象)。 JS本身也是一门面向对象的语言,js和vue都是基于面向对象的编程思想构造出来的。 S中的面向对象和后端语言常见的面向对象稍微有些不同,JS中的类和实例是基于原型和原型链机制来处理的。 封装:高内聚低耦合。 继承:子类 实例 继承父类的属性和方法。 重写:修改已有的属性和方法。 重载 JS本身是没有重载的,重载对于客户端代码来说是没有必要的。
9.声明一个父类和一个子类,并且实现子类继承父类
//父类的构造函数
function Parent(x) {
this.x = x;
this.sayHello = function () {
console.log('sayHello')
}
}
// 父类原型
Parent.prototype.getX = function () {
console.log('getX', this.x)
}
let p1 = new Parent(100)
//子类构造函数
function Child(y) {
Parent.call(this, 666) //让父类的构造函数的this指向子类构造函数
this.y = y;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getY = function () {
console.log('getY', this.y)
}
//子类实例
let c1 = new Child(200, 666);
10.什么是深拷贝和浅拷贝,使用递归的方式实现一个深拷贝
深拷贝和浅拷贝是针对与引用数据类型,进行数据拷贝。在不影响原数据的情况下,再复制一份数据出来。
let obj = {
a: 1,
b: 2,
c: {
name: "哈哈",
age: 18,
friend: [
{
name: "嘻嘻",
age: 16,
},
]
},
d: [1, 2, 3, 4]
}
// 浅拷贝:对于复杂综合的数据,只拷贝第一层
let obj2 = {};
for (let Key in obj) {
obj2[Key] = obj[Key]
}
// 深拷贝:层层拷贝
function deepCopy(obj) {
let result = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
result[key] = deepCopy(obj[key])
} else {
result[key] = obj[key]
}
}
}
return result
}