每天进步一点点系列:3.29

大家好,我是梅巴哥er


从今天开始,开一个《每天进步一点点:日期》的系列内容。

该系列会以问答的形式,整理一些前端问题。

每天会对博客进行更新。

如果你有问题在本系列中没提到,可以评论下方留言,我会在该篇结尾处加上你提出的问题,并给出自己的解答。

注:博文内写的回答,并不一定正确,也不一定全面,只是自己的理解。回答后,还会查资料,不断进行修正。切勿模仿里面的回答,最多只可作为参考,谢谢!


1,问:怎么做前端优化

答:

  • 图片
    • 使用精灵图,减少http请求次数
    • 使用favicon.ico
    • 使用懒加载技术
    • 合理利用缓存
    • 使用WebP、svg格式的图片
  • css方面
    • css样式从外部引入,放入head标签里
    • 避免使用css表达式
    • 减少选择器的嵌套
    • 压缩css代码
    • 使用link,少使用@Import
  • js方面
    • 最大化合并JS、CSS模块
    • js代码从外部引入,并放入body的最下面
    • 启用事件委托
    • 减少DOM操作
    • 清除废弃的事件监听
    • 减少iframe的使用,因为会阻塞页面onload事件加载
    • 使用defer,async异步加载
  • http方面
    • 减少http请求次数
    • 避免重定向,因为要多一步去别的地方找资源
    • 减少iframe的使用,因为会增加请求
    • 避免404错误

2,问:说说css盒模型

答:

  • 所有html元素都可以看作盒子。盒模型的本质就是盒子,由内容,边距和边框组成的区域。

  • 盒模型分为标准模型和怪异模型(IE盒子或非标准盒子)。

  • 标准盒子是的宽度就是content的宽度。符合W3C规范。html盒子默认是标准盒子。也可以用用属性box-sizing: content-box来规定。

  • 怪异模型的宽度是content的宽度 + padding宽度 + border宽度。可以用属性box-sizing: border-box来规定。

IE8及以下版本存在浏览器的兼容性,可以在HTML页面声明 <!DOCTYPE html>即可。


3,问:BFC是什么?讲讲BFC

答:

  • BFC是块级格式化上下文。是一块独立的区域,在区域内,有自己独立的布局规则,不受外界环境影响。

  • BFC的布局规则:

    • 区域内的盒子会在垂直方向上一个接一个的放置
    • 相邻的盒子的距离由margin绝对,会发生边距重叠。
    • BFC区域不会被浮动盒子覆盖
    • 计算BFC区域的高度时,里面的浮动元素也会参与计算
  • 触发BFC区域的属性:

    • float值为left/ right
    • position的值为absolute/ fixed
    • display的值为inline-block/ flex/ inline-flex/ table-cell/ table-caption
    • overflow的值为hidden/ scroll/ auto
  • 根据BFC的特性和触发条件,可以轻松解决或解释很多常见问题:

    • 比如:利用规则4,可以清除浮动。把父盒子变为BFC后,就可以计算高度。
    • 比如一个盒子浮动,另一个盒子不浮动,怎么解决不浮动盒子被覆盖的问题?那就是把不浮动盒子变成BFC即可。

4,问:flex布局

