导图社区 前端面试宝典
前端面试宝典、寄生组合继承:寄生和组合继承的合体,为了避免组合继承中无可避免的要调用两次父类构造函数的最佳方案,本质就是子类的原型继承自父类的原型。
编辑于2023-01-10 21:48:41 广东前端面试宝典
计算机基础
前端三件套
html
H5新特性
1、语义化标签 2、增强型 3、视频和音频4、Canvas绘图和SVG绘图5、地理定位6、拖放API 7、WebWorker和WebStorage和WebSocket
伪类和伪元素
伪类::hover
伪元素
音频视频标签属性
js
this指向:每个函数都有它的this指向,箭头函数除外,this指向都指向调用者
改变this指向的三种方法
call :传入的参数有两个第一个是对象,第二个是所要传入函数的参数用逗号隔开
apply: 同样也是两个参数,第二个是一个数组
bind:和call相同,但不是立即执行的需要加上()
js工作原理:js是单线程脚本语言,再执行需要浏览器协助完成,形成一套事件循环
线程
是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
进程
是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位
事件循环Event Loop
异步相关:不进入主线程,而进入任务队列,只有主线程读取任务队列队列才可以进入主线程执行:(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。 (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 (4)主线程不断重复上面的第三步。 主线程从任务队列中读取事件,这个过程是不断循环的,所以整个的运行机制称为event loop。任务还可以分为宏任务和微任务
宏任务:可以理解是每次执行执行栈的代码就是一个宏任务,(包括每一次从任务队列中获取一个事件回调并且放入执行栈中执行,每一个宏任务从头到尾将这任务任务执行完毕,不会执行其他)主要的setTimeout,setInterval
微任务:可以理解是在当前task执行结束后立即执行的任务 包括Promise,$nextTick
同步:在主线程上排队执行的任务,只有前面一个任务执行结束才会执行下一个任务
Event Loop是一个程序结构,用于等待和发送消息和事件。简单的说,就是在程序中设置两个线程:一个负责程序本身的运行,称为主线程,另一个负责主线程与其他进程的通信,被称为Event Loop线程
ES5 ES6
原型链
就是实例对象中由
proto 和原型连接在一起的链条,每个函数都有它的原型对象prototype属性,通过new构造出来的实例对象上的隐式原型 proto 就等于构造函数上的显式原型prototype,如果寻找某个值时首先在对象自身找,如果没有那么会沿着原型链寻找,如果原型链上没有这个值则会报undefined,如果对象本身和原型链上都有则取到自身的那个值。
继承
原型链继承:将父亲构造函数的原形赋值给子构造函数 优点:简单、易实现,父类新增原型属性和方法也会被子类所继承 缺点:无法实现多继承、引用类型的值会被实例共享、子类型无法给超类型传递参数
function Parent() { this.parentName = '父类'; } Parent.prototype.getParentName = function() { return this.parentName; };
function Child() { this.childName = '子类'; } Child.prototype = new Parent(); Child.prototype.getChildName = function() { return this.childName };
var c = new Child(); console.log(c.getParentName()); // '父类'
构造函数继承:在解决原型链继承中包含引用类型带来大问题,开发了一种借助子构造函数的技术,即在子类型构造函数的内部调用超类型构造函数。借助构造函数基本思想就是利用call和apply把父类中通过this指定的属性和方法复制到子类创建的实例中 优点:解决了引用类型的值被实例共享的问题、可以向超传递参数、实现多继承 缺点:不能继承超类原型上的属性和方法、无法实现函数的复用、由于call有多个父类实例的副本,性能消耗、子类的原型链丢失
function Parent(name) { this.name = name; this.hobbies = ["sing", "dance", "rap"]; }
function Child(name) { Parent.call(this, name); this.age = 24 }
var c1 = new Child('c1'); var c2 = new Child('c2'); c1.hobbies.push('coding');
console.log(c1.hobbies) console.log(c2.hobbies) console.log(c1 instanceof Parent) console.log(c1 instanceof Child)
组合模式继承:是将原型链继承和构造函数继承的技术组合到一块,从而发挥两者之长,这种继承方式看起来似乎没有问题,但是它却调用了 2 次超类型构造函数:一次在子类构造函数内,另一次是将子类的原型指向父类构造的实例
function Parent(name){ this.name = name; this.hobbies = ["sing", "dance", "rap"]; } Parent.prototype.getName = function(){ return this.name } function Child(name){ Parent.call(this, name); this.age = 24 }
Child.prototype = new Parent('父类') var c1 = new Child('c1'); var c2 = new Child('c2');
console.log(c1.hasOwnProperty('name')); // true console.log(c1.getName()); // "c1"
c1.hobbies.push('coding'); console.log(c1.hobbies); // ["sing", "dance", "rap", "coding"] console.log(c2.hobbies); // ["sing", "dance", "rap"]
共享继承:类似于原型链继承,理解简单,但是只能继承父类原型属性和方法,不能继承构造函数属性和方法 也存在引用类型问题
function Parent(){} Parent.prototype.hobbies = ["sing", "dance", "rap"];
function Child(name, age){ this.name = name; this.age = age; } Child.prototype = Parent.prototype;
var c1 = new Child("c1", 20); var c2 = new Child("c2", 24);
c1.hobbies.push("coding"); console.log(c1.hobbies); // ["sing", "dance", "rap", "coding"] console.log(c2.hobbies); // ["sing", "dance", "rap", "coding"] console.log(c1.name); // "c1" console.log(c2.name); // "c2"
原型式继承:这种继承方式普遍用于当前已有对象创建新对象。
function createAnother(o) { function F() {} F.prototype = o; return new F(); }
var o1 = { name: '父对象', say: function() {} }
var o2 = createAnother(o1); console.log(o2.name); // "父对象"
寄生式继承:是原型式继承的加强版,它结合原型式继承和工厂模式,创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后放回对象 缺点:原型式继承有的缺点它都有
寄生组合继承:寄生和组合继承的合体,为了避免组合继承中无可避免的要调用两次父类构造函数的最佳方案,本质就是子类的原型继承自父类的原型,申明一个用于继承原型的inheritPrototype方法,通过这个方法我们能够将子类的原型指向超类的原型对象,从而避免二次实例化。
function Parent(name) { this.name = name; this.hobbies = ["sing", "dance", "rap"]; } Parent.prototype.getHobbies = function(){ return this.hobbies } function Child(name) { Parent.call(this, name); this.age = 24 }
Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;
// 测试结果 var c1 = new Child('c1'); var c2 = new Child('c2');
console.log(c1 instanceof Child); // true console.log(c1 instanceof Parent); // true console.log(c1.constructor); // Child console.log(Child.prototype.
proto === Parent.prototype); // true console.log(Parent.prototype. proto === Object.prototype); // true
c1.hobbies.push('coding'); console.log(c1.getHobbies()); // ["sing", "dance", "rap", "coding"] console.log(c2.getHobbies()); // ["sing", "dance", "rap"]
class类的继承:ES6中通过class关键字来定义类,子类可以通过extends继承父类 要点:
constructor 为构造函数,即使未定义也会自动创建。 在父类构造函数内 this 定义的都是实例属性和方法,其他方法包括 constructor、getHobbies 都是原型方法。 static 关键字定义的静态方法都必须通过类名调用,其 this 指向调用者而并非实例。 通过 extends 可以继承父类的所有原型属性及 static 类方法,子类 constructor 调用 super 父类构造函数实现实例属性和方法的继承。 对比: ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply(this))。 ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到 this 上面(所以必须先调用 super 方法),然后再用子类的构造函数修改 this。
class Parent{ constructor(name) { this.name = name; this.hobbies = ["sing", "dance", "rap"]; } getHobbies() { return this.hobbies; } static getCurrent() { console.log(this); } }
class Child extends Parent { constructor(name) { super(name); } }
var c1 = new Child('c1'); var c2 = new Child('c2');
console.log(c1 instanceof Child); // true console.log(c1 instanceof Parent); // true
函数
箭头函数
箭头函数的形式就是()=>{},它这个作用于的this指向是它所定义区域的this指向,它没有自己的this,不能通过call和apply、bind改变this指向,并且箭头函数不能作为构造函数,所以不能通过new关键字构造实例化对象,箭头函数没有arguments参数、但是可以通过...argus拿到所传入的参数或者通过Rest,最后箭头函数都是匿名函数
普通函数
普通函数需要用function关键字声明,有匿名函数和具名函数,this指向是调用者,如果没有声明调用者,则指向当前所调用区域的this指向,有arguments参数、可以通过new关键字构造实例化对象、可以改变它的this指向
数组和对象还有字符串相关方法
数组
字符串
对象
数据类型
基本类型:字符串String、数字Number、布尔Boolean,Null、undefined、Bigint、Symbol
引用类型:对象Object,函数Function
浅拷贝和深拷贝
浅拷贝:浅拷贝只是针对引用类型而言的一个浅层次的拷贝,拷贝的是对象的其引用地址,当修改这个对象是原对象也会发生改变,原因是新对象和久的对象的引用地址指向的是堆中同一个值
实现的方法:Object.assgin(),Array.prototype.concat()和slice() ...拓展运算符
深拷贝:基本数据类型都是采用深拷贝,引用数据类型的深拷贝是指,复制一个新的对象在堆中开辟一个新的空间存放这个对象的值,新对象和就对象的值是一样的,当他们的引用地址是指向不同的值,当改变新的对象时,旧的对象的值不会发生改变
//定义检测数据类型的功能函数 function checkedType(target) { return Object.prototype.toString.call(target).slice(8, -1) } //实现深度克隆---对象/数组 function clone(target) { //判断拷贝的数据类型 //初始化变量result 成为最终克隆的数据 let result, targetType = checkedType(target) if (targetType === 'Object') { result = {} } else if (targetType === 'Array') { result = [] } else { return target } //遍历目标数据 for (let i in target) { //获取遍历数据结构的每一项值。 let value = target[i] //判断目标结构里的每一值是否存在对象/数组 if (checkedType(value) === 'Object' || checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组 //继续遍历获取到value值 result[i] = clone(value) } else { //获取到value值是基本的数据类型或者是函数。 result[i] = value; } } return result }
实现的方法:递归算法,JSON.parse(JSON.stringify()),可以用第三库loadsh中的_.cloneDeep,手动拷贝
css
盒模型
标准盒子模型 box-sizing:content-box
盒子大小等于盒子本身的大小
怪异盒模型 box-sizing:border-box
盒子的大小等于盒子本身大小加上内边距padding border的宽度大小
BFC
原理:BFC内部的盒子,会在垂直方向,一个接一个地垂直放置。垂直方向也会发生边缘的重叠 ,它相当于一个独立的容器,容器不会影响外面的元素,外面的元素也不会影响里面的元素,BFC区域不会与float重叠。 计算BFC的高度时,浮动元素也要被计算在内
BFC如何产生–
overflow: auto/ hidden;
position: absolute/ fixed;
float: left/ right;
display: inline-block/ table-cell/ table-caption/ flex/ inline-flex
布局
弹性布局
flex布局
grod布局
样式属性
样式导入
link
@import
区别:link是HTML中的一个标签,兼容性好,当浏览器读取到HTML时同时也会加载link标签引入的css,@import只是用于css导入样式,并且兼容性差,在浏览器加载完后加载@import导入的CSS样式
less和sass
权重:import>行内>id选择器>类class选择器(伪类选择器)>标签选择器
回流和重绘:回流就是当页面中元素改变大小或者位置时浏览器会发生回流,重绘就是当浏览器只改变元素的样色样式
前端进阶
框架
开发工具
算法与数据结构
业务场景