Bootstrap

前端面试基础html/js/css

一、css

1.说一下css盒子模型

CSS盒子模型(Box Model)是CSS中用于描述元素尺寸和布局的一个重要概念。它定义了元素的内容、内边距、边框、外边距和高度的计算方式。盒子模型对于网页布局和响应式设计至关重要。

在CSS中,每个元素都可以被视为一个盒子,这个盒子由内容(content)、内边距(padding)、边框(border)、外边距(margin)和高度(height)组成。

盒子模型的计算方式如下:

内容(content):元素的内容区域,也就是元素中实际的内容。
内边距(padding):元素内容与边框之间的距离。
边框(border):元素边框的宽度。
外边距(margin):元素边框外侧的距离

高度(height):元素的高度。

在CSS中,可以通过设置元素的宽度和高度来控制元素的大小。但是,如果元素没有设置高度,那么它的实际高度将根据其内容自动调整

盒子模型在响应式设计中非常重要,因为它可以帮助我们实现响应式布局。通过调整盒子模型的尺寸,我们可以轻松地实现响应式布局,使网页在不同设备上都能保持良好的显示效果。

盒子组成:内容content、内边距padding、边框border、外边距margin

盒模型类型:
在CSS中,盒模型可以分为两种类型:块级元素盒模型内联元素盒模型。

块级元素盒模型:
块级元素盒模型包括:content(内容)、padding(内边距)、border(边框)和margin(外边距)

块级元素在网页中占据一行,其高度、宽度、内边距和外边距都可以设置。

内联元素盒模型:
内联元素盒模型包括:content(内容)、padding(内边距)和margin(外边距)。

内联元素在网页中不会占据一行,其高度和宽度不能直接设置,但可以通过设置行高(line-height)和letter-spacing(字符间距)来间接控制。

内联元素盒模型主要用于文本元素,如文字、链接等。

2.css选择器的优先级

CSS(Cascading Style Sheets)是一种层叠样式表,主要用于描述HTML元素在屏幕上的显示样式。CSS具有以下特性:

层叠性:CSS具有层叠性,即可以多次定义同一个元素的样式,且后定义的样式会覆盖先定义的样式。

继承性:CSS具有继承性,即子元素会继承父元素的样式。但是,如果子元素有与父元素相同的样式定义,则子元素的样式会覆盖父元素的样式。

选择器优先级:CSS中,选择器的优先级分为四种,分别是:内联样式、内部样式表 、外部样式表 、浏览器默认样式。

样式表的层叠顺序:当一个HTML元素有多个样式定义时,CSS会按照以下顺序进行层叠:内联样式 > 内部样式表 > 外部样式表 > 浏览器默认样式。

样式表的优先级:当一个HTML元素有多个相同的样式定义时,CSS会按照以下优先级进行层叠:important > 内联样式 > 内部样式表 > 外部样式表 > 浏览器默认样式。

总结:

CSS具有层叠性、继承性和选择器优先级等特性,这些特性可以让我们更好地控制HTML元素的显示样式。
选择器的优先级

!important、行内样式、、id选择器、类/伪类/属性、标签、全局选择器。

3.隐藏元素的方法

display: none;可以完全隐藏元素,不占据空间
visibility: hidden;可以隐藏元素但占据空间,
opacity: 0;可以隐藏元素但占据空间,
position: absolute;或position: fixed;可以隐藏元素并将其移出正常文档流。

4.px和rem的区别

px和rem都是CSS中常用的长度单位,但它们之间存在一些区别。

  • 定义范围不同:px是绝对长度单位,它的值是固定的,不会因为浏览器或设备的尺寸而改变。rem是相对长度单位,它的值是基于元素父元素的font-size属性值。
  • 计算方式不同:px可以直接用来设置元素的大小,而rem需要先设置父元素的font-size属性值,然后通过rem进行计算。
  • 兼容性不同:px在大多数浏览器中都可以正常显示,而rem在浏览器中的兼容性较差,特别是在移动设备上。
  • 响应式设计:在响应式设计中,rem可以实现不同设备上的自适应效果,而px则无法实现。
  • rem:相对单位长度,相对于html根节点的font-size的值,1rem=10px;font-size:62.5%(16px* 62.5%=10)。
5.重绘重排有什么区别?

重绘和重排是网页渲染过程中两种不同的操作,它们对网页性能的影响不同。

  • 重绘(Repaint):当一个元素的样式发生变化,但布局不变,此时浏览器会重新绘制该元素,这个过程称为重绘。重绘不会影响网页的性能,因为它不需要重新计算元素的布局。
  • 重排(Reflow):当一个元素的布局发生变化,浏览器需要重新计算该元素及其子元素的布局,这个过程称为重排。重排会对网页的性能产生影响,因为它需要重新计算元素的布局,可能会导致页面重新渲染。

重排和重绘的区别在于,重排需要重新计算元素的布局,可能会导致页面重新渲染;而重绘不需要重新计算布局,只是重新绘制元素。

在实际使用中,可以通过减少重排次数来优化网页性能。例如,可以使用display: none;和visibility: hidden;来隐藏元素,而不是直接删除元素;可以使用transform属性来调整元素的位置,而不是直接调整元素的top和left属性。

6.让一个元素水平垂直居中

定位+margin

定位+transform

父元素 {
  position: relative;
}
子元素 {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100px; /* 或者设置为其他宽度 */
  height: 100px; /* 或者设置为其他高度 */
}

flex布局

父元素 {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%; /* 或者设置为其他高度 */
}
子元素 {
  width: 100px; /* 或者设置为其他宽度 */
  height: 100px; /* 或者设置为其他高度 */
}

grid布局

父元素 {
  display: grid;
  justify-items: center;
  align-items: center;
  height: 100%; /* 或者设置为其他高度 */
}
子元素 {
  width: 100px; /* 或者设置为其他宽度 */
  height: 100px; /* 或者设置为其他高度 */
}