答:

  • flex布局就是弹性布局,通过设置display: flex属性和值,让盒子变成一个可伸缩的弹性盒子。
  • flex在布局方向上,有两个轴。一个是沿X方向的水平轴(横轴),一个是沿Y轴方向的交叉轴(竖轴)
  • 设置盒子内的项目排列方向,通过属性flex-direction设置。箭头代表方向。
    • 值raw:→(默认)
    • 值raw-reverse: ←
    • 值column: ↓
    • 值column-reverse: ↑
  • 盒子内的项目在一行或一列排满后,是否换行,由属性flax-wrap决定。
    • 值nowrap:不换行(默认)
    • 值wrap: 换行。第一行排满,再去第二行排列。竖轴也同理。
    • 值wrap-reverse:换行。但是项目是先从最下面一行开始排的,排满之后往上面一行切换,继续排。
  • 属性flex-flow,用户把排列方向和是否换行两个属性合并在一起写。
  • 横轴方向的对齐方式,由属性justify-content决定。
    • 值flex-star: 靠左对齐
    • 值flex-end:靠右对齐
    • 值center:居中对齐
    • 值space-between:左右两端对齐,项目之间的间隔都相等
    • 值space-around: 每个项目两侧的间隔相等。
  • 竖轴方向的对齐方式,由属性align-items决定。
    • 值flex-start: 上对齐
    • flex-end: 下对齐
    • center: 居中(垂直方向的居中)
    • baseline: 项目的第一行文字的基线对齐
    • stretch: 默认值。项目被拉伸。如果项目不设置高度,则在高度上顶满容器。
  • 以上的属性,都是添加到flex盒子上的,用来控制整体项目的。flex还有一些控制单个项目的属性。
  • 项目的排列顺序,由order属性决定。order的值是数字,默认是0.使用方法是,先给父盒子设置flex属性,然后规定项目的排列方向,再给项目设置order,数值越小,位置越靠前。假如横轴上排列有3个子盒子,如果给第一个盒子设置order: 1;,那么这个盒子就会跑去最后面,因为另外两个盒子的默认值是0,小于1,所以这个盒子要跑去最后面。
  • 项目是否要放大,由属性flex-grow决定。值是数字,默认为0,就是不放大。当横轴上排列了3个盒子,其中一个盒子设置了flex-grow: 1;,则这个盒子会把整行除了其他两个盒子外的区域都填满。如果3个盒子都设置flex-grow: 1; ,则这3个盒子会在同一行上平分宽度。如果其中一个盒子的值是2,其他两个的值是1,那么这个盒子占据的区域,要比另外俩盒子占据的区域大一些,但是并不是2倍哦。我做了一个demo,父盒子宽度是200px,3个子盒子宽度原始值是50px。现在给盒子1设置放大值为2,盒子2和3都设置放大1倍,最后的结果是,盒子1的宽度是75px,盒子2和3的宽度是62.5px。
  • 项目是否缩小,由属性flex-shrink决定。默认是1,即同一行的空间不足时,项目盒子会缩小自己的宽度。举例一个demo。仍然设置父盒子200px,3个子盒子中,盒子1和2的宽度为50px,盒子3的宽度设置为150px,3个盒子在横轴上的宽度之和明显超过了父盒子的200px。这时候,查看各个盒子的宽度,盒子1和2的宽度变为40px,盒子3的宽度实际为120px。各自都缩小了20%, flex-shrink的值也可以设置为0,表示当宽度不足时,该盒子不缩小。它会挤占其他盒子的空间,让其他盒子缩小。
  • 项目的初始宽度,由属性flex-basis决定。因为盒子都会放大或缩小,所以flex-basis的值一般都是用auto的。
  • flex属性,是用来合并放大、缩小和初始值的写法的。常用的值有两个,即auto (1 1 auto) 和 none (0 0 auto)。auto表示这个项目会自动放大缩小来填满空间,none表示这个项目不放大也不缩小。
  • flex常用来做弹性布局,实际开发中,用的也特别多。缺点是还有点兼容性问题。要IE11才支持。(IE不愧是开发界的毒瘤)

5,问: js有哪些数据类型?怎么判断数据类型? typeof null的结果是什么?

答:

  • js的基本数据类型有:string, number, boolean, null, undefined, symbol(ES6加的)
  • js的引用类型有:array, function, object 。(Date, Math之类的就不单独说了,这些都是一种可以调用的函数)
