[toc]
一面
1. 讲一讲继承的所有方式都有什么 手写一个寄生组合式继承
- 1).原型链继承: 将父类的实例作为子类的原型
function Cat(){}Cat.prototype = new Animal();Cat.prototype.name = 'cat';// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.eat('fish'));console.log(cat.sleep());console.log(cat instanceof Animal); //trueconsole.log(cat instanceof Cat); //true
特点:1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例2. 父类新增原型方法/原型属性,子类都能访问到3. 简单,易于实现缺点:1. 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中2. 无法实现多继承3. 来自原型对象的所有属性被所有实例共享4. 创建子类实例时,无法向父类构造函数传参
- 2).构造函数的继承: 使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(){}Cat.prototype = new Animal();Cat.prototype.name = 'cat';// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.eat('fish'));console.log(cat.sleep());console.log(cat instanceof Animal); //trueconsole.log(cat instanceof Cat); //true
特点:1. 解决了1中,子类实例共享父类引用属性的问题2. 创建子类实例时,可以向父类传递参数3. 可以实现多继承(call多个父类对象)缺点:1. 实例并不是父类的实例,只是子类的实例2. 只能继承父类的实例属性和方法,不能继承原型属性/方法3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
- 3).实例继承: 为父类实例添加新特性,作为子类实例返回
function Cat(name){var instance = new Animal();instance.name = name || 'Tom';return instance;}// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); // false
特点:1. 不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果缺点:1. 实例是父类的实例,不是子类的实例2. 不支持多继承
- 4).拷贝继承
function Cat(name){var animal = new Animal();for(var p in animal){Cat.prototype[p] = animal[p];}Cat.prototype.name = name || 'Tom';}// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // falseconsole.log(cat instanceof Cat); // true
特点:1. 支持多继承缺点:1. 效率较低,内存占用高(因为要拷贝父类的属性)2. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
- 5).组合继承:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){Animal.call(this);this.name = name || 'Tom';}Cat.prototype = new Animal();Cat.prototype.constructor = Cat;// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); // true
特点:1. 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法2. 既是子类的实例,也是父类的实例3. 不存在引用属性共享问题4. 可传参5. 函数可复用缺点:1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
- 6). 寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Cat(name){Animal.call(this);this.name = name || 'Tom';}(function(){// 创建一个没有实例方法的类var Super = function(){};Super.prototype = Animal.prototype;//将实例作为子类的原型Cat.prototype = new Super();})();// Test Codevar cat = new Cat();console.log(cat.name);console.log(cat.sleep());console.log(cat instanceof Animal); // trueconsole.log(cat instanceof Cat); //trueCat.prototype.constructor = Cat; // 需要修复下构造函数
特点:堪称完美缺点:实现较为复杂
2. 讲一讲你平时怎么学习前端的
参考话术:在网上找了一些前端技术栈的知识架构图, 根据结构路程图买了一些响应的书籍,以及看了一些博客文章,在github上找一些成型的项目, 先看了一下别人的代码,觉得自己也能实现,就自己写了一下,写的过程中出现了xxxxxxxx问题, 网上找问题的原因,以及解决方案, 有一些基础知识网上找了一些教学视频, 看了一下,做了一些笔记...
3. 都喜欢看些什么技术类的书
就说你看过的就行
4. 讲一讲http
HTTP: 超文本传输协议,1. WEB浏览器与WEB服务器之间的一问一答的交互过程必须遵循的规则。2. 它是TCP/IP 协议集中的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程以及数据本身的格式。3. HTTP协议的版本 HTTP/1.0、HTTP/1.1、HTTP-NG,比较常用的是http/1.1HTTP的会话方式分为四个过程:1. 建立连接2. 发出请求3. 接收响应4. 断开连接(此处可以详细的说一下建立连接和断开连接的过程)可以说说HTTP 报文结构 contentType不一样的话报文结构是不一样的可以进行加以区分
5. https了解吗,讲一讲
http的加密版, 主要在http的基础上添加了一层SSL/TLS加密详细描述参照文章: https://blog.csdn.net/xiaoming100001/article/details/81109617
6. 加密过程是什么
数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为“密文”,使其只能在输入相应的密钥之后才能显示出本来内容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的
- this指向问题 看代码说答案
8. call apply bind区别
都是用来改变this指向的call,apply 与bind 的区别是返回值不同call apply执行的结果返回值由改变this指向的函数返回值决定 而bind方法执行完之后返回一个新的改变this指向之后的函数call apply的区别在于传参形式不同 call的第二个参数开始是传递到函数体内的实参,apply的第二个参数是数组,数组的每一项是函数体内的实参
9. es6讲一讲
把你会的都说一下,新增的语法,接口优化方式....
- 各种es6知识点挨个细说
11. 讲讲深浅拷贝
深浅拷贝主要是针对对象来说的, 因为对象是一个引用值, 如果拷贝的时候直接赋值的话就会对原始对象有影响深拷贝是说复制出来的内容完全独立,而浅拷贝是表面上一样,对于属性类型为对象类型的数据并不是独立的.**深浅拷贝的实现方式说一下伪代码**
- 手撕深拷贝
<!--普通的深层拷贝函数: -->function deepCopy( source ) {if (!isObject(source)) return source; //如果不是对象的话直接返回let target = Array.isArray( source ) ? [] : {} //数组兼容for ( var k in source ) {if (source.hasOwnProperty(k)) {if ( typeof source[ k ] === 'object' ) {target[ k ] = deepCopy( source[ k ] )} else {target[ k ] = source[ k ]}}}return target}function isObject(obj) {return typeof obj === 'object' && obj !== null}// 缺点:(1)无法保持引用(2)当数据的层次很深,会栈溢出<!--防栈溢出函数-->function cloneLoop(x) {const root = {};// 栈const loopList = [{parent: root,key: undefined,data: x,}];while(loopList.length) {// 深度优先const node = loopList.pop();const parent = node.parent;const key = node.key;const data = node.data;// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素let res = parent;if (typeof key !== 'undefined') {res = parent[key] = {};}for(let k in data) {if (data.hasOwnProperty(k)) {if (typeof data[k] === 'object') {// 下一次循环loopList.push({parent: res,key: k,data: data[k],});} else {res[k] = data[k];}}}}return root;}<!--最简单的深拷贝方式-->function clone(obj) {return JSON.parse(JSON.stringify(obj));}
- 有什么问我的吗
二面
1. 我们来假设一个场景,就比如说部门给我们一个任务,让我们做一个上传文件的区域,该怎么做啊
该问题需要注意,问题不明确,要问面试官是什么样式的,普通的文件上传,还是类似于组件库里面上传文件组件的效果.如果是简单按钮的文件上传直接用input type="file"就可以了如果是非普通的,再问一下需要兼容什么样的文件, 如果是图片的化需要预览嘛? 可以用组件库嘛? 等等一系列的问题如果上传的文件是图片需要做预览, 可以用div配合着点击事件配合着input type="file" 配合着fileReader实现具体思想input标签隐藏显示(设置hidden属性就好) div区域里面画一个十字表示可点击上传文件,点击这个区域时手动触发input的点击事件这样就可以上传文件了,再上穿文件的过程中可以用fileReader的相应一些事件进行监听读取的文件进度等到文件读取完成之后,上传至服务器端(调用指定接口) 如果上传成功,可以使用一些动画效果文字显示,并且将图片显示再指定位置上
- 除了你刚才提到的方法 还有别的吗
3. http常见的请求头都有什么啊 (能记住几个说几个)
content-Type 请求体的MIME类型 (用于POST和PUT请求中)Accept-Language 可接受的响应内容语言列表。If-Match 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要用于像 PUT 这样的方法中,仅当从用户上次更新某个资源后,该资源未被修改的情况下,才更新该资源。host 表示服务器的域名以及服务器所监听的端口号。如果所请求的端口是对应的服务的标准端口(80),则端口号可以省略。referer 表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接将浏览器带到了当前页面。Referer其实是Referrer这个单词,但RFC制作标准时给拼错了,后来也就将错就错使用Referer了origin 发起一个针对跨域资源共享的请求(该请求要求服务器在响应中加入一个Access-Control-Allow-Origin的消息头,表示访问控制所允许的来源)If-Modified-Since 允许在对应的资源未被修改的情况下返回304未修改
- 了解定长包体吗
5. 你对refer怎么理解的
Referer 是 HTTP 请求header 的一部分,当浏览器(或者模拟浏览器行为)向web 服务器发送请求的时候,头信息里有包含 RefererReferer 的正确英语拼法是referrer 。由于早期HTTP规范的拼写错误,为了保持向后兼容就将错就错了。其它网络技术的规范企图修正此问题,使用正确拼法,所以目前拼法不统一。还有它第一个字母是大写。referer证明了请求的源自哪里发起的,能够明确的指向地址 我们可以用这个header 做防止恶意请求处理或者防盗链, 服务器端可以通过怕这个请求头判断请求的来源是不是被允许的,如果不是被允许的就不正常返回信息就好例如京东的部分接口就是不被允许随意使用的,只有在京东的域名下面访问才可以
6. es6之前如何模拟类的
通过构造函数的方式进行模拟构造函数的封装就可以体现类的封装特点构造函数的prototype属性就可以模拟继承的关系构造函数的在不同作用域下使用也就类似于类的多态的特点
7. 构造函数给我讲讲可以吗,最好加上你自己对于他的理解
首先构造函数的作用主要是为了封装一个工具函数可以快速的创建出一类的实例对象供我们使用.构造函数本身也是函数,所以他也可以按照函数的方式进行执行, 构造函数和普通的函数的区别在于1, 函数名字的写法不同 构造函数的名称首字母大写(但并不是说首字母不大写的函数就不能作为构造函数使用,这只是一个规范性的写法)2, 使用不同构造函数的一般使用方式是 通过new关键字进行构建实例对象普通函数就直接函数名() 直接会执行函数3, 函数内部this指向不同构造函数内部的this指向的是由构造函数构造出来的实例对象普通函数的this指向一般为全局对象构造函数里面可以定义所有实例对象公共的属性方法, 构造函数有prototype属性代表了所有由构造函数构造出来的实例对象的共有属性或方法,实例对象身上相应的也有__proto__属性指向的是构造出这个实例对象的构造函数的prototype属性构造函数也是es6中类实现的基础
8. 在你学习数据结构的时候,有没有觉得有什么很难,但是很有趣的地方呢
这里就是比较开放性的题了, 可以说数据结构我觉得并不难, 他的难点在于算法的实现.算法我的理解就是解决问题的思维逻辑, 比较抽象化, 个人觉得一个问题可能很多种思维方式都可以实现,但是想要找得到最优的,这就是个难点,如果找到了最优解的时候是特别有成就感的,这个过程就比较好玩
- 一道链表算法题
- 有什么问我的吗
HR面
常规问题