table布局

7.css那些属性可以继承,那些不能继承

可以继承的属性:
(1)字体和文本属性:font-family、font-size、font-weight、font-style、text-align、line-height、letter-spacing等。

(2)颜色和背景属性:color、background-color、background-image、background-repeat、background-position、background-size等。

(3)盒模型属性:margin、padding、border、width、height、min-width、max-width、min-height、max-height等。

(4)其他属性:list-style、float、clear、cursor等。

不能继承的属性:
(1)元素类型属性:display、position、float、clear等。

(2)visibility属性:visibility、opacity等。

(3)overflow属性:overflow、overflow-x、overflow-y等。

(4)z-index属性:z-index等。

总结:CSS属性具有继承性,但并非所有的属性都可以继承。可以继承的属性主要包括字体和文本属性、颜色和背景属性、盒模型属性等;不能继承的属性主要包括元素类型属性、visibility属性、overflow属性、z-index属性等。

8.预处理器

预处理语言增加了变量、函数、混入等功能。

二、javascript

1.js的组成

JavaScript是一种脚本语言,通常用于创建交互式的网页。它由三部分组成:ECMAScript、DOM和BOM。

ECMAScript:ECMAScript是一种由ECMA-262标准定义的脚本语言,它是JavaScript的核心部分。ECMAScript定义了JavaScript的基本语法、变量、运算符、函数等基本元素。

DOM:文档对象模型(DOM,Document Object Model)是JavaScript操作网页的接口。它将HTML文档转换为一个由节点和对象组成的树形结构,从而可以通过JavaScript代码来操作网页元素。

BOM:浏览器对象模型(BOM,Browser Object Model)是JavaScript操作浏览器窗口的接口。它提供了与浏览器窗口相关的各种信息,如窗口大小、历史记录、location等,还可以用于实现一些与浏览器相关的功能,如弹出窗口、跳转页面等。

2.js内置对象

JavaScript内置对象是指在JavaScript中预先定义好的对象,可以直接使用。以下是一些常用的内置对象:

  1. Number:创建一个数字对象。
var num = new Number(123);
  1. String:创建一个字符串对象。
var str = new String("hello");
  1. Boolean:创建一个布尔对象。
var bool = new Boolean(true);
  1. Array:创建一个数组对象。
var arr = new Array("apple", "banana", "orange");
  1. Object:创建一个对象对象。
var obj = new Object();
obj.name = "John";
obj.age = 30;
  1. Function:创建一个函数对象。
function sayHello() {
  alert("Hello!");
}
  1. Date:创建一个日期对象。
var date = new Date();
  1. RegExp:创建一个正则表达式对象。
var reg = new RegExp("\\d+", "g");
  1. Error:创建一个错误对象。
var err = new Error("Something went wrong.");
3.操作数组的方法有哪些

JavaScript提供了许多操作数组的方法,以下是一些常用的方法:
push() ,pop() ,sort() ,splice() ,unshift() ,shift() ,reverse() ,map()

  1. push(): 向数组的末尾添加一个或多个元素,并返回新数组的长度。
var arr = [1, 2, 3];
arr.push(4, 5);
console.log(arr); // [1, 2, 3, 4, 5]
  1. pop(): 删除数组的最后一个元素,并返回被删除的元素。
var arr = [1, 2, 3];
var ret = arr.pop();
console.log(ret); // 3
console.log(arr); // [1, 2]
  1. unshift(): 向数组的开头添加一个或多个元素,并返回新数组的长度。
var arr = [1, 2, 3];
arr.unshift(0);
console.log(arr); // [0, 1, 2, 3]
  1. shift(): 删除数组的第一个元素,并返回被删除的元素。
var arr = [1, 2, 3];
var ret = arr.shift();
console.log(ret); // 1
console.log(arr); // [2, 3]
  1. splice(): 删除数组中指定索引的元素,并返回被删除的元素。
var arr = [1, 2, 3, 4, 5];
var ret = arr.splice(1, 2);
console.log(ret); // [2, 3]
console.log(arr); // [1, 4, 5]
  1. slice(): 返回一个新数组,包含从开始到结束(不包括结束)的所有元素。
var arr = [1, 2, 3, 4, 5];
var newArr = arr.slice(1, 4);
console.log(newArr); // [2, 3, 4]
  1. concat(): 连接两个或多个数组,并返回一个新的数组。
var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
var newArr = arr1.concat(arr2);
console.log(newArr); // [1, 2, 3, 4, 5, 6]
  1. reverse(): 反转数组中的元素,并返回新数组。
var arr = [1, 2, 3, 4, 5];
var newArr = arr.reverse();
console.log(newArr); // [5, 4, 3, 2, 1]
  1. sort(): 对数组进行原地排序,并返回排序后的数组。
var arr = [3, 1, 4, 1, 5];
arr.sort(function(a, b) {
  return a - b;
});
console.log(arr); // [1, 1, 3, 4, 5]
  1. map(): 对数组中的每个元素执行一个给定的函数,并返回一个新的数组。
var arr = [1, 2, 3, 4, 5];
var newArr = arr.map(function(item) {
  return item * 2;
});
console.log(newArr); // [2, 4, 6, 8, 10]
  1. filter(): 对数组中的每个元素执行一个给定的函数,并返回一个新的数组。
var arr = [1, 2, 3, 4, 5];
var newArr = arr.filter(function(item) {
  return item % 2 === 0;
});
console.log(newArr); // [2, 4]
4.对数据类型的检测

JavaScript可以通过以下方法检测数据类型:

  1. typeof运算符:用于检测变量的数据类型。
var num = 123;
console.log(typeof num); // "number"
  1. instanceof运算符:用于检测一个对象是否属于某个特定的构造函数。