// 外面先来看一组demo
// 可以看出,typeof可以判断string , number,boolean,undefined,symbol,这5种基本数据类型的直接值,但是不能判断它们实例的类型
// 但是可以判断function, object的new实例
// typeof不能判断null和array
console.log(typeof 'a') // string
console.log(typeof 1) // number
console.log(typeof true) // boolean
console.log(typeof null) // object
console.log(typeof undefined) // undefined
console.log(typeof Symbol()) // symbol
console.log('-----继续')
console.log(typeof new String('a')) // object
console.log(typeof new Number(1)) // object
console.log(typeof new Boolean(true)) // object
console.log('-----继续')
console.log(typeof [1]) // object
console.log(typeof function(){}) // function
console.log(typeof {}) // object
// 我们再来看一组demo
// 可以知晓,instanceof可以判断实例,可以判断引用类型的直接值,
// 但是不能判断基本数据类型的直接值,
// 也不能判断null和undefined
console.log( 'a' instanceof String) // false
console.log(new String('a') instanceof String) // true
console.log(null instanceof Null) // 报错
console.log(1 instanceof Number) // false
console.log(new Number(1) instanceof Number) // true
console.log([] instanceof Array) // true
console.log({} instanceof Object) // true
console.log(function(){} instanceof Function) // true
// 最后,我们再来看一组demo
// 可以知晓,Object.protorype.toString.call()可以判断所有数据类型
// 不管是直接值,还是实例
console.log(Object.prototype.toString.call('a'))
console.log(Object.prototype.toString.call(new String('a')))
console.log(Object.prototype.toString.call(1))
console.log(Object.prototype.toString.call(new Number(1)))
console.log(Object.prototype.toString.call(true))
console.log(Object.prototype.toString.call(new Boolean(true)))
console.log(Object.prototype.toString.call(null))
console.log(Object.prototype.toString.call(undefined))
console.log(Object.prototype.toString.call([]))
console.log(Object.prototype.toString.call({}))
console.log(Object.prototype.toString.call(function(){}))
console.log(Object.prototype.toString.call(new Function()))
console.log(Object.prototype.toString.call(new Array([])))


6,问:js原型链 讲解一下

答:

  • js的构造函数,例如function Star() { },他的原型是Star.protorype。假设构造函数Star上有两个实例,分别是star1和star2,当通过star1添加属性和方法时,star2是没办法调用的。为了解决这个问题,就创造了原型Star.protorype。
// 代码举例
function Star() {
    this.name = 'dilireba'
}
var star1 = new Star()
var star2 = new Star()
star1.age = 18
console.log(star1.age) // 18
console.log(star2.age) // undefined
  • 为了方便查找对应的原型,创造了__proto__属性。每个对象都有一个__proto__属性。
  • Star的原型是Star.protorype,Star.protorype的constructor又指回了Star本身。Star通过new创建了自己的实例,实例的__proto__又指向了Star的原型Star.protorype。Star.protorype的__proto__又指向了Object的原型Object.prototype。Object.prototype的__proto__最终指向null。这就是原型链。原型链提供了一种查询原型对应关系的途径。
  • 用图表示就是:
    在这里插入图片描述

7,问:手写instanceof

答:

  • a instanceof B 表示a可以通过原型链查找到B的原型。即a.__proto__ = B.prototypea.__proto__.__proto__ = B.prototype。一直查找,直到a.__proto__ = null
function useInstance(a, obj) {
	// 判断a的__proto__是否为空
	if(a.__proto__ === null) {
		return false
	// 判断a的第一步查找有没有找到
	} else if(a.__proto__ === obj.prototype) {
		return true 
	} else {
	// 如果没找到,就继续沿着原型链查找
		return useInstance(a.__proto__, obj)
	}
}

8,问:谈谈call, apply, bind的区别

答:

  • 三兄弟都是改变this指向的
  • call,语法是A.call(thisArg, arg1, arg2…)。里面的参数都不是必须的,也可以有多个参数。调用的时候会理解执行。返回的是this和参数调用A方法得到的值。
  • apply,语法是A.apply(thisArg, array)。第一个参数是必须的,第二个参数是数组形式。而且参数最多只能有俩。调用的时候会立即执行。返回的是this和参数调用A方法得到的值。
  • bind,也可以有多个参数,不会立即执行,而是需要的时候才执行。返回的是一个带有新的this的函数。

9,手写bind

答:

Function.prototype.useBind = function (thisArg) {
	if(typeof this !== 'function') {
		return 
	}
	var _self = this 
	var args = Array.prototype.slice.call(arguments, 1)
	return function() {
		return _self.apply(thisArg, args.concat(Array.prototype.slice.call(arguments)))
	}
}

