# 面试题
每天记录一下自己对题的理解和学到的知识
# 1. 写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
key是给每一个vnode的唯一id,可以
依靠key
,更准确
, 更快
的拿到oldVnode中对应的vnode节点。
对于vue的diff算法,在进行新旧节点的开始与结束位置的四个节点进行sameVnode比较,若没有则会进行对每一个旧节点的key进行map新节点的key
# 更准确
因为带key就不是就地复用
了,在sameNode函数 a.key === b.key
对比中可以避免就地复用的情况。所以会更加准确。
# 更快
利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。
# 2. ['1', '2', '3'].map(parseInt) what & why ?
# parselnt()
这道题先要了解一下parseInt——用来解析字符串的,使字符串成为指定基数的整数。
parseInt(string, radix)
接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。该值介于 2 ~ 36 之间。
如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。
如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。
# map
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
他的callback接受三个参数
currentValue | 必须。当前元素的值 |
---|---|
index | 可选。当前元素的索引值 |
arr | 可选。当前元素属于的数组对象 |
- 运行情况
- parseInt('1', 0) //这个时候返回1
- parseInt('2', 1) //(1进制)返回NaN
- parseInt('3', 2) //(2进制)返回NaN
- map函数返回的是一个数组,所以最后结果为[1, NaN, NaN]
# 3. 什么是防抖和节流?有什么区别?如何实现?
# 防抖
防抖就是高频触发事件后在规定时间内只执行一次。如果在规定时间内再次触发,则重新计算时间(后很关键!)
function debounce(fn){
let timeout = null
return function(){
clearTimeout(timeout)//将上一个时间清除掉
let timeout = setTimeout(() => {//重新计算时间
fn.apply(this,argument)
},500)
}
}
# 节流
节流就是在高频触发时间,在规定时间内只执行一次,会稀释函数的执行频率
function throttle(fn){
let do = true
return function(){
if(!do){
return
}
do = false
setTimeout(() => {
fn.apply(this,argument)
do = true
},500)
}
}
# 4.介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
# set
类似数组,但里成员的值是唯一的(可以用来去重)Set是一个构造函数,用来生成一个Set数据结构(可以接受一个数组)
向set加入值时,不会发生类型转换,所以5 ‘5’不同,但是NaN只能加入一个(在set里面NaN是相同的,但是在js里面NaN===NaN is false!!!两个对象永远是不相等的)
const a = new Set()
const b = new Set([1,1,2,3,4,4,2,6])
a.add(1)//加入
a.size//成员数
a.has(1)//是否有该成员
a.delete(1)//删除
a.clear()//清除set没有返回值
Array.from()可以将Set转变成数组
遍历方法(因为Set没有键名只有键值,所以两个都是一个值)
a.keys()//返回键名
a.values()//返回键值 默认的遍历方法
a.entries()//返回键值对
a.forEach()
# WeakSet
类似于Set,不能重复的值的集合,但只接受对象
几乎与Set相同
# Map
类似于对象,键值对的集合,各种类型都能当作键“值值对应”
const a = new Map()
a.set(o,'a')
a.get(o) //'a'
a.has(o) //true
a.delete(o)
a.size
Set和Map都可以用来创建Map
const set = new Set([
['foo', 1],
['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1
const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3
对同一个键赋值,后值覆盖前值
Map在对键进行比较时,比较的是内存地址
遍历方式与Set相同(遍历顺序就是插入顺序)
[...Map]//将Map转换为数组
strMapToObj(Map)//将Map转化为对象
objToStrMap(obj)//将对象转化为Map
strMapToJson()//将Map转化为json
jsonToStrMap()//将json转化为Map
# WeakMap
WeakMap
结构与Map
结构类似,也是用于生成键值对的集合。
WeakMap
与Map
的区别有两点。
首先,WeakMap
只接受对象作为键名(null
除外),不接受其他类型的值作为键名。其次,WeakMap
的键名所指向的对象,不计入垃圾回收机制。键名弱引用,一旦不使用,则会删除
# 5. 介绍下深度优先遍历和广度优先遍历,如何实现?
# 深度优先遍历
先找一个顶点为初始点v1,先访问v1并且将v1标记为已访问;然后依次访问v1的未被访问的相邻节点v2进行深度优先遍历并且将v2标记为已访问,在对v2的相邻节点进行如果没有则回溯到v1,直到所有的点都被访问。
类似于树的先序遍历(顺便树的三种遍历方式
先序遍历:根——左——右
中序遍历:左——根——右
后序遍历:左——右——跟
)
let deepTraversal1 = (node, nodeList = []) => {
if (node !== null) {
nodeList.push(node)
let children = node.children
for (let i = 0; i < children.length; i++) {
deepTraversal1(children[i], nodeList)
}
}
return nodeList
}
let deepTraversal2 = (node) => {
let nodes = []
if (node !== null) {
nodes.push(node)
let children = node.children
for (let i = 0; i < children.length; i++) {
nodes = nodes.concat(deepTraversal2(children[i]))
}
}
return nodes
}
// 非递归
let deepTraversal3 = (node) => {
let stack = []
let nodes = []
if (node) {
// 推入当前处理的node
stack.push(node)
while (stack.length) {
let item = stack.pop()
let children = item.children
nodes.push(item)
// node = [] stack = [parent]
// node = [parent] stack = [child3,child2,child1]
// node = [parent, child1] stack = [child3,child2,child1-2,child1-1]
// node = [parent, child1-1] stack = [child3,child2,child1-2]
for (let i = children.length - 1; i >= 0; i--) {
stack.push(children[i])
}
}
}
return nodes
}
# 广度优先遍历
先找一个顶点为初始点v1,先访问v1并且将v1标记为已访问;然后对v1的所有相邻节点进行访问并标记排序(v2,v3,v4),在对排序中的第一个v2进行广度优先遍历,直到所有的点都被访问。
let widthTraversal2 = (node) => {
let nodes = []
let stack = []
if (node) {
stack.push(node)
while (stack.length) {
let item = stack.shift()
let children = item.children
nodes.push(item)
// 队列,先进先出
// nodes = [] stack = [parent]
// nodes = [parent] stack = [child1,child2,child3]
// nodes = [parent, child1] stack = [child2,child3,child1-1,child1-2]
// nodes = [parent,child1,child2]
for (let i = 0; i < children.length; i++) {
stack.push(children[i])
}
}
}
return nodes
}
# 6.请分别用深度优先思想和广度优先思想实现一个拷贝函数?
代码没写
思路:先判断是否为引用类型,对环状数据进行处理
深度优先:先对第一个值进行赋值,判断有无子节点,有——在对他的子节点进行遍历操作,无——返回
广度优先:先对第一个值进行赋值,判断有无子节点,对每一个子节点进行赋值,在对第一个子节点进行判断有无子节点,有——进行所有孙子节点赋值再重复,无——返回,进行第二个子节点
#
# 7. ES5/ES6 的继承除了写法以外还有什么区别?
# 8. setTimeout、Promise、Async/Await 的区别
事件循环中分为宏任务队列和微任务队列。 其中settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行; promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。
# 宏任务
每次执行栈的代码就是一个宏任务。
# 微任务
可以理解是在当前宏任务 执行结束后立即执行的任务
# setTimeout
console.log('script start') //1. 打印 script start
setTimeout(function(){
console.log('settimeout') // 4. 打印 settimeout
}) // 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end') //3. 打印 script start
// 输出顺序:script start->script end->settimeout
# Promise
Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行
console.log('script start')
let promise1 = new Promise(function (resolve) {
console.log('promise1')
resolve()
console.log('promise1 end')
}).then(function () {
console.log('promise2')
})
setTimeout(function(){
console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout
# async/await
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start');
async1();
console.log('script end')
// 输出顺序:script start->async1 start->async2->script end->async1 end
# 9.Async/Await 如何通过同步的方式实现异步
# 10. 常见异步笔试题
//请写出输出内容
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
执行settimeout()会在所有的最后面执行
async 如果遇到await 会立刻执行await后面的内容然后跳出,继续执行事件栈内容,等执行完在进行回来执行
promise resolve()会异步执行.then的内容
# 11. 请写出如下代码的打印结果
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
Foo.prototype.a = function() {
console.log(3)
}
Foo.a = function() {
console.log(4)
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
/*
4
2
1
*/
在进行第一个Foo.a调用时构造函数Foo还没有声明,所以为4
let obj = new Foo();接着对Foo进行声明。产生实例
1. 将全局的 Foo 上的直接方法 a 替换为一个输出 1 的方法。
2. 在新对象上挂载直接方法 a ,输出值为 2。
# 12.算法手写题
已知如下数组:
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
flat()函数——把二位函数进行展开
[1, 2, [3, 4]].flat(x)
// [1, 2, 3, 4]
参数x为要拉平的层数默认为1
先拉平在通过set进行去重最后sort进行排序
sort的使用
arr.sort(function(a,b){
return b-a
})//这是一个降序排列
# 13.怎么实现一个new
new的四个步骤
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
(3) 执行构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象。
function _new(fn, ...arg) {
const obj = Object.create(fn.prototype);//使用指定的原型对象及其属性去创建一个新的对象
const ret = fn.apply(obj, arg);// 绑定 this 到obj, 设置 obj 的属性
return ret instanceof Object ? ret : obj;
}
# 14.谈谈你对TCP三次握手和四次挥手的理解
# 三次握手
你好,我是A
收到,我是B
那么我们链接了
# 四次挥手
男:我要挂了哦 女:等哈,我还要敷面膜 女:我敷完了,现在可以挂了 男:我舍不得挂,你挂吧 女:好吧,我挂了 男:等了2MSL听见嘟嘟嘟的声音后挂断
A:B,不好意思,我这边需要关闭连接了,你准备一下?(发了一个fin信号给B,等待回应)
B:好的A,我收到你的关闭信号了,我还有数据没发好,你等我下(回应A,带回去ACK的最后一个信息,失败可以重发)
B:A老弟,我好了,我可以关闭了,给你最后说一下,等下你回应我的话,我就直接关了;
A:好的老哥,我回应你一下,你收到就关闭吧,不用理我(发完这条信息后,进入time_wait状态)
B:(收到ack信息,直接就关闭了),此过程不产生数据的交互,不算挥手次数
A:等待2MSL(最大报文段生存时间)后,B没东西给过来,我也关了;
# 15. 有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣
Object.prototype.toString.call() 、 instanceof 以及 Array.isArray()
# Object.prototype.toString.call()
每一个继承 Object 的对象都有 toString
方法,如果 toString
方法没有重写的话,会返回 [Object type]
,其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用 toString
方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文。
这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。
Object.prototype.toString.call('An') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call({name: 'An'}) // "[object Object]"
# instanceof
instanceof
的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype
。
使用 instanceof
判断一个对象是否为数组,instanceof
会判断这个对象的原型链上是否会找到对应的 Array
的原型,找到返回 true
,否则返回 false
。
但 instanceof
只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。
# Array.isArray()
功能:用来判断对象是否为数组
# typeof
typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,
typeof ''; // string 有效
typeof 1; // number 有效
typeof Symbol(); // symbol 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Function(); // function 有效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
- 对于基本类型,除 null 以外,均可以返回正确的结果。
- 对于引用类型,除 function 以外,一律返回 object 类型。
- 对于 null ,返回 object 类型。
- 对于 function 返回 function 类型。
# 16.实现一个call或 apply
# call的核心
- 将函数设为对象的属性
- 执行&删除这个函数
- 指定
this
到函数并传入给定参数执行函数 - 如果不传入参数,默认指向为 window
Function.prototype.call2 = function(content = window) {
content.fn = this;//绑定this指向
let args = [...arguments].slice(1);//传值
let result = content.fn(...args);//执行
delete content.fn;//删除函数
return result;
}
# apply的核心
与call的不同就是参数形式不一样
Function.prototype.apply2 = function(context = window) {
context.fn = this
let result;
// 判断是否有第二个参数
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
# 17.实现一个promise
三种状态pending| fulfilled(resolved) | rejected
当处于pending
状态的时候,可以转移到fulfilled(resolved)
或者rejected
状态
当处于fulfilled(resolved)
状态或者rejected
状态的时候,就不可变。
# promise的基本使用
var promise = new Promise((resolve,reject) => {
if (操作成功) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function (value) {
// success
},function (value) {
// failure
})
# promise的实现
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
//同时,需要在myPromise的原型上定义链式调用的then方法
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
# 18.[typeof null, null instanceof Object]
答案:object false
typeof判断只有null、object、function、array等应用类型为object
null虽然typeof为object,打在他的——proto上为空
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上
# 19.[ [3,2,1].reduce(Math.pow), [ ].reduce(Math.pow) ]
reduce接受两个参数, 一个回调, 一个初始值. 回调函数接受四个参数 previousValue, currentValue, currentIndex, array 需要注意的是 If the array is empty and no initialValue was provided, TypeError would be thrown. 所以第二个表达式会报异常. 第一个表达式等价于 Math.pow(3, 2) => 9; Math.pow(9, 1) =>9
从左往右进行执行
答案 9 error
# 20. var val = 'smtg';
# console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');
这道题考的是执行的优先级,因为+的优先级大于?的优先级
所以先执行加得'Value is true' ? 'Something' : 'Nothing'
最后结果为Something
# 21.js的最大值
Math.pow(2, 53) == 9007199254740992 是可以表示的最大值. 最大值加一还是最大值
var END = Math.pow(2, 53);
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
count++;
}
console.log(count);
所以函数是一个无限循环,因为最大值加一还是最大值
# 22. 稀疏数组
在js中,数组中没有元素的,在遍历时会跳过这些缝隙
var ary = [0,1,2];
ary[10] = 10;
ary.filter(function(x) { return x === undefined;});
他会返回一个空数组
因为js对稀疏数组遍历时,会跳过缝隙
# 23. var x = [].reverse;x();
方法 reverse是翻转数组并返回
所以它会返回这个this,可是 x 执行的时候是上下文是全局. 那么最后返回的是 window.
# 24. [1 < 2 < 3, 3 < 2 < 1]
这道题从左往右进行
需要对第一个进行判断
【true<3】=》【1<3】=》true
同理3《2《1也是
# 25.
# 即使正则的字面量一致, 他们也不相等.
var a = /123/,//false
b = /123/;//false
a == b
a === b
字面量相等的数组也不相等.数组在比较大小的时候按照字典序比较
var a = [1, 2, 3],
b = [1, 2, 3],
c = [1, 2, 4]
a == b//false
a === b//false
a > c//false
a < c//true
# 26. 函数的名字不可变.
function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]
函数的name 属性返回一个函数声明的名称
# 27. Math.max Math.min
var min = Math.min(), max = Math.max()
min < max//false
因为 Math.min 不传参数返回 Infinity, Math.max 不传参数返回 -Infinity
# 28.
var a = ?;
if(a == 1 && a == 2 && a == 3){
conso.log(1);
}
因为==会进行隐式类型转换 所以我们重写toString方法就可以了
# 方法一:因为会进行隐式转换所以它会调用a的tostring方法。
var a = {
i: 1,
toString() {
return a.i++;
}
}
# 方法二:利用valueOf
let a = {
i: 1,
valueOf () {
return a.i++
}
}
# 29. 实现一个 sleep 函数,比如 sleep(1000) 意味着等待1000毫秒,可从 Promise、Generator、Async/Await 等角度实现
# 使用promise
const sleep = (time)=> {
return new Promise (resolve => setTimeout(resolve,time))
}
sleep(1000).then(() =>{
//操作
})
# 使用Async/Await
function sleep(time) {
return new Promise(resolve => setTimeout(resolve,time))
}
async function output() {
let out = await sleep(1000);
console.log(1);
return out;
}
output();
# 使用Generator
function* sleepGenerator(time) {
yield new Promise(function(resolve,reject){
setTimeout(resolve,time);
})
}
sleepGenerator(1000).next().value.then(()=>{console.log(1)})
# 30.输出以下代码执行的结果并解释为什么
var obj = {
'2': 3,
'3': 4,
'length': 2,
'splice': Array.prototype.splice,
'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)
还是没有弄懂!!!!
# 31
var a={n:1};
var b=a;
a.x=a={n:2};
console.log(a.x);
console.log(b.x);
这道题主要是连续赋值,因为.的优先级比=要高,所以获得a.x为undefined
# 32. 冒泡排序如何实现,时间复杂度是多少, 还可以如何改进?
let arr=[]
for (let i = 0 ; i < arr.length ; i++){
for(let j = i+1 ; j < arr.length ; i++){
if(arr[i] > arr[j]) {
let beforeVar = arr[i]
arr[i] = arr[j]
arr[j] = beforeVar
}
}
}
空间复杂度为 O(n2)
改进的........
# 33. 某公司 1 到 12 月份的销售额存在一个对象里面
如下:{1:222, 2:123, 5:888},请把数据处理为如下结构:[222, 123, null, null, 888, null, null, null, null, null, null, null]。
let data = {1:222, 2:123, 5:888};
let arr = Array.from({length:12}).map((it,i)=> data[i+1]||null);
console.log(arr)
array.from 方法从一个类似数组或可迭代对象中创建一个新的,浅拷贝的数组实例。
先创建一个12的数组,在对每一个进行赋值,通过对象的obj[index]获得值
# 34. 箭头函数与普通函数的不同
一、箭头函数是匿名函数,不能作为构造函数,不能使用new
二、箭头函数不绑定arguments,取而代之用rest参数...解决
三、箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
四、箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
五、箭头函数不能当做Generator函数,不能使用yield关键字
六、箭头函数没有原型属性
# 35.解决递归问题
可以通过return写法进行改写
# 36.对于深拷贝(json.parse(json.stringify()))的注意事项
如果obj里面有时间对象,时间将只是字符串的形式。而不是时间对象;
如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
# 37.造成内存泄露的几种情况
1 闭包
2 意外的全局变量
3 定时器setTimeout setInterval
4 如果在mounted/created 钩子中使用了$on,需要在beforeDestroy 中做对应解绑($off)处理
5 DOM对象与JS对象相互引用
6 给DOM对象添加的属性是一个对象的引用
7 从外到内执行appendChild。这时即使调用removeChild也无法释放
8 反复重写同一个属性会造成内存大量占用(但关闭IE后内存会被释放)
# 38.[[1,2,3],{name:123},[[123],12]](不使用flat())
可以通过循环
可以通过arr1.reduce((acc, val) => acc.concat(val), []);
# 39. css选择器的优先级
!important
- 内联样式(1000)
- ID选择器(0100)
- 类选择器/属性选择器/伪类选择器(0010)
- 元素选择器/关系选择器/伪元素选择器(0001)
- 通配符选择器(0000)
# 40.BFC
# 什么是BFC
BFC 全称为块级格式化上下文 (Block Formatting Context) 。BFC是 W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位以及与其他元素的关系和相互作用,当涉及到可视化布局的时候,Block Formatting Context提供了一个环境,HTML元素在这个环境中按照一定规则进行布局。一个环境中的元素不会影响到其它环境中的布局。比如浮动元素会形成BFC,浮动元素内部子元素的主要受该浮动元素影响,两个浮动元素之间是互不影响的。这里有点类似一个BFC就是一个独立的行政单位的意思。可以说BFC就是一个作用范围,把它理解成是一个独立的容器,并且这个容器里box的布局与这个容器外的box毫不相干。
# 触发BFC的条件
根元素或其它包含它的元素
浮动元素 (元素的 float
不是 none
)
绝对定位元素 (元素具有 position
为 absolute
或 fixed
)
内联块 (元素具有 display: inline-block
)
表格单元格 (元素具有 display: table-cell
,HTML表格单元格默认属性)
表格标题 (元素具有 display: table-caption
, HTML表格标题默认属性)
具有overflow
且值不是 visible
的块元素
弹性盒(flex
或inline-flex
)
display: flow-root
column-span: all
# BFC的约束规则
- 内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流)
- 处于同一个BFC中的元素相互影响,可能会发生外边距重叠
- 每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反),即使存在浮动也是如此
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然
- 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算
- 浮动盒区域不叠加到BFC上
# BFC可以解决的问题
- 垂直外边距重叠问题
- 去除浮动
- 自适用两列布局(
float
+overflow
)
# 41.盒模型
# W3C盒子模型
box-sizing: content-box
(W3C盒子模型):元素的宽高大小表现为内容的大小。
# IE盒子模型
border-box
(IE盒子模型):元素的宽高表现为内容 + 内边距 + 边框的大小。背景会延伸到边框的外沿。
# 42. 跨域
# 跨域三要素
- 协议、IP和端口不一致都是跨域行为
# 43. 图片预加载、懒加载
# 懒加载的实现步骤?
1)首先,不要将图片地址放到src属性中,而是放到其它属性(data-original)中。 2)页面加载完成后,根据scrollTop判断图片是否在用户的视野内,如果在,则将data-original属性中的值取出存放到src属性中。 3)在滚动事件中重复判断图片是否进入视野,如果进入,则将data-original属性中的值取出存放到src属性中。
# 预加载的方法有哪些?
方法一:用CSS和JavaScript实现预加载 方法二:仅使用JavaScript实现预加载 方法三:使用Ajax实现预加载
# 44.Promise
# Promise.finally
finally
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
# Promise.all()
将多个promise实例包装成一个新的promise实例——只有当所有的promise实例的状态都变为fulfilled,所有promise实例的返回值组成数组传递给新的promise的回调
# Promise.race()
将多个promise实例包装成一个新的promise实例——只要有一个promise实例的状态变为fulfilled,率先改变的promise实例的返回值传递给新的promise的回调
# Promise.resolve()
将现有对象转化为Promise对象,状态为resolve
# Promise.reject()
将现有对象转化为Promise对象,状态为reject
# 45.&& || 的判断用法
对于 a&&b,如果a为true,则返回b;如果a为false,则返回a;
对于a||b,如果a为true,则返回a;如果为false,则返回b;
可以用来代替一些v-if判断
# 46, 过滤空值
let result1 = [1, 2, 0, undefined, null, false, ''].filter(Boolean);
console.log(result1); [1,2]
# 47,引用对象赋值
let person = { name : 'ludia' }
const number = [person]
person = null
console.log(number)//[{name : 'ludia' }]
首先我们声明了一个拥有 name
属性的对象 person
。
然后我们又声明了一个变量 members
. 将首个元素赋值为变量 person
。 当设置两个对象彼此相等时,它们会通过 引用 进行交互。但是当你将引用从一个变量分配至另一个变量时,其实只是执行了一个 复制 操作。(注意一点,他们的引用 并不相同!) 我们没有修改数组第一个元素的值,而只是修改了变量 person
的值,因为元素(复制而来)的引用与 person
不同。members
的第一个元素仍然保持着对原始对象的引用。当我们输出 members
数组时,第一个元素会将引用的对象打印出来。
# 48,
var a = [1,2,12]
var b = [3,5,21]
a.concat(b)
a.sort()
console.log(a)//1,12,2
这道题主要考察concat不改变原数组
sort是对字符串进行排序的(如果要对数组进行排序,需要写一个比较方法
比较函数应该具有两个参数 a 和 b,其返回值如下:
若 a 小于 b,即 a - b 小于零,则返回一个小于零的值,数组将按照升序排列。
若 a 等于 b,则返回 0。
若 a 大于 b, 即 a - b 大于零,则返回一个大于零的值,数组将按照降序排列。
a-b升序 b-a降序