var arr = [1, 2, 3];
console.log(arr instanceof Array); // true
  1. Object.prototype.toString.call()方法:用于检测变量的数据类型。
var num = 123;
console.log(Object.prototype.toString.call(num)); // "[object Number]"
  1. Array.isArray()方法:用于检测一个对象是否为数组。
var arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true
  1. isNaN()函数:用于检测一个值是否为非数字。
var num = "123";
console.log(isNaN(num)); // false
5.闭包,特点

闭包(Closure)是JavaScript中一种重要的概念,它指的是一个函数可以访问其词法作用域中的变量,即使这个函数在其词法作用域之外被调用。
简言之:

闭包:函数嵌套函数,内部函数被外部函数返回并保存下来

闭包的特点如下:

  1. 函数可以访问其词法作用域中的变量,即使这个函数在其词法作用域之外被调用。

  2. 闭包可以使得局部变量在函数外部被访问和修改。

  3. 闭包可以实现模块化和封装,防止全局作用域被污染。

缺点:会消耗内存,导致页面性能下降,在ie浏览器中会导致内存泄漏

使用场景:防抖、节流、避免数据全局污染

下面是一个简单的闭包示例:

function createCounter() {
  let count = 0;
  return function() {
    count++;
    console.log(count);
  };
}

const counter = createCounter();
counter(); // 1
counter(); // 2
counter(); // 3

在这个示例中,createCounter函数返回了一个新的函数,这个新函数可以访问createCounter函数的词法作用域中的count变量。即使createCounter函数在其词法作用域之外被调用,count变量仍然可以被访问和修改。

总结:闭包是JavaScript中一种重要的概念,它可以使得函数访问其词法作用域中的变量,即使这个函数在其词法作用域之外被调用。闭包可以实现模块化和封装,防止全局作用域被污染。

6.前端内存泄漏

js分配内存地址,但长时间没释放导致崩溃的情况。

因素:不声明就赋值、未清除的定时器、闭包、引用元素没有清楚。
前端内存泄漏是指在Web应用程序中,由于程序的错误导致浏览器内存被占用,导致浏览器性能下降,甚至崩溃。

前端内存泄漏的原因有很多,以下是一些常见的原因:

  1. 意外保存了引用:在函数中保存了对象的引用,但该对象不再被使用,导致内存泄漏。
function saveObject(obj) {
  // ...
}

var obj = {
  name: "John"
};
saveObject(obj);
  1. 意外创建了循环引用:两个或多个对象相互引用,导致内存泄漏。
var obj1 = {
  name: "John"
};
var obj2 = {
  name: "Jane",
  friend: obj1
};
obj1.friend = obj2;
  1. 意外使用了过时的API:使用了过时的API,导致内存泄漏。
var element = document.getElementById("element");
element.style.color = "red";
  1. 意外使用了全局变量:全局变量在页面关闭时不会被释放,导致内存泄漏。
var globalVar = "some value";
  1. 没有正确释放对象:在不再需要对象时,没有正确释放对象,导致内存泄漏。
var obj = {
  name: "John"
};
// ...
obj = null;

要检测内存泄漏,可以使用浏览器的开发者工具,如Chrome DevTools的Memory标签。通过Memory标签,可以查看当前页面的内存使用情况,包括对象数量、大小等,还可以进行内存泄漏检测。

7.事件委托

原理:事件冒泡机制,子元素事件绑定到父元素身上

阻止事件冒泡 event.stopPropagation()
事件委托是指将事件监听器绑定到父元素上,通过事件冒泡来触发子元素的事件。事件委托可以减少事件监听器的数量,提高性能。

事件委托的原理是事件冒泡,当子元素上的事件触发时,事件会向父元素传递,如果父元素上也有相同类型的事件监听器,那么父元素上的事件监听器也会被触发。

下面是一个简单的例子,使用事件委托实现点击子元素时,父元素上的函数也被触发:

<!DOCTYPE html>
<html>
<head>
<style>
  .parent {
    background-color: lightblue;
    padding: 20px;
  }
  .child {
    background-color: lightcoral;
    padding: 20px;
    margin: 10px;
  }
</style>
</head>
<body>

<div class="parent" id="parent">
  <div class="child" id="child1">Child 1</div>
  <div class="child" id="child2">Child 2</div>
  <div class="child" id="child3">Child 3</div>
</div>

<script>
  var parent = document.getElementById("parent");
  parent.addEventListener("click", function(event) {
    if (event.target.className === "child") {
      console.log("Clicked on child element:", event.target.id);
    }
  });
</script>

</body>
</html>

在这个例子中,我们将事件监听器绑定到父元素parent上,当点击子元素时,父元素上的事件监听器也会被触发,从而实现事件委托。

总结:事件委托是指将事件监听器绑定到父元素上,通过事件冒泡来触发子元素的事件。事件委托可以减少事件监听器的数量,提高性能。

8.基本数据类型和引用数据类型的区别

JavaScript中有两种数据类型:基本数据类型和引用数据类型。

  1. 基本数据类型:基本数据类型包括Number、String、Boolean、Undefined和Null。这些数据类型在内存中占用固定大小的空间,可以直接操作。它们保存在栈内存当中,保存的是具体的值。

例如:

var num = 123;
var str = "hello";
var bool = true;
var undef = undefined;
var nullVal = null;
  1. 引用数据类型:引用数据类型包括Object、Array、Function等。这些数据类型在内存中占用动态分配的空间,不能直接操作。要操作引用数据类型,需要操作其引用(即内存地址)。它们保存在堆内存中,保存的是地址。

例如:

var obj = { name: "John" };
var arr = [1, 2, 3];
var func = function() { console.log("hello"); };