10,问:箭头函数和普通函数的区别

答:

  • 箭头函数没有自己的this,它的this来自于父级作用域
  • 箭头函数没有自己的arguments
  • 箭头函数不能使用new
// 举个demo
// 在谷歌浏览器下输出结果(和编辑器下的结果会稍微不一样)
var a = 1
var obj = {
    a: 2,
    fn1: function() {
        var a = 3
        console.log(this.a) // 2
        console.log(this) // obj
    },
    fn2: () => {
        var a = 4
        console.log(this.a) // 1
        console.log(this) // window
    }
}
obj.fn1()
obj.fn2()
// 可以得知,普通函数在对象里,指向的是自己的调用者
// 箭头函数没有自己的this,在对象里也不是指向自己的调用者
// 而是指向了他的父级作用域window,并获取了window下的值

11,问:函数的this指向

答:

  • 普通函数的this指向window,在严格模式下,指向undefined
  • 定时器的this指向window
  • 箭头函数没有自己的this,他会去找父级作用域里找值,this就指向父级作用域
  • 对象中的函数,指向调用者
  • 事件中的this指向绑定事件的节点
  • 构造函数中的this指向构造函数本身

12,问:手撕一个调度器,第一秒输出1,第二秒输出2,以此类推,第10秒后输出10,然后停止输出。

答:

// 方法一:for循环
function scheduler(n) {
    for(let i = 1; i < n+1; i++) {
        setTimeout(function() {
            console.log(i)
        }, 1000*i)
    }
}
scheduler(10)

// 方法二:递归
var i = 0
function scheduler(n) {
    i++
    setTimeout(function() {
        console.log(i)
        i === n || scheduler(n)
    }, 1000)
}
scheduler(10)

// 方法三:promise
// 一直.then下去,可能写的不好
function scheduler(n) {
    return new Promise((resolve, reject) => {
        setTimeout(function() {
            console.log(n)
            resolve()
        }, 1000)
    })
}
scheduler(1)
.then(() => scheduler(2))
.then(() => scheduler(3))
... 
.then(() => scheduler(10))

13,问:TCP(三次握手,四次挥手,流量控制,拥塞控制)

答:

  • TCP三次握手用于客户端和服务器的连接
    • 第一次:客户端连接服务器,向服务器发送连接请求(服务器大哥,我想和你说话,嘤嘤嘤~
    • 第二次:服务器接收客户端的连接请求,向客户端返回应答(好的,我知道了,我准备好了
    • 第三次:客户端确认收到服务器的应答,并告诉服务器自己收到了应答。连接完成,可以发送数据了。(服务器大哥,我知道你也想和我说话了,那我们开始说话吧,嘻嘻嘻~
  • TCP四次挥手用于断开客户端和服务器的连接
    • 第一次:客户端向服务器发送断开连接的请求(我说完了,不想说了
    • 第二次:服务器接收到客户端的断开连接请求,向客户端返回应答,并确认客户端的状态(好了,我知道你不想说了。你真的不想和我说话了吗?此时服务器能向客户端发送应答,但客户端不能向服务器发送请求了)
    • 第三次:客户端接收到服务器的应答,进入等待,然后断开连接。
    • 第四次:服务器确认客户端的状态,也关闭连接。(他确实不想和我说话了,电话都挂了,那我也挂吧
  • TCP的流量控制
    • 如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。
    • 利用滑动窗口实现流量控制
  • TCP的拥塞控制
    • 拥塞:即对资源的需求超过了可用的资源。若网络中许多资源同时供应不足,网络的性能就要明显变坏,整个网络的吞吐量随之负荷的增大而下降。
    • 拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。
    • 慢启动(slow-start)、拥塞避免(congestion avoidance)、快速重传(fast retransmission)和快速恢复(fastrecover)解决拥塞。

14,问:React的生命周期

答:

  • react生命周期分为3个阶段
    • 挂载,渲染,卸载

以上。时间晚了,明天继续。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值