最近前端面试题目复盘

259 阅读6分钟

一、css选择器优先级

不同级别
1、在属性后面使用 !important 会覆盖页面内任何位置定义的元素样式。
2、作为style属性写在元素内的样式
3、id选择器
4、类选择器
5、元素选择器
6、通配符选择器
7、浏览器自定义或继承
总结排序:!important > 行内样式 > ID选择器 > 类选择器 > 元素选择器 > 通配符选择器 > 继承 > 浏览器默认属性 同一级别
(1) 同一级别中后写的会覆盖先写的样式
(2) 同一级别css引入方式不同,优先级不同
总结排序:内联(行内)样式 > 内部样式表 > 外部样式表 > 导入样式(@import)。

二、 什么是闭包?

结论:闭包找到的是同一地址中父级函数中对应变量最终的值 1、概念 闭包函数:声明在一个函数中的函数,叫做闭包函数。 闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。 2、特点 让外部访问函数内部变量成为可能; 局部变量会常驻在内存中; 可以避免使用全局变量,防止全局变量污染; 会造成内存泄漏(有一块内存空间被长期占用,而不被释放) 3、闭包的创建:­­­ 闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,每次外部函数执行的时 候,外部函数的引用地址不同,都会重新创建一个新的地址。但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。 闭包内存泄漏为: key = value,key 被删除了 value 常驻内存中; 局部变量闭包升级版(中间引用的变量) => 自由变量;

闭包的优缺点

当函数执行完毕,本作用域内的局部变量会销毁。

闭包的优点:

  • 1.能够读取函数内部的变量
  • 2.让这些变量一直存在于内存中,不会在调用结束后,被垃圾 回收机制回收

闭包的缺点:

  • 由于闭包会使函数中的变量保存在内存中,内存消耗很大,所以不能滥用闭包

开始讲之前先看下下面的这个需求,你会怎么实现呢?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">

          /*需求: 点击某个按钮, 提示"点击的是第n个按钮"*/
    </script>
</head>
<body>
    <button>测试1</button>
    <button>测试2</button>
    <button>测试3</button>
</body>
</html>

普通的实现方式

var btns = document.getElementsByTagName('button');
// 遍历&监听
for(var i = 0, length = btns.length; i < length; i++) {
    var btn = btns[i];
    // 将btn所对应的下标保存在btn上
    btn.index = i;
    btn.onclick = function () {
        alert('第' + (this.index + 1) + '个');
    }
}

利用闭包的实现

var btns = document.getElementsByTagName('button');
// 遍历&监听
for(var i = 0, length = btns.length; i < length; i++) {
    (function(i){
        var btn = btns[i]; // 局部的i
        btn.onclick = function () {
            alert('第' + (i + 1) + '个');
        } 
    })(i); // 全部的i
}

三、什么是Event Loop

首先Event Loop(事件循环)是指计算机系统的一种运行机制。

它是浏览器或者Node用来解决JavaScript单线程运行时不会阻塞的一种机制,我们经常都会使用异步,而这种机制就是异步的原理。

先来说一下浏览器的Event Loop JavaScript代码的执行过程中,除了依靠函数调用栈来确定函数的执行顺序外,还会依靠task queue(任务队列)(先进先出)来让另外一些代码的执行

Javascript单线程任务被分为同步任务和异步任务,同步任务会在调用栈中按照顺序等待主线程依次执行(代码从上到下的执行),异步任务会在异步任务有了结果后,将对应的回调函数放入任务队列中等待主线程空闲的时候(调用栈被清空),被读取到栈内等待主线程的执行。

一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。 一段代码示例

console.log("start");
setTimeout(() => {
  console.log("setTimeout");
  new Promise(resolve => {
    console.log("promise inner1");
    resolve();
  }).then(() => {
    console.log("promise then1");
  });
}, 0);
new Promise(resolve => {
  console.log("promise inner2");
  resolve();
}).then(() => {
  console.log("promise then2");
});
// start->promise inner2->promise then2->setTimeout->promise inner1->promise then1

然后再来一个稍微复杂点的例子

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  return Promise.resolve().then(_ => {
    console.log("async2 promise");
  });
}
console.log("start");
setTimeout(function() {
  console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
  console.log("promise1");
  resolve();
}).then(function() {
  console.log("promise2");
});
// start->async1 start->promise1 ->async2 promise->promise2->async1 end->setTimeout

四、BFC--块级格式化上下文

BFC(Block formatting context)

形成独立的渲染区域 内部元素的渲染不会影响外界

  • 形成BFC常见的条件
  • 浮动元素 float 不是none
  • 绝对定位元素 position 是absolute 或fixed
  • 块级元素 overflow不是visible