引用数据类型的特点:

  1. 引用数据类型在内存中占用动态分配的空间,不能直接操作。

  2. 引用数据类型的值是内存地址,可以通过引用(内存地址)来访问和修改其值。

  3. 当引用数据类型的值发生变化时,内存地址也会发生变化,因此引用数据类型的值是可变的。

  4. 引用数据类型可以包含其他引用数据类型,形成嵌套结构。

总结:基本数据类型在内存中占用固定大小的空间,可以直接操作;引用数据类型在内存中占用动态分配的空间,不能直接操作,需要操作其引用(内存地址)。引用数据类型的值是可变的,可以包含其他引用数据类型,形成嵌套结构。

9.原型链

原型链是JavaScript中实现对象间继承的一种方式。在JavaScript中,每个对象都有一个原型(proto),这个原型可以指向另一个对象,从而形成原型链。

当访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript会自动查找该对象的原型,如果原型上有这个属性或方法,则可以直接使用。如果原型上也没有,则继续查找原型的原型,直到找到为止。

下面是一个简单的例子,说明原型链的查找过程:

function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function() {
  console.log(this.name);
};

function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}

Student.prototype = new Person();

var student = new Student("Tom", 1);
student.sayName(); // "Tom"

在这个例子中,Student对象继承了Person对象,通过原型链的查找过程,Student对象可以访问Person对象上的sayName方法。

原型链的特点:

  1. 每个对象都有一个原型(proto),可以指向另一个对象。

  2. 当访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript会自动查找该对象的原型,如果原型上有这个属性或方法,则可以直接使用。

  3. 如果原型上也没有,则继续查找原型的原型,直到找到为止。

  4. 原型链的查找过程是沿着原型(proto)属性进行的,直到找到为止。

总结:原型链是JavaScript中实现对象间继承的一种方式,每个对象都有一个原型(proto),可以指向另一个对象。通过原型链的查找过程,可以实现对象间的继承和属性、方法的继承。原型是一个对象,为构造函数的实例共享方法和属性,所有实例引用的都是同一个原型对象。一个实例对象在调用属性和方法的时候,会依次从实例本身、构造函数原型、原型的原型上去查找。

10.new操作符具体做了什么?

new操作符是JavaScript中用于创建对象的关键字。当使用new操作符创建一个新对象时,会执行以下步骤:

  1. 创建一个新的空对象。

  2. 将新对象的原型(proto)属性设置为构造函数的prototype属性。

  3. 将构造函数的this指向新对象,执行构造函数的代码,为对象添加属性和方法。

  4. 返回新对象。

下面是一个简单的例子,说明new操作符的执行过程:

function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function() {
  console.log(this.name);
};

var person = new Person("John");
person.sayName(); // "John"

在这个例子中,使用new操作符创建了一个新的Person对象,并将Person构造函数的this指向新对象,然后执行构造函数的代码,为对象添加了name属性和sayName方法。最后返回新对象。

总结:new操作符用于创建对象,执行过程包括创建一个新的空对象、设置原型(proto)属性、执行构造函数的代码、返回新对象。通过new操作符,可以实现对象间的继承和属性、方法的继承。

11.js是如何实现继承的

JavaScript中实现继承的方式主要有两种:原型链继承和构造函数继承。

  1. 原型链继承:通过将一个对象的 prototype 属性设置为另一个对象的实例,从而实现继承。
function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function() {
  console.log(this.name);
};

function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}

Student.prototype = new Person();

var student = new Student("Tom", 1);
student.sayName(); // "Tom"

在这个例子中,Student对象继承了Person对象,通过原型链的查找过程,Student对象可以访问Person对象上的sayName方法。

  1. 构造函数继承:通过在子构造函数中调用父构造函数,并将返回值作为子构造函数的实例,从而实现继承。
function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function() {
  console.log(this.name);
};

function Student(name, grade) {
  Person.call(this, name);
  this.grade = grade;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

var student = new Student("Tom", 1);
student.sayName(); // "Tom"

在这个例子中,Student对象继承了Person对象,通过构造函数的调用过程,Student对象可以访问Person对象上的sayName方法。

总结:JavaScript中实现继承的方式主要有两种:原型链继承和构造函数继承。原型链继承通过将一个对象的 prototype 属性设置为另一个对象的实例实现继承;构造函数继承通过在子构造函数中调用父构造函数,并将返回值作为子构造函数的实例实现继承。

  1. 原型链继承

  2. 借用构造函数继承

  3. 组合式继承

  4. es6的class类继承

12.js中this指向问题

在JavaScript中,this关键字指向当前对象,即当前函数所在的对象。this的指向取决于函数的调用方式。

  1. 普通函数调用:this指向全局对象(window)。
function sayHello() {
  console.log("Hello, " + this.name);
}

var name = "John";
sayHello(); // "Hello, John"
  1. 方法调用:this指向调用该方法的对象。
function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log("Hello, " + this.name);
};

var person = new Person("John");
person.sayHello(); // "Hello, John"
  1. 事件处理函数:this指向触发事件的元素。
<button id="btn">Click me</button>
document.getElementById("btn").addEventListener("click", function() {
  console.log("Clicked: " + this.id);
});
  1. 箭头函数:this指向箭头函数所在的对象。
function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = () => {
  console.log("Hello, " + this.name);
};

var person = new Person("John");
person.sayHello(); // "Hello, John"

总结:在JavaScript中,this关键字指向当前对象,即当前函数所在的对象。this的指向取决于函数的调用方式。普通函数调用时,this指向全局对象(window);方法调用时,this指向调用该方法的对象;事件处理函数时,this指向触发事件的元素;箭头函数时,this指向箭头函数所在的对象。

全局对象中的this指向的是window

普通函数中this指向全局window

this永远指向最后调用它的对象(非箭头函数)

new关键词改变了this的指向

apply、call、bind可以改变this指向(非箭头函数)

箭头函数中的this指向是其父类的this

匿名函数中的this指向window

13.script中async和defer有什么区别

