一轮轮的笔面试是对自己查缺补漏、学习成长的过程。
反思自己对前端的学习过程:2017年10月加入Smartlab411,起步阶段看书比较多但不够透彻,大部分看完代码片就开始撸代码,到了暑假实验室师兄准备秋招毕业,我们也担上了主程的任务,从去年暑假开始更多的是锻炼动手能力,后来前端体系升级导师采用了React作为管理端框架,一点点的踩坑,一步步的摸索。虽说自己的代码理解力提高了不少但对原理的概念稍微有点薄弱,面试提问也被问倒,在此决定分享总结自己的面经。
PS:悲惨的是我八月初硬拉导致右手韧带断裂,目前正在恢复中,这段时间只能靠左手码字。
概念:
1).只要创建了一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象;2).所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针;
3).当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。
综上所述,假如我们让原型对象等于另一个类型的实例,则此时的原型对象将包含一个指向另一个原型的实例,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条,这就是原型链。
首先要理解构造函数、原型对象、实例对象,我们以案例讲解:
构造函数:function Fn(){};
原型对象:Fn.prototype;
实例:const fn = new Fn();
上述满足原型三要素以及内部指针指向:
1. 构造函数通过prototype指向原型对象: Fn.prototype => Fn.protype
2. 原型对象通过constructor指向构造函数:Fn.prototype.constructor => Fn(){};
3. 实例通过__proto__指向原型对象:fn.__proto__ => Fn.protype;
4. 实例通过__proto__指向原型对象再通过constructor指向构造函数:fn.__proto__.constructor => Fn(){};
上面关系可以通过画图理解,三者呈三角关系,通过指正关联,
***** 拓展isPrototypeOf() 和 getPrototypeOf() ************
* *
******************* isPrototypeOf() *********************
* *
* 通过Object.isPrototypeOf来判断从属关系 *
* Fn.prototype.isPrototypeOf(fn); *
* Object.prototype.isPrototypeOf(fn); *
* Object.prototype.isPrototypeOf(Fn); *
* *
******************** getPrototypeOf() *******************
* *
* 此时为Fn的原型对象添加一个属性 *
* var proto = Object.getPrototypeOf(fn); *
* proto.name = 'Luvsta'; *
* 打印 Fn.prototype === proto; // true *
* 得到结果:两者相等; *
* proto = {name: "Luvsta", constructor: ƒ} *
* Fn.prototype = {name: "Luvsta", constructor: ƒ}; *
* *
*********************************************************
概念: 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
闭包的特性:
1.函数内再嵌套函数
2.内部函数可以引用外层的参数和变量
3.参数和变量不会被垃圾回收机制回收
这里不详细赘述计网的概念,主要分为以下几个方面:
1.谈一谈HTTP/1.0/1.1/2.0以及HTTPS的区别
2.什么是无状态服务,有状态服务呢?
3.状态码 笔者面试问到了206、301、302、304,其他的自行阅读学习
4.有了解HTTPS的SSL吗?对称加密和非对称加密算法
5.TCP、UPD的区别,TCP是如何保证可靠传输的?
6.socket有了解吗?在你的项目中为何要将心跳设置为15s
这里需要掌握发送了哪些指令以及Client和Server端的状态
初始化一个div默认宽度继承父级元素宽度,之所以不显示div是因为高度为0;
如果父元素设置了浮动,子元素也浮动那么这时候div的宽度为0。
利用flex弹性盒子布局
父元素display:flex
左右子元素宽度设定固定值宽度(width),中间子元素flex:1
如果想进一步实现多列自动响应式设置flex:1即可
本题尽可能不要采用js动态算变化的宽度,首先考虑用户体验问题。
1.全局作用域
显示声明:用var声明变量
var name = "luvsta";
var foo = function () { console.log("this is a function") };
/*---------全局变量会挂载到 window 对象上------------*/
console.log(window.name) // "luvsta"
console.log(window.foo) // ƒ () { console.log("this is a function") }
隐式声明:不用var声明变量
function foo(){
name = "luvsta"; // 直接变量名赋值
return name;
}
foo();
console.log(window.name); // luvsta 此时name为全局变量
2.函数作用域
变量在函数作用域内,外部无法直接访问函数内部变量,但可以通过return访问函数内部变量
function foo(){
var name = "luvsta";
return name;
}
console.log(foo()); // luvsta
值得一提的是立即执行函数是一个单独的作用域
(function(){
var name = "luvsta"
})();
console.log(window.name); // undefined
3.块级作用域(ES6)
块级作用域借助let、const声明变量,在函数执行后变量回收消失,let的优势体现在for循环中的变量
for(let i = 0 ; i < 5 ; i++ ){
setTimeout(function () {
console.log(i); // 0 1 2 3 4
}, 200);
}
for(var i = 0 ; i < 5 ; i++ ){
setTimeout(function () {
console.log(i); // 5 5 5 5 5
}, 200);
}
4.词法作用域
词法作用域是指一个变量的可见性,当我们要声明一个变量时,JS引擎总会从最近的一个域向外层域查找;
var name = 'luvsta';
function foo() {
console.log(name); // "luvsta"
}
function bar() {
var name = 'sam';
foo();
}
bar();
在该案例中,JS查找name这个变量时候发现全局的name更近一些。
5.动态作用域
该作用域查找变量方式与词法作用域相反,与this引用机制有关
动态作用域是基于调用栈的,而不是代码中的作用域嵌套;
掘金文章:谈谈 JavaScript 的作用域 ——作者:leiting1998
let 的用法类似于 var,但是 let 只在所在的代码块内有效,所以我们一般使用 let 替代 var。而 const 用来声明常量。
var:1)声明在函数外部 2)未定义直接赋值的变量 3)所有Window对象
let:局部变量 只在所处块级作用域有效 没有变量提升 会出现暂时性死区 特性(在声明前使用let变量会报错)
const:定义常量 通常不可变 声明的同时必须赋值 否则报错。
掘金文章:浅谈var、let和const区别——作者:马猴烧酒
1.String + Number => String ( "1"+1 = "2")
2.String + String => String ( "1"+"1"= "11")
3.Number + Number => Number ( 1+1= 2)
4.Boolean + String => String ( true + "1" = "true1" )
5.Boolean + Number => Number ( true + 1 = 2 )
6.Object + Object => String ( {}+{}="[object Object][object Object]")
7.Object + Array = String ( {} + [] = "[object Object]" ) ( [1,2]+{} = "12[obj]") ( [1,2,,]+[3]+{} = "1,2,3[obj]" )
实例:
a. 1 + null = 1;
b. 1 + undefined = NaN
c. "2" > "10" = true
d. "2" > 10 = false
e. 2 > "1t" =false
f. NaN == NaN => false
g. [] == [0] == [''] == ['0'] == false // true
h. if([] || [] == false){ return true };
URL解析:首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。
DNS查询:浏览器缓存->操作系统缓存->路由器缓存->ISP DNS缓存->根域名服务器查询
TCP连接:按我课本中TCP/IP网络模型来说
1.应用层:发起http请求
请求报头(Headers),请求方法,目标地址、遵循协议
请求body
2.传输层:TCP报文传输(TCP三次握手的建立)
3.网络层:IP协议查询Mac地址
4.链路层:以太网协议
在发送请求时从应用层层层包裹数据,每一层都要对数据进行封装。
处理请求:接受 TCP 报文后,会对连接进行处理,对HTTP协议进行解析(请求方法、域名、路径等),并且进行一些验证。
接受响应:浏览器接收到来自服务器的响应资源后,会对资源进行分析。首先查看 Response header,根据不同状态码做不同的事(比如上面提到的重定向)
渲染页面:不同的浏览器内核,渲染过程也不完全相同。
经典面试题:从 URL 输入到页面展现到底发生什么?——作者:Fundebug
1.函数的调用 this指向全局Window
2.方法调用
3.用new关键字创建一个新的函数调用、构造器调用
4.call、apply直接调用 bind创建一个实例调用
先解释浏览器的运行机制:
a.构建DOM树:渲染引擎解析HTML文档 首先将标签转换为DOM树中的DOM节点生成内容树(包括JS生成的标签)
b.构建渲染树:解析对应的CSS样式文件信息
c.布局渲染树:从根节点递归调用 计算每一个元素的大小、位置 给出每个节点的精确位置。
d.绘制渲染树:遍历渲染树
当元素的位置、大小以及其他属性确定下来后,浏览器会按照这些元素的特性绘制一遍并将内容呈现在页面上。
重绘(repaint):当一个元素外观的改变所触发的浏览器行为 浏览器会根据新特性重绘从而展示新的外观。
重排(重构|回流|reflow):当DOM树中的一部分元素因为大小、布局、隐藏等改变而需要重新构建就称之为回流(reflow),每个页面至少发生回流一次在第一次加载页面的时候。
重绘不一定重排,重排一定重绘。
触发重排例子:
a.页面渲染初始化
b.添加或删除可见的DOM元素
c.元素位置的改变,使用动画
d.元素尺寸的改变——大小、外边距、边框
e.浏览器窗口发生变化
f.填充的内容发生改变,文本图片改变而引起的计算
g.offsetLeft/Top/Height/Width, clientTop/Left/Width/Height, scrollTop/Left/Width/Height, width/height, getComputedStyle(), currentStyle(IE)
重绘重排的代价:耗时,导致浏览器卡慢
优化:浏览器自己的优化,浏览器本身会维护一个队里,把所有的回流、重绘的操作放入这个队列,当队列中的操作数量达到了一个阈值或者一个时间间隔就进行处理。这样把多个事件变成了一次回流。
另一种就是减少重绘和重排也就是减少DOM操作,但可以合并多次DOM和样式的修改并减少style的样式的请求。
1.直接改变元素的className
2.display:none
3.不要经常访问flush队列,如果要访问利用缓存
4.创建多个节点用DocumentFragment创建后一次性加入DOM树
防抖概念(debounce):在事件被触发n秒后再执行回调,如果这个时间间隔内又触发则重新计时
例子:频繁输入时则不发送数据,当停止输入后才发送
节流概念(throttle):规定一个单位时间内,只能触发一次函数。如果单位时间内触发多次,则只有一次生效。
例子:不过怎么输入,定时候会发送。
总结:
·防抖:search搜索联想 减少资源请求。 窗口发生resize的时候,用防抖使回流只触发一次。
·节流:鼠标不停点击,单位时间内只触发一次。监听滚动,滚到底部是否加载更多。
懒加载:懒加载是延迟加载。
案例:当访问一个page的时候,如果有img图片先用一张低像素低资源图把img占位,当图片出现在浏览器的视窗内再加载真实路径的图片。
预加载:提前加载资源,当用户查看时可从本地缓存直接渲染
案例:访问门户网站时,图片先加载到浏览器中,这样图片就会很快的加载出来,不会出现区域空白或者看到图片一片片渲染。
区别:一个是延迟加载或者不加载,而另一个是提前加载。
下面附上面上欢聚时代的大三学弟的博客
【HTTP】10分钟带你快速了解HTTP中常见的状态码(内附大量实例)
虽然现在很多公司不用JQuery这种轻量级框架了,但有些关于js的问题还是会问,就拿链式调用来说(涂鸦智能电话一面)
$('#luvsta').width('100px').height('100px');
实现原理:JQuery中提示了一个方法,该方法可以将原生JS元素对象 转换成JQ对象同时在一个对象里this指向的是该对象本身,我们在链式调用的时候,这些方法都存在于对象内部,因此所有this都可以访问这些方法且不会报错undefined。
var Jq = {
foo1:function(){
// do smt1
return this;
},
foo2:function(){
// do smt2
return this;
}
}
从例子直接开讲
function foo() {
console.log(this); // window
}
const foo1 = () => {
console.log(this); // window
}
var obj = {
name: function () {
console.log(this); // name:function(){}
}
}
var obj1 = {
name: () => {
console.log(this); // ()=>{}
}
}
(function(){
console.log(this); // window
})();
总结: 全局环境下this指向window对象,函数作为对象属性时,this指向对象的实例;
严格模式下箭头函数没有this指针。
语义化的优点:
引入HTML5语义化新标签
Promise对象,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,但Promise对象的状态不受外界影响,Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
另外在这些基础知识外还要补充学习事件循环(EventLoop)和Promise链式调用等;
关于Promise拓展的题目很多,但总结下来大同小异,近期针对Promise我准备单开一篇博客分享。
Object.prototype.toString.call(new Array());
1、字体系列属性
font:组合字体
font-family:规定元素的字体系列
font-weight:设置字体的粗细
font-size:设置字体的尺寸
font-style:定义字体的风格
font-variant:设置小型大写字母的字体显示文本,这意味着所有的小写字母均会被转换为大写,但是所有使用小型大写字体的字母与其余文本相比,其字体尺寸更小。
font-stretch:对当前的 font-family 进行伸缩变形。所有主流浏览器都不支持。
font-size-adjust:为某个元素规定一个 aspect 值,这样就可以保持首选字体的 x-height。
2、文本系列属性
text-indent:文本缩进
text-align:文本水平对齐
line-height:行高
word-spacing:增加或减少单词间的空白(即字间隔)
letter-spacing:增加或减少字符间的空白(字符间距)
text-transform:控制文本大小写
direction:规定文本的书写方向
color:文本颜色
3、元素可见性:visibility
4、表格布局属性:caption-side、border-collapse、border-spacing、empty-cells、table-layout
5、列表布局属性:list-style-type、list-style-image、list-style-position、list-style
6、生成内容属性:quotes
7、光标属性:cursor
8、页面样式属性:page、page-break-inside、windows、orphans
9、声音样式属性:speak、speak-punctuation、speak-numeral、speak-header、speech-rate、volume、voice-family、pitch、pitch-range、stress、richness、、azimuth、elevation
Cookie 是小甜饼的意思。顾名思义,cookie 确实非常小,它的大小限制为4KB左右。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到“记住密码”,这通常就是通过在 Cookie 中存入一段辨别用户身份的数据来实现的。
localStorage 是 HTML5 标准中新加入的技术,它并不是什么划时代的新东西。早在 IE 6 时代,就有一个叫 userData 的东西用于本地存储,而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今,localStorage 被大多数浏览器所支持,如果你的网站需要支持 IE6+,那以 userData 作为你的 polyfill 的方案是种不错的选择。
sessionStorage与localStorage的接口类似,但保存数据的生命周期与localStorage不同,做过后端的同学都知道Session这个词,翻译过来就是会话。而sessionStorage是前端的一个概念。它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但是叶敏啊关闭后,sessionStorage中的数据就会被清空。
三者的异同
数据上的生命周期的不同
Cookie 一般由服务器生成,可设置失效时间,如果在浏览器端生成cookie,默认是关闭后失效。
localStorage 除非被永久清除,否则永久保存。
sessionStorage 仅在当前会话会有效,关闭页面或浏览器后被清除
存放数据的大小不同
Cookie 一般为4kb
localStorage 和 sessionStorage 一般为5mb
与服务器端通信不同
Cookie 每次都会携带HTTP头中,如果使用cookie保存过多数据会带来性能问题
localStorage 和 sessionStorage 仅在客户端(即浏览器)中保存,不参与和服务器的通信。
易用性
Cookie 需要程序员自己来封装,原生的cookie接口不够友好
localStorage 和 sessionStorage 原生接口可以接受,可以封装来对Object和Array有更好的支持。
有八种继承方式
1.原型链继承
2.构造函数继承
3.原型式继承
4.组合继承
5.寄生继承
6.寄生组合继承
7.混入方式继承多个对象
8.ES6类Class继承extens
掘金文章:JavaScript常用八种继承方案 —— 作者:木易阳说
相同权重 以后写的为准
口诀:NNUSB
BFC概念
Formatting context(格式化上下文)是W3C CSS2.1规范中的一个概念,它是页面中的一块渲染区域,并有一套渲染规则,它决定了其子元素如何定位,以及和其他元素的关系和相互作用。
BFC是Black Formatting Contexts(块级格式化上下文) 属于定位方案的普通流
具有BFC特性的元素可以看做是隔离了的独立容器,容器里的元素不会在布局上影响到外面的元素,并且BFC具有普通容器所没有的一些特性。
如何处罚BFC:只要元素满足下面任何一种条件即可触发BFC特性:
a.body根元素
b.浮动元素:float除none以外的值
c.绝对定位元素:position (absolute、fixed)
d.display 为inline-block、table-cells、flex
e.overflow除了visible以外的值(hidden、auto、scroll)
BFC可以包含浮动元素(清除浮动),容器内元素浮动导致元素高度塌陷后,如果触发容器的BFC则容器会包裹浮动元素
BFC可以组织元素被浮动覆盖,两个平级块级元素,第一个元素浮动了给第二元素设置overflow:hidden 可以解决元素覆盖问题
null 表示一个对象是“没有值”的值,也就是值为“空”;
undefined 表示一个变量声明了没有初始化(赋值);
undefined不是一个有效的JSON,而null是;
undefined的类型(typeof)是undefined;
null的类型(typeof)是object;
再来一个例子:
null
Q:有张三这个人么?
A:有!
Q:张三有房子么?
A:没有!
undefined
Q:有张三这个人么?
A:有!
Q: 张三有多少岁?
A: 不知道(没有被告诉)
function getName(){
name = "luvsta"; // name成为一个全局变量,不会被回收
}
for(var i = 0 ; i < arr.len ; i++){
// 此时的i污染了全局变量i
}
var someResouce = getData();
setInterval(function(){
var node=document.getElementById('Node');
if(node){
node.innerHTML=JSON.stringify(someResouce)
}
},1000);
这样的代码很常见, 如果 id 为 Node 的元素从 DOM 中移除, 该定时器仍会存在, 同时, 因为回调函数中包含对 someResource 的引用, 定时器外面的 someResource 也不会被释放。
Ajax的全称:Asynchronous Javascript And XML。
异步传输+js+xml。
所谓异步,在这里简单地解释就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果它自己会根据设定进行后续操作,与此同时,页面是不会发生整页刷新的,提高了用户体验。
在实际项目中,更多的运用的是基于JQuery封装的ajax请求或者是基于Promise语法封装的fetch请求
基于JQuery的ajax请求
//请求参数
var list = {};
//
$.ajax({
//请求方式
type: "POST",
//请求的媒体类型
contentType: "application/json;charset=UTF-8",
//请求地址 对应后台接口
url: "http://127.0.0.1/admin/list/",
//数据,json字符串
data: JSON.stringify(list),
//请求成功的回调
success: function (result) {
console.log(result);
},
//请求失败的回调,包含具体的错误信息
error: function (e) {
console.log(e.status);
console.log(e.responseText);
}
});
基于Promise语法的fetch请求
export const ip = 'http://127.0.0.1:8081';
export default function request(method, url, body, history) {
method = method.toUpperCase();
if (method === 'GET') {
//fetch的GET不允许有body,参数只能放在url中
body = undefined;
} else {
body = body && JSON.stringify(body);
}
return fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: body
}).then((res) => {
if (res.status === 401) {
history.push('/');
return Promise.reject('Unauthorized.');
} else {
return res.json();
}
});
}
export const get = url => request('GET', url);
export const post = (url, body) => request('POST', url, body);
浅拷贝: 只拷贝对象的引用(指针),值还是指向原对象的内存
eg:
var foo = {
a : 1,
b : 2
}
var foo1 = foo; // 发生浅拷贝
foo1.a = 2;
console.log(foo.a); // ...2
深拷贝: 会拷贝创建一个一模一样的对象,与原对象不共享内存
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i];
// 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if (prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
如何让 a=2 ,b=4的值交换
算术运算
a = a + b;
b = a - b;
a = a - b;
异或运算
a = a ^ b;
b = a ^ b;
a = a ^ b;
ES6解构
[a,b] = [b,a];
数组特性
a = [a,b];
b = a[0];
a = a[1];
答: 不相等
如何给一个对象定义属性和赋值
obj.a = '';
obj['a'] = '';
Object.defineProperty();
语法定义Object.defineProperty();
使用说明
Object.defineProperty() [ obj , prop , desc ];
obj
为被定义或修改的对象prop
为被定义或修改的属性key
desc
参数如下value; // 对应key的value,默认为undefined
configurable; // 是否配置,以及能否删除,默认为false
writable; // 是否可更改,默认为false
enumerable; // 是否为枚举类型,默认为false
get; // 默认为undefined
set; // 默认为undefined
let proto = Object.defineProperties({}, {
foo: {
value: 'bar',
writable: false,
configurable: true,
enumerable:false,
get:function(){},
set:function(){}
}
})
console.log(proto);
SVG
SVG 是一种使用 XML 描述 2D 图形的语言。
SVG 基于 XML,这意味着 SVG DOM 中的每个元素都是可用的。您可以为某个元素附加 JavaScript 事件处理器。
在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形。
Canvas
Canvas 通过 JavaScript 来绘制 2D 图形。
Canvas 是逐像素进行渲染的。
在 canvas 中,一旦图形被绘制完成,它就不会继续得到浏览器的关注。如果其位置发生变化,那么整个场景也需要重新绘制,包括任何或许已被图形覆盖的对象。
区别
SVG
Canvas
bind()
ECMAScript还定义了一个叫bind()的方法。这个方法会创建一个函数的实例,其this值会被绑定给传入bind()函数的值。
var name = "name:";
var person = {name: "luvsta"};
function sayHi () {
console.log(this.name + ': hi')
}
var ISay = sayHi.bind(person);
ISay(); // luvsta:hi
见《Part 3:基础编程题》
开始一个全新的项目,每天都要元气满满呀~在划分模块和创建单页面组件时,常常写到name。嵌套路由中,index.vue极为常见,那么在vue中,export default { name: ‘xxx’} 中的name到底有啥作用呢?
以上就是vue.js中组件export default 中name的三种作用。调试和keep-alive是我们开发中常用的功能,关于组件的递归调用,还是第一次实践,递归时,大家一定要注意递归的条件,否则会进入死循环。
事件委托
事件委托就是利用事件冒泡机制指定一个事件处理程序,来管理某一类型的所有事件。
即:利用冒泡的原理,把事件加到父级上,触发执行效果。
好处:
- 只在内存中开辟了一块空间,节省资源同时减少了dom操作,提高性能
- 对于新添加的元素也会有之前的事件