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
}
|