在HTML中,<script>标签可以包含asyncdefer属性,它们用于控制脚本的加载和执行顺序。

  1. async属性:async属性用于异步加载脚本,当脚本加载时不会阻塞页面其他内容的加载,但加载完成后会立即执行。
<script async src="script.js"></script>
  1. defer属性:defer属性用于延迟加载脚本,当脚本加载时不会阻塞页面其他内容的加载,但会在页面加载完成后执行。
<script defer src="script.js"></script>

总结:async属性用于异步加载脚本,当脚本加载时不会阻塞页面其他内容的加载,但加载完成后会立即执行;defer属性用于延迟加载脚本,当脚本加载时不会阻塞页面其他内容的加载,但会在页面加载。

  1. 没有async和defer,浏览器会立刻加载并执行指定脚本。

  2. 有async,异步的,加载和渲染元素的过程将和script的加载和执行并行进行。

  3. 有defer,加载和渲染元素的过程将和script的加载并行进行,所有元素解析之后才执行。

14.setTimeout最小执行时间

setTimeout:4ms

setInterval:10ms

setTimeout 是 JavaScript 中的一个函数,用于在指定的毫秒数后执行一段代码。但是,如果在指定的毫秒数内,函数已经执行完毕,那么 setTimeout 会将这段代码延迟到下一个宏任务(例如 setTimeout 本身)的开始执行。

因此,setTimeout 的最小执行时间实际上是 4 毫秒。这是因为,浏览器为了保证页面流畅滚动,会强制将小于 4 毫秒的延迟任务放到下一个宏任务中执行。

需要注意的是,这个最小执行时间是在现代浏览器中观察到的,可能随着浏览器和操作系统的更新而改变。

15.ES5和ES6的区别ECMAScript

ECMAScript(ES)是一种由ECMA-262标准定义的脚本语言,JavaScript是ES的一种实现。ES5和ES6是两个不同的版本,它们之间存在一些区别。

  1. 变量声明:ES5中,变量需要使用var关键字声明,而ES6中,可以使用letconstvar关键字声明变量。

ES5:

var a = 1;
var b = 2;

ES6:

let a = 1;
const b = 2;
  1. 函数声明:ES5中,函数需要使用function关键字声明,而ES6中,可以使用functionletconstclass关键字声明函数。

ES5:

function a() {
  console.log(1);
}

ES6:

let a = function() {
  console.log(1);
};
  1. 对象字面量:ES5中,对象字面量需要使用varfunction关键字声明,而ES6中,可以使用letconstclass关键字声明对象字面量。

ES5:

var obj = {
  name: "John"
};

ES6:

let obj = {
  name: "John"
};
  1. 箭头函数:ES6中引入了箭头函数,可以更简洁地定义函数。

ES6:

const add = (a, b) => a + b;
  1. 模板字符串:ES6中引入了模板字符串,可以更方便地拼接字符串。

ES6:

const name = "John";
const greeting = `Hello, ${name}!`;

总结:ES5和ES6之间存在一些区别,主要体现在变量声明、函数声明、对象字面量、箭头函数和模板字符串等方面。ES6中引入了一些新的特性,使得JavaScript更加简洁和强大。

16.ES6的新特性

ECMAScript 6(简称 ES6)是 JavaScript 的一个重要版本,它引入了许多新的特性,使得 JavaScript 更加简洁和强大。以下是一些 ES6 的新特性:

  1. 变量声明:ES6 中可以使用 letconstclass 关键字声明变量,而不再需要使用 var 关键字。
let a = 1;
const b = 2;
class C {}
  1. 函数声明:ES6 中可以使用 functionletconstclass 关键字声明函数。
function foo() {
  console.log(1);
}
let bar = function() {
  console.log(2);
};
  1. 箭头函数:ES6 中引入了箭头函数,可以更简洁地定义函数。
const add = (a, b) => a + b;
  1. 模板字符串:ES6 中引入了模板字符串,可以更方便地拼接字符串。
const name = "John";
const greeting = `Hello, ${name}!`;
  1. 解构赋值:ES6 中引入了解构赋值,可以更方便地从对象中获取值。
const obj = {
  name: "John",
  age: 30
};
const { name, age } = obj;
  1. 剩余参数:ES6 中引入了剩余参数(…rest),可以更方便地处理函数参数。
function sum(...numbers) {
  return numbers.reduce((acc, num) => acc + num, 0);
}
  1. 扩展运算符:ES6 中引入了扩展运算符(…),可以更方便地合并数组。
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
  1. Promise:ES6 中引入了 Promise,可以更方便地处理异步操作。
async function fetchData() {
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();
  console.log(data);
}
  1. async/await:ES6 中引入了 async/await,可以更方便地编写异步代码。
async function fetchData() {
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();
  console.log(data);
}

总结:ES6 引入了许多新的特性,使得 JavaScript 更加简洁和强大。这些特性包括变量声明、函数声明、箭头函数、模板字符串、解构赋值、剩余参数、扩展运算符、Promise、async/await 等。

17.call、apply、bind的区别

callapplybind 是 JavaScript 中的三个方法,它们都可以改变函数的 this 指向。但是它们之间存在一些区别。

  1. 参数:callapply 都需要传入一个函数和一个 this 值,而 bind 需要传入一个函数和一个 this 值,以及一个可选的参数数组。

call 示例:

function add(a, b) {
  return a + b;
}
add.call(1, 2); // 3

apply 示例:

function add(a, b) {
  return a + b;
}
add.apply(1, [2]); // 3

bind 示例:

function add(a, b) {
  return a + b;
}
const add5 = add.bind(1, 5);
add5(2); // 7
  1. 返回值:call 和 apply 都会直接执行函数,并返回函数的执行结果。而 bind 会返回一个新的函数,这个新函数的 this 值被绑定到指定的 this 值,并传入指定的参数数组。