拓展:CSS中常见的定位方案

  • 普通流定位
  • 1.元素按照其在HTML中的先后位置至上而下进行布局
  • 2.行内元素水平排列,直至当行被占满然后换行,块级元素则会被渲染为完整的一整行
  • 3.所有元素默认都是普通流定位
  • 浮动
  • 元素首先按照普通流的位置出现,然后根据浮动的方向尽可能向左或向右偏移
  • 绝对定位
  • 元素整体会脱离普通流,因此绝对定位的元素不会对兄弟元素造成影响

五、Flex容器的对齐

.box {display: flex:align-items: center:justify-content: center;}
.box div {width: 100px;height: 100px;}

<div class="box"> <div></div></div>

六、检测数据类型的方法:

1、typeof 检测一些基本的数据类型

语法:typeof 后面加不加括号都是可以用的
注意:正则、{}、[]、null输出结果为object

console.log(typeof /\d/);//object
console.log(typeof {});//object
console.log(typeof []);//object
console.log(typeof (null));//object
console.log(typeof 123);//number
console.log(typeof true);//boolean
console.log(typeof function () {});//function
console.log(typeof (undefined));//undefined

2、A  instanceof   B检测当前实例是否隶属于某各类

双目运算符 a instanceof b ,判断a的构造器是否为b,返回值为布尔值

function b(){}
let a = new b;
console.log(a instanceof b);//true
console.log(b instanceof Object);//true
let arr = [1,2,3,4];
console.log(arr instanceof Array);//true

3、constructor构造函数

  • 语法:实例.constructor
  • 对象的原型链下(构造函数的原型下)有一个属性,叫constructor
  • 缺点:**constructor并不可靠,容易被修改(只有参考价值)。即使被修改了,也不会影响代码的正常运行。正常开发的时候,constructor不小心被修改了,为了方便维护,和开发,可以手动更正constructor的指向。
function a() {}
let a = new b;
console.log(a.constructor.name);//a
console.log(b.constructor);//Function(){}
console.log(Function.constructor);//Function(){}

4、hasOwnporperty 检测当前属性是否为对象的私有属性

语法: obj.hasOwnporperty(“属性名(K值)”)

例子:
let obj = {
name:"lxw"
};
console.log(obj.hasOwnProperty('name'));//true
console.log(obj.hasOwnProperty('lxw'));//false
      

5、is Array 判断是否为数组

console.log(Array.isArray([]));//true
console.log(Array.isArray(new Array()));//true

6、valueOf

可以看到数据最本质内容(原始值)

例子:
let a = "12345";
let b= new String('12345');
console.log(typeof a);//string
console.log(typeof b);//object
console.log(a == b);//true  (typeof检测出来是对象居然和一个数组相等,显然b并不是一个真的对象。)
//此时看看 valueOf()的表现
console.log(b.valueOf());//12345  拿到b的原始值是 字符串的12345
console.log(typeof b.valueOf())//string  这才是 b的真正的数据类型

1.Math是个对象,不是类。类才有prototype(原型)。也就是说类都有原型:prototype,对象都有原型链:proto,函数既是类,也是对象,也是函数。 2.如何把对象转换成字符串?  对象下的toString方法是检测数据类型的,而不是用来转化成字符串的,这时可以用JSON的方法来转化

例子:
let obj={name:'朱军林'}
obj.toString()//=>"[object Object]"
JSON.stringify({name:'朱军林'})//"{"name":"朱军林"}"

7、Object.portotype.toString (最好的)

语法:Object.prototype.toString.call([value]) 获取Object.portotype上的toString方法,让方法的this变为需要检测的数据类型值,并且让这个方法执行

在Number、String、Boolean、Array、Function、RegExp…这些类的原型上都有一个toString方法:这个方法就是把本身的值转化为字符串

例子:
   (123).toString()//'123'
   (true).toString()//'true'
   [12,23].toString()//'12.23'

...

在Object这个类的原型上也有一个方法toString,但是这个方法并不是把值转换成字符串,而是返回当前值得所属类详细信息,固定结构:’[object 所属的类]'

调取的正是Object.prototype.toString方法obj.toString()  首先执行Object.prototype.tostring方法,这个方法中的this就是我们操作的数据值obj

总结:Object.prototype.toString执行的时候返回当前方法中的this的所属类信息,也就是,我想知道谁的所属类信息,我们就把这个toString方法执行,并且让this变为我们检测的这个数据值,那么方法返回的结果就是当前检测这个值得所属类信息

Object.prototype.toString.call(12)//[boject Number]

Object.prototype.toString.call(true)//[boject Boolean]
//"[object Number]"
//"[object String]"
//"[object Object]"
//"[object Function]"
//"[object Undefined]"
//"[object Null]"
//"[object RegExp]"
//"[object Boolean]"