总结:call、apply 和 bind 都可以改变函数的 this 指向,但它们之间存在一些区别。call 和 apply 直接执行函数并返回函数的执行结果,而 bind 返回一个新的函数,这个新函数的 this 值被绑定到指定的 this 值,并传入指定的参数数组。

都是改变this指向和函数的调用

call、apply功能类似,只是传参方法不同

call传参是一个参数列表

apply传参是一个数组

bind传参后不会立刻执行,会返回一个改变this指向的函数,不能去做构造函数,这个函数可以传参bind()()

call方法性能比较好用的多

18.箭头函数和普通函数区别?

箭头函数是 ECMAScript 6(ES6)中引入的一种新的函数语法,它使得函数更加简洁和易读。箭头函数与普通函数存在一些区别。

  1. 语法:箭头函数的语法更加简洁,它使用一个箭头(=>)来表示函数的参数和返回值。

普通函数:

function add(a, b) {
  return a + b;
}

箭头函数:

const add = (a, b) => a + b;
  1. 简写:箭头函数可以省略 return 关键字,如果函数体只有一行代码,并且是返回值。

普通函数:

function add(a, b) {
  return a + b;
}

箭头函数:

const add = (a, b) => a + b;
  1. 绑定 this:箭头函数会自动绑定 this 值,即箭头函数所在的对象。

普通函数:

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, ${this.name}!`);
};

const person = new Person("John");
person.sayHello(); // "Hello, John!"

箭头函数:

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = () => {
  console.log(`Hello, ${this.name}!`);
};

const person = new Person("John");
person.sayHello(); // "Hello, John!"

总结:箭头函数与普通函数存在一些区别,包括语法、简写和绑定 this 值。箭头函数使得函数更加简洁和易读,并且具有自动绑定 this 值的功能。

19.let const var区别

letconstvar 是 JavaScript 中的三个关键字,用于声明变量。它们之间存在一些区别。

  1. 作用域:letconst 声明的变量具有块级作用域,即变量仅在声明所在的代码块(如循环、函数等)内有效。而 var 声明的变量具有函数级作用域,即变量在整个函数内有效。

letconst

function example() {
  let a = 1;
  const b = 2;
  if (true) {
    let a = 3;
    const b = 4;
    console.log(a, b); // 3 4
  }
  console.log(a, b); // 3 4
}
example();

var

function example() {
  var a = 1;
  var b = 2;
  if (true) {
    var a = 3;
    var b = 4;
    console.log(a, b); // 3 4
  }
  console.log(a, b); // 3 4
}
example();
  1. 变量提升:letconst 声明的变量会发生变量提升,即在声明之前就可以访问到变量。而 var 声明的变量不会发生变量提升。

letconst

console.log(a); // undefined
let a = 1;
console.log(a); // 1

var

console.log(a); // undefined
var a = 1;
console.log(a); // 1
  1. 重新赋值:letconst 声明的变量可以重新赋值,而 var 声明的变量可以重新赋值,也可以删除。

letconst

let a = 1;
a = 2;
console.log(a); // 2

var

var a = 1;
a = 2;
console.log(a); // 2

总结:letconstvar 都是用于声明变量的关键字,但它们之间存在一些区别。letconst 具有块级作用域,会发生变量提升,不能重新赋值;而 var 具有函数级作用域,不会发生变量提升,可以重新赋值也可以删除。

let const:不存在变量提升、必须定义才能使用、存在暂时性死区的问题、存在块级作用域的内容、不能在同一个作用域内重复声明

var:存在变量提升

20.如何实现深拷贝

实现深拷贝的方法有很多,下面介绍两种常用的方法:

  1. 使用第三方库:可以使用一些第三方库,如 lodash 的 _.cloneDeep 方法,实现深拷贝。
const _ = require('lodash');
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = _.cloneDeep(obj1);
console.log(obj1); // { a: 1, b: { c: 2 } }
console.log(obj2); // { a: 1, b: { c: 2 } }
console.log(obj1.b === obj2.b); // false
  1. 使用递归实现深拷贝:
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  let copy = Array.isArray(obj) ? [] : {};

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = deepClone(obj[key]);
    }
  }

  return copy;
}

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = deepClone(obj1);
console.log(obj1); // { a: 1, b: { c: 2 } }
console.log(obj2); // { a: 1, b: { c: 2 } }
console.log(obj1.b === obj2.b); // false

注意:这两种方法都不是万能的,有些特殊对象(如函数、循环引用的对象等)可能无法完全深拷贝。在实际使用中,需要根据具体需求选择合适的方法。

完全拷贝一个新的对象,在堆内存中开辟新的空间

主要针对引用类型数据

扩展运算符 {…obj}只能实现第一层

json.parse(json.stringify()),不会拷贝内部函数

利用递归函数实现

21.ajax是什么

AJAX(Asynchronous JavaScript and XML)是一种在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容的技术。通过 AJAX,前端可以异步地向服务器发送请求,获取数据,然后更新页面,从而提高用户体验。

AJAX 的核心是 XMLHttpRequest 对象,它可以在后台与服务器交换数据,而不会影响用户正在查看的页面。随着 HTML5 的普及,现在可以使用 Fetch API 等更现代的方法来实现 AJAX。

下面是一个简单的 AJAX 示例:

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => {
    console.log(data);
  });

这个示例使用 Fetch API 向服务器发送请求,获取 JSON 数据,然后将数据输出到控制台。

总结:AJAX 是一种与服务器交换数据的技术,可以异步地更新网页内容,提高用户体验。AJAX 的核心是 XMLHttpRequest 对象,现在可以使用 Fetch API 等更现代的方法来实现 AJAX。

  1. 创建交互式网页应用的网页开发技术

  2. 在不重新加载整个网页的前提下,与服务器交换数据并更新部分内容

  3. 通过xmlhttprequest对象向服务器发送异步请求,从服务器拿到数据之后,通过js操作dom的方式操作页面

  4. 创建xmlhttprequst对象

  5. 通过xmh对象里的open()方法和服务器建立连接

  6. 构建请求所需的数据,并通过xmh的send()发送给服务器

  7. 通过xmh的onreadystate change事件监听服务器和你的通信状态

  8. 接受并处理服务器响应的数据结果

  9. 把处理的数据更新到html页面上

22.get和post的区别

GETPOST 是 HTTP 请求中的两种方法,它们的主要区别如下:

  1. 请求方式:GET 请求会将请求的数据附在 URL 中,以 ? 符号分割,多个参数之间用 & 符号分割;POST 请求会将请求的数据放在请求体中,以 application/x-www-form-urlencoded 格式编码。

GET 请求示例:

GET /search?q=javascript&type=software

POST 请求示例:

POST /search
Content-Type: application/x-www-form-urlencoded

q=javascript&type=software
  1. 数据大小:GET 请求的数据大小有限制,通常不能超过 2KB;POST 请求的数据大小没有限制。

  2. 安全性:GET 请求会将请求的数据暴露在 URL 中,可能会导致安全问题;POST 请求会将请求的数据放在请求体中,相对安全。

  3. 幂等性:GET 请求具有幂等性,即多次请求同一个 URL,得到的结果是一样的;POST 请求不具有幂等性,多次请求同一个 URL,可能会得到不同的结果。

总结:GETPOST 是 HTTP 请求中的两种方法,主要区别在于请求方式、数据大小、安全性以及幂等性。在实际使用中,需要根据具体需求选择合适的请求方法。

get参数会放在url上,安全性比较差,post是放在body中的

get刷新服务器或退回是没有影响的,post则会重新提交

get请求会被缓存,post则不会

get请求会保存在浏览器的历史记录中,post不会

get只能进行url编码,post则支持很多种

23.promise的内部原理和优缺点

Promise 是 JavaScript 中用于处理异步操作的一种对象,它表示一个异步操作的最终结果。Promise 的内部原理主要是通过状态机来实现的,它有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。Promise 的优点是统一了异步操作的处理方式,提供了链式调用和错误处理等功能,提高了代码的可读性和可维护性。

Promise 的内部原理主要是通过状态机来实现的,它有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。Promise 的构造函数接收一个函数参数,这个函数有两个参数,分别是 resolve 和 reject,它们分别用于将 Promise 的状态从 pending 变为 fulfilled 和从 pending 变为 rejected。

Promise 的优点是统一了异步操作的处理方式,提供了链式调用和错误处理等功能,提高了代码的可读性和可维护性。但是,Promise 也有一些缺点,例如:

  1. 无法取消:一旦创建了一个 Promise,就无法取消它,即使它还没有开始执行。

  2. 错误处理:Promise 的错误处理比较复杂,需要使用 catch 方法或者 then 方法的第二个参数来处理错误。

  3. 代码可读性:Promise 的代码可读性不如传统的回调函数,特别是对于初学者来说,可能会导致代码难以理解。

总结:Promise 是 JavaScript 中用于处理异步操作的一种对象,它表示一个异步操作的最终结果。Promise 的内部原理主要是通过状态机来实现的,它有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。Promise 的优点是统一了异步操作的处理方式,提供了链式调用和错误处理等功能,提高了代码的可读性和可维护性。但是,Promise 也有一些缺点,例如无法取消、错误处理复杂和代码可读性差等。

  1. Promise对象封装了一个异步操作并且还可以获取成功或失败的结果。

  2. Promise对象主要解决回调地狱的问题,之前如果异步任务比较多,同时他们之间有相互的依赖关系,就只能使用回调函数处理,这样就容易形成回调地狱,代码的可读性、可维护性差。

  3. 有三种状态:pending初始状态,fulfilled成功状态,rejected失败状态。

  4. 原理:构造一个promise实例,实例需要传递参数,这个参数有两个形参,分别都是函数类型,一个是resolve一个是reject。

  5. promise上还有then方法,用来指定状态改变时的确定操作,resolve是执行第一个函数,reject是执行第二个。

24.promise和async await的区别

Promise 和 async/await 是 JavaScript 中处理异步操作的两种不同方式。Promise 是基于回调函数的,而 async/await 是基于生成器的。

Promise 的主要优点是统一了异步操作的处理方式,提供了链式调用和错误处理等功能,提高了代码的可读性和可维护性。但是,Promise 也有一些缺点,例如无法取消、错误处理复杂和代码可读性差等。

async/await 是基于生成器的,它可以将异步操作转换为同步操作,使得代码更加简洁和易读。但是,async/await 也有一些缺点,例如:

  1. 无法处理多个异步操作:async/await 只能处理一个异步操作,如果要处理多个异步操作,需要使用 Promise.all 等方法。

  2. 无法处理错误:async/await 无法直接处理错误,需要使用 try/catch 等语句来处理错误。

  3. 性能问题:async/await 可能会导致性能问题,因为它会阻塞主线程,直到异步操作完成。

总结:Promise 和 async/await 都是 JavaScript 中处理异步操作的两种方式,它们各有优缺点。在实际使用中,需要根据具体需求选择合适的处理方式。

  1. 都是处理异步请求

  2. promise是es6,async await是es7

  3. async await是基于promise实现的,都是非阻塞性的

优缺点:

  1. promise是返回对象,要用then,catch的方法处理和捕获异常,并且书写方式是链式,容易造成代码重叠,可维护性差

  2. async await是通过try catch进行捕获异常

  3. async await能让代码看起来像同步一样,只要遇到await就会立刻返回结果,然后在执行后面的操作

  4. promise.then()会出现请求还没返回,就执行了后面操作

25.浏览器的存储方式

浏览器的存储方式主要有以下几种:

  1. 本地存储:本地存储将数据存储在本地,即使浏览器关闭,数据也不会丢失。本地存储通常使用 localStorage 对象来实现。
localStorage.setItem('key', 'value');
const value = localStorage.getItem('key');
  1. sessionStorage:sessionStorage 将数据存储在当前会话中,当会话结束(通常是窗口或标签页关闭)时,数据将被清除。sessionStorage 通常用于存储跨页面的数据。
sessionStorage.setItem('key', 'value');
const value = sessionStorage.getItem('key');
  1. 缓存:缓存将数据存储在浏览器中,以便在下次访问时更快地加载页面。缓存通常使用 cache 对象来实现。
caches.open('my-cache').then((cache) => {
  cache.addAll([
    '/',
    '/index.html',
    '/styles.css',
    '/script.js',
  ]);
});
  1. IndexedDB:IndexedDB 是一个轻量级的数据库,可以在浏览器中存储大量数据。IndexedDB 通常用于存储复杂的数据结构,如对象和图形。
const db = openDatabase('my-db', '1.0', 'My Database', 2 * 1024 * 1024);
db.transaction((tx) => {
  tx.executeSql('CREATE TABLE IF NOT EXISTS my-table (id INTEGER PRIMARY KEY, name TEXT)');
  tx.executeSql('INSERT INTO my-table (name) VALUES (?)', ['John']);
});

总结:浏览器的存储方式主要有本地存储、sessionStorage、缓存和 IndexedDB。本地存储和 sessionStorage 用于存储简单的键值对,缓存用于存储网页资源,IndexedDB 用于存储复杂的数据结构。

  1. cookie 标准本地存储方式,兼容性好,存储量小,需要封装

  2. localstorage 以键值对标准的人方式、操作方便、永久存储、兼容性好,保存值的类型被限定,隐私模式下不可读取

  3. sessionstorage 页面关闭就会清理,会话级别的存储方式

  4. indexedDB 键值对方式存储,存储方便

26.token存在哪儿

Token 通常存储在浏览器的本地存储或 sessionStorage 中。Token 是一种身份验证方式,用于确保用户请求的合法性。将 Token 存储在本地存储或 sessionStorage 中,可以在用户多次请求时自动携带 Token,避免每次请求都重新登录。

以下是将 Token 存储在本地存储和 sessionStorage 中的示例:

// 将 Token 存储在本地存储
localStorage.setItem('token', 'your-token');

// 从本地存储获取 Token
const token = localStorage.getItem('token');
// 将 Token 存储在 sessionStorage
sessionStorage.setItem('token', 'your-token');

// 从 sessionStorage 获取 Token
const token = sessionStorage.getItem('token');

注意:Token 存储在本地存储或 sessionStorage 中时,需要确保在用户登录时设置 Token,并在用户注销时清除 Token。

token:验证身份的令牌

如果存在localdtorage里,每次请求接口都要把它当作字段传给后台,容易被xss攻击

存在cookie中,会自动发送,但是不能跨域

27.页面渲染过程

页面渲染过程是指浏览器将 HTML 代码转换为可视化页面的一系列步骤。页面渲染过程主要可以分为以下几个阶段:

  1. 解析 HTML:浏览器将 HTML 代码解析成 DOM 树。DOM 树是一个树形结构,表示页面上所有的 HTML 元素。

  2. 解析 CSS:浏览器将 CSS 代码解析成 CSSOM 树。CSSOM 树是一个树形结构,表示页面上所有的 CSS 样式。

  3. 解析 JavaScript:浏览器将 JavaScript 代码解析成可执行的代码。

  4. 渲染:浏览器将解析好的 DOM 树和 CSSOM 树结合,生成渲染树。渲染树是一个线性结构,表示页面上可见的元素及其顺序。

  5. 布局:浏览器根据渲染树计算每个元素的尺寸和位置,并将它们放置在正确的位置。

  6. 绘制:浏览器将渲染树中的元素绘制到屏幕上,形成可视化页面。

总结:页面渲染过程主要可以分为解析 HTML、解析 CSS、解析 JavaScript、渲染、布局和绘制六个。

dns解析

建立tcp连接

发送http请求

服务器处理请求

渲染页面

浏览器会获取html和css资源,把html解析成dom树

再把css解析成cssom

把dom和cssom合并为渲染树

吧渲染树的每个节点渲染到页面

断开tcp连接

三、HTML/CSS

1.html5、css3有哪些新特性

HTML5:

  1. 语义化:HTML5 提供了更多的语义化元素,如 <header><nav><section><article> 等,使得页面结构更加清晰。

  2. 媒体元素:HTML5 新增了 <video><audio> 元素,可以方便地播放音频和视频。

  3. 表单元素:HTML5 新增了 <input> 元素的多种新类型,如 emailurlnumberrange 等,使得表单验证更加方便。

  4. 拖放功能:HTML5 提供了拖放功能,可以方便地实现页面元素的拖放操作。

  5. 离线存储:HTML5 提供了离线存储技术,如 localStoragesessionStorage,使得页面可以在离线状态下正常访问。

CSS3:

  1. 选择器:CSS3 提供了更多的选择器,如属性选择器、伪类选择器、伪元素选择器等,可以更方便地选择页面元素。

  2. 样式:CSS3 提供了更多的样式特性,如 box-sizingborder-radiustransform 等,可以实现更多的视觉效果。

  3. 动画:CSS3 提供了动画功能,可以使用 @keyframes 规则定义动画,使用 animation 属性应用动画。

  4. 响应式设计:CSS3 提供了响应式设计技术,如 @media 规则、flexboxgrid 布局等,可以实现不同设备上的自适应布局。

  5. 变量:CSS3 提供了变量功能,可以使用 var() 函数定义全局变量,方便在整个项目中使用。

;