前端开发面试题总结

(一)JavaScript 

    1.深拷贝与浅拷贝的区别?
  • 浅拷贝只复制了对象的基本结构,对于嵌套对象,只是复制了其引用,原对象和浅拷贝对象共享嵌套对象。(Object.assign)
  • 深拷贝则是完全复制了对象的所有结构,生成了一个独立的新对象,原对象和深拷贝对象没有任何引用关系,更改原对象不会影响深拷贝对象。(JSON.stringify和JSON.parse,jquey的extend方法)
  • const original = { a: 1, b: { c: 2 } };
    const shallowCopy = Object.assign({}, original); 
    original.a = 10; original.b.c = 20; console.log(shallowCopy.a); // 1 console.log(shallowCopy.b.c); // 20 (因为嵌套对象的引用被复制,所以更改了原始对象也会影响到浅拷贝对象)
  •  
    const original = { a: 1, b: { c: 2 } }; 
    const deepCopy = JSON.parse(JSON.stringify(original));
    original.a = 10; 
    original.b.c = 20; 
    console.log(deepCopy.a); // 1 console.log(deepCopy.b.c); // 2 (完全独立的对象,不受原始对象的更改影响) 
    2.什么是闭包?
  • 当一个函数内部定义的函数(内部函数)引用了外部函数的变量,即使外部函数已经执行完毕,内部函数依然能够访问并使用外部函数的变量,这就形成了闭包。

  • 闭包的特点:
    能访问外部作用域的变量:内部函数可以访问包含函数(外部函数)的变量。
    保持词法作用域:内部函数可以在其定义时“记住”其词法作用域,即使在该作用域执行结束后仍然可以访问。

  • 闭包的应用场景:
    私有变量:利用闭包可以创建私有变量,因为外部函数的变量对外是不可见的,只有内部函数可以访问和修改这些变量。
    模块化:闭包能够创建独立的模块,通过封装变量和函数,限制其它代码对内部数据的访问,提供更安全和可维护的代码结构。
    延迟执行:在事件处理和回调函数中使用闭包,可以保存外部函数的变量状态,实现在未来某个时刻执行特定逻辑。

  • 闭包的弊端:内层函数引用外层函数变量,内层函数占用内存。如果不释放内存,过多时,易引起内存泄露。

    解决办法:无法自动销户,就及时手动回收,使用后将函数的引用赋null。
     

    function outerFunction() {
      let outerVariable = 'I am from the outer function';
    
      function innerFunction() {
        console.log(outerVariable); // 访问外部函数的变量
      }
    
      return innerFunction;
    }
    
    const myClosure = outerFunction(); // 返回内部函数
    myClosure(); // 执行内部函数,依然可以访问外部函数的变量
    3.call/apply/bind
  • call, apply, 和 bind 是 JavaScript 中用于改变函数执行上下文(即 this 的指向)的方法。它们的作用是相似的,但在参数传递和返回值方面有些许差异。 

  • call 方法:

  • 语法function.call(thisArg, arg1, arg2, ...)
  • 作用:调用一个函数,传递一个指定的 this 值和一个参数列表。
  • 参数thisArg 表示在函数执行时指定的 this 值,后续参数是作为函数的参数传递进去的。
  • 传参方式:参数以列表形式传递。
  • 返回值:立即执行该函数,并返回函数执行的结果。
    function sayHello() {
      console.log(`Hello, ${this.name}`);
    }
    
    const person = { name: 'Alice' };
    
    sayHello.call(person); // 输出: Hello, Alice
    

    apply 方法:

  • 语法function.apply(thisArg, [argsArray])
  • 作用:调用一个函数,传递一个指定的 this 值和一个参数数组。
  • 参数thisArg 表示在函数执行时指定的 this 值,argsArray 是一个数组,包含作为函数参数的元素。
  • 传参方式:参数以数组形式传递。
  • 返回值:立即执行该函数,并返回函数执行的结果。
    function sayHello() {
      console.log(`Hello, ${this.name}`);
    }
    
    const person = { name: 'Bob' };
    
    sayHello.apply(person); // 输出: Hello, Bob
    

    bind 方法:

  • 语法function.bind(thisArg, arg1, arg2, ...)
  • 作用:创建一个新的函数,使传入的 this 值和参数列表作为原函数的上下文。
  • 参数thisArg 表示在函数执行时指定的 this 值,后续参数是作为函数的参数传递进去的。
  • 传参方式:参数以列表形式传递,用于预先填充函数的参数。
  • 返回值:返回一个新的函数,而不是立即执行。
    function sayHello() {
      console.log(`Hello, ${this.name}`);
    }
    
    const person = { name: 'Charlie' };
    const helloFunction = sayHello.bind(person);
    
    helloFunction(); // 输出: Hello, Charlie
    

    总结:callapply 立即执行函数,bind 返回一个新的函数,需要显式调用。 
               callapply 之间的区别在于参数的传递方式,一个是列表,一个是数组。
    这三种方法都可以用来改变函数执行时的上下文,选择使用哪个方法取决于具体的需求和参数传递的形式。
               bind 在绑定函数上下文的同时,可以预先填充参数。

        4.浏览器存储的区别?

           在浏览器中,有几种常用的存储方式,包括 Cookies、Web Storage(包括本地存储和会话存储)、IndexedDB 等。它们各自有不同的特点和用途。  

           4.1. Cookies(HTTP Cookie):

          存储容量:通常每个 Cookie 最大存储 4KB 数据。

          存储位置:以文本方式存储在浏览器和服务器之间,每个请求都会携带。

          过期时间:可以设置过期时间,也可在会话结束后自动删除(会话 Cookie)。 

          用途:存储少量数据,例如会话标识、用户偏好设置等。

          4.2. Web Storage(本地存储和会话存储):

          存储容量:每个域名的存储容量一般为 5MB 左右。

          存储位置:在浏览器中,不会随每次 HTTP 请求发送。

  •    分为
    • LocalStorage:数据长期存储,除非主动删除,否则不会自动过期。
    • SessionStorage:数据在页面会话结束时被清除,即当页面关闭后数据会被清空。
  •     用途:用于存储大量的结构化数据,比如本地缓存、用户配置等。

          4.3. IndexedDB:

         存储容量:一般比 Web Storage 更大。

         存储位置:本地数据库,更类似于关系型数据库,支持更复杂的数据结构和查询。

         过期时间:需要手动管理数据的生命周期,没有自动过期的机制。

         用途:用于存储大量结构化数据,并支持查询和索引,适用于需要大量本地数据的应用。

         主要区别:

  • 存储容量:Cookies < Web Storage < IndexedDB。
  • 数据传输:Cookies 随每次 HTTP 请求发送,Web Storage 和 IndexedDB 不会自动发送。
  • 存储位置:Cookies 存储在浏览器和服务器之间,Web Storage 和 IndexedDB 存储在浏览器中。
     5.常用的数组方法有哪些?

      JavaScript 中数组有许多内置方法用于执行不同的操作,例如添加、删除、替换、过滤、映射、排序等。以下是常用的数组方法:

修改原始数组:

  1. push():向数组末尾添加一个或多个元素,并返回新的长度。
  2. pop():从数组末尾移除最后一个元素,并返回移除的元素。
  3. unshift():向数组开头添加一个或多个元素,并返回新的长度。
  4. shift():从数组开头移除第一个元素,并返回移除的元素。
  5. splice():根据索引位置添加/删除/替换元素。

不修改原始数组:

  1. concat():将数组连接起来生成一个新数组。
  2. slice():返回数组的选定部分,不会修改原数组。
  3. join():将数组元素连接为一个字符串。

遍历数组:

  1. forEach():遍历数组的每个元素执行提供的函数。
  2. map():对数组中的每个元素执行提供的函数,并返回一个新数组。

过滤数组:

  1. filter():根据条件筛选数组中的元素,返回一个新数组。

数组排序:

  1. sort():对数组元素进行排序,默认是按照字符串 Unicode 编码顺序进行排序。
  2. reverse():颠倒数组中元素的顺序。

其他常用方法:

  1. indexOf()lastIndexOf():返回指定元素在数组中的位置。
  2. find()findIndex():根据条件查找元素或元素的索引。
  3. reduce()reduceRight():对数组中的所有元素执行一个提供的函数,并将其结果汇总为单个值。
  4. includes():判断数组是否包含某个元素,返回布尔值。
    6.数组去重的方式

      6.1. 使用 Set(ES6新增):Set 是 ES6 中引入的一种数据结构,它只存储唯一值,可用于去除数组中的重复元素。

const array = [1, 2, 2, 3, 4, 4, 5];

const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]

      6.2. 使用 filter 和 indexOf/lastIndexOf:利用 filter 方法和 indexOflastIndexOf 方法来创建一个只包含唯一值的新数组。

const array = [1, 2, 2, 3, 4, 4, 5];

const uniqueArray = array.filter((value, index, self) => {
  return self.indexOf(value) === index;
});
console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]

         6.3.使用 reduce:通过 reduce 方法遍历数组,创建一个新数组并将非重复的元素添加进去。

const array = [1, 2, 2, 3, 4, 4, 5];

const uniqueArray = array.reduce((accumulator, currentValue) => {
  if (!accumulator.includes(currentValue)) {
    accumulator.push(currentValue);
  }
  return accumulator;
}, []);
console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]

  •    6.4.使用map:使用 Map 数据结构,利用其键的唯一性特性,将数组元素作为 Map 的键
  •  
    const array = [1, 2, 2, 3, 4, 4, 5];
    
    const uniqueArray = Array.from(new Map(array.map((item) => [item, item])).values());
    console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
    

    7.原型链

    原型链是 JavaScript 中的一个重要概念,用于实现继承和属性/方法的查找。在 JavaScript 中,几乎所有的对象都是通过原型链相互关联的。

    原型(Prototype):

  • 每个 JavaScript 对象(除了 null)都有一个原型(prototype)。
  • 对象可以直接访问其他对象的属性和方法,因为它们共享一个原型链。
  • 原型链是一种对象间的关联形式,当查找对象的属性或方法时,JavaScript 引擎会顺着原型链往上查找,直到找到对应的属性或方法。
  • 当一个对象被创建时,它的原型会指向构造该对象的构造函数的原型(prototype 属性)。
  • 这样就形成了一个链式结构,每个对象都有一个指向其原型的引用。
  • function Animal(name) {
      this.name = name;
    }
    
    Animal.prototype.sayName = function() {
      console.log(`My name is ${this.name}`);
    };
    
    function Dog(name, breed) {
      Animal.call(this, name);
      this.breed = breed;
    }
    
    Dog.prototype = Object.create(Animal.prototype);
    Dog.prototype.constructor = Dog;
    
    Dog.prototype.bark = function() {
      console.log("Woof!");
    };
    
    const myDog = new Dog('Buddy', 'Golden Retriever');
    myDog.bark(); // 输出: Woof!
    myDog.sayName(); // 输出: My name is Buddy
    

    在上述例子中,myDog 实例继承了 Animal 原型链上的 sayName 方法,即使这个方法定义在 Animal 构造函数的原型上。这展示了原型链的继承特性

       8.防抖和节流

        防抖(Debouncing)和节流(Throttling)是优化前端性能的两种常见技术,主要用于减少高频率触发的事件或函数执行次数。   

防抖(Debouncing):防抖是指在短时间内连续触发同一事件时,只执行最后一次操作。例如,当用户持续触发某事件(如浏览器窗口大小改变)时,只有在事件停止后的特定时间间隔之后才执行相应操作。

function debounce(func, delay) {
  let timer;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, arguments);
    }, delay);
  };
}

// 使用防抖
const debouncedFunc = debounce(() => {
  // 需要执行的操作
}, 300);

节流(Throttling):节流是指在一段时间内只允许函数执行一次,即规定一个时间间隔,当触发事件时,如果过了这个时间间隔,才会执行相应操作。

function throttle(func, limit) {
  let inThrottle;
  return function() {
    if (!inThrottle) {
      func.apply(this, arguments);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// 使用节流
const throttledFunc = throttle(() => {
  // 需要执行的操作
}, 300);

  9.面向对象?

       面向对象编程(OOP)是一种编程范式,它以对象为中心,将数据和操作数据的方法组合到一个单个单元中。在 JavaScript 中,虽然是一门基于原型的语言,但它也支持面向对象编程。

      JavaScript 是基于原型的语言,它的对象系统是基于原型链的。虽然 ES6 引入了类的概念,但实际上这些类只是语法糖,底层仍然是基于原型的实现。

使用构造函数和原型链实现 OOP:

// 构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 原型方法
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
};

// 创建实例
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);

person1.sayHello(); // 输出: Hello, my name is Alice and I'm 30 years old.
person2.sayHello(); // 输出: Hello, my name is Bob and I'm 25 years old.

  使用 ES6 的 class 实现:

// 使用 class 定义一个 Animal 类
class Animal {
  // 构造函数,在创建实例时执行
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  // 方法
  makeSound() {
    console.log('Some generic sound');
  }

  // 静态方法
  static info() {
    console.log('This is an animal class');
  }
}

// 创建 Animal 类的实例
let cat = new Animal('Kitty', 3);

// 访问属性并调用方法
console.log(cat.name); // 输出: Kitty
cat.makeSound(); // 输出: Some generic sound

// 静态方法可以直接通过类访问
Animal.info(); // 输出: This is an animal class

在上述示例中,class Animal 定义了一个 Animal 类,它有一个构造函数 constructor,定义了对象的属性 nameagemakeSound 是一个方法,用于在控制台输出一段文本。static info() 是一个静态方法,它属于类本身,而不是类的实例。

使用 ES6 的 class 关键字使得面向对象的实现更加直观和易于理解,同时也能够更好地利用 JavaScript 的面向对象编程特性。



目录

(一)JavaScript 

    1.深拷贝与浅拷贝的区别?

2.什么是闭包?

3.call/apply/bind

        4.浏览器存储的区别?

     5.常用的数组方法有哪些?

    6.数组去重的方式

7.原型链

       8.防抖和节流

  9.面向对象?

  10.箭头函数

11.new  都做了什么?

12.es6都用了哪些新特性?


  10.箭头函数

   箭头函数是 ES6 新增的函数表达式语法,提供了更简洁的语法形式和一些特性。它与传统的函数声明/表达式有一些区别和优势:

  10.1. 箭头函数的语法:

   基本语法:(parameters) => { statements }

  • 如果只有一个参数,可以省略括号:param => { statements }
  • 如果没有参数,或有多个参数,需要使用括号:() => { statements }(param1, param2) => { statements }
    // 传统函数表达式
    function greet(name) {
      return `Hello, ${name}!`;
    }
    
    // 箭头函数
    const greetArrow = name => `Hello, ${name}!`;
    
    // 使用箭头函数作为回调函数
    const numbers = [1, 2, 3, 4];
    const squared = numbers.map(num => num * num);

    10.2. 特点:

  • 无 this 绑定:箭头函数没有自己的 this,它继承自外围作用域。这意味着它不会在函数内部创建自己的 this,而是沿用外部作用域的 this
  • 无 arguments 对象:箭头函数也没有自己的 arguments 对象,可以使用剩余参数 ...args 来代替。
  • 不能作为构造函数:箭头函数不能被用作构造函数,无法使用 new 关键字。
  • 没有原型:因为箭头函数无法作为构造函数,所以也没有 prototype 属性。

总的来说,箭头函数提供了更简洁的语法,尤其适合于一些简单的回调函数或者函数式编程中的应用。然而,在某些场景下,仍需要考虑传统函数的特性以及上下文绑定问题

11.new  都做了什么?

在 JavaScript 中,new 关键字用于创建一个对象实例。当使用 new 关键字来调用一个函数时,发生了以下操作:
 

  1. 创建一个空的对象new 创建一个空的 JavaScript 对象。

  2. 将构造函数中的 this 指向新对象new 关键字将构造函数中的 this 关键字指向新创建的对象实例。

  3. 将新对象链接到原型:新创建的对象会被链接到构造函数的原型对象(prototype),使新对象能够访问原型对象上的方法和属性。

  4. 执行构造函数中的代码new 调用构造函数,将实参传递给构造函数,从而在新创建的对象上执行构造函数内部的逻辑。构造函数可以对新对象进行初始化。

  5. 返回新对象:如果构造函数中没有显式返回其他对象,new 操作符会隐式返回新创建的对象实例。如果构造函数中有返回其他对象,则返回该对象。
     

    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    
    // 使用 new 创建 Person 对象实例
    const john = new Person('John', 30);
    
    // 上述代码等同于以下操作
    const john = {};
    Person.call(john, 'John', 30);
    // 此处,john 的原型会指向 Person.prototype
    

12.es6都用了哪些新特性?

     ES6(ECMAScript 2015)作为 JavaScript 的一次重大更新,引入了许多新特性和语法改进。以下是 ES6 中一些常用和主要的新特性: 

12.1. let 和 const 声明: letconst 关键字用于声明变量,let声明的变量可被重新赋值,const声明的变量为常量,不可重新赋值。

12.2. 箭头函数:箭头函数语法更简洁,不绑定自己的 thisargumentssupernew.target,适合于回调函数或简短的函数表达式。

12.3. 模板字面量(Template Literals):使用反引号(`)包裹的字符串,可以插入变量和换行,提供了更加灵活和易读的字符串拼接方式。

12.4. 解构赋值(Destructuring):方便地从数组或对象中提取数据,并赋值给变量。

12.5. 对象属性简写和方法简写:对象属性的简写形式:{ x, y } 相当于 { x: x, y: y }。对象方法的简写形式:{ method() { ... } } 相当于 { method: function() { ... } }

12.6. 默认参数:可以为函数参数设置默认值,简化函数调用时的参数传递。

12.7. 扩展运算符(Spread Operator)和剩余参数(Rest Parameter):扩展运算符(...)可以将数组展开成逗号分隔的值;剩余参数则可以将一系列参数收集为一个数组。

12.8. 类(Class):引入了类的概念,更符合传统面向对象编程的语法。

12.9. Promise 和 async/await:Promise 提供了更好的异步编程解决方案,async/await 则提供了更清晰、更易理解的异步代码写法。

12.10. 模块化(Module):ES6 提供了原生的模块化支持,通过 importexport 关键字实现模块的导入和导出。

12.11. Map 和 Set 数据结构:Map 是键值对集合,Set 是一组不重复的值的集合,提供了更多的数据结构选项。

13.script 引入js文件async和defer的区别?

如果一个script加了defer属性,即使放在head里面,它也会在html页面解析完毕之后再去执行,也就是类似于把这个script放在了页面底部。
对于async,这个是html5中新增的属性,它的作用是能够异步的加载和执行脚本,不因为加载脚本而阻塞页面的加载。一旦加载到就会立刻执行。

14.js引入文件头部和尾部的区别?

如果将JavaScript文件放置在head当中,会先加载JS文件,之后再继续执行,那么此时,如果JS文件比较大,页面加载就会很慢,导致空白。

如果将JS文件放置在底部的话,可以让JS文件与图片几乎同时下载,使得页面当中的内容能够尽快的下载下来,但是,由于网页基本结构与样式均已经加载完成,那么此时负责交互的JS并没有下载下来,必然也会对用户的体验造成影响。
所以,如果“交互性优先”,那么我们应当将JS放置在顶部。如果对于交互性要求没那么高的页面,我们将JS放置在底部

15.js基本数据类型和引用类型的区别?   

 基本数据类型包括:数值(number)、字符串(string)、布尔(boolean)、空(null)、未定义(undefined)等。基本数据类型是直接存储在栈中的,值与值之间是独立存在,修改一个变量不会影响其他的变量
引用数据类型包括:对象(object)、数组(array)、函数(function)、日期(date)等。引用数据类型是存储在堆中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,指向实际数据所在的内存地址(对象的引用),因此无法直接访问,只能通过引用来间接访问。如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响.
 区别:

1、存储位置:基本数据类型的值是直接存储在栈内存中,而引用类型的值存储在堆内存中。

2、复制方式:基本数据类型赋值时,是将变量的值复制一份给新的变量,互不影响;引用数据类型赋值时,是将变量的地址(指针)复制一份给新的变量,因此两个变量指向同一块堆内存空间。

3、内存管理方式:基本数据类型在栈内存中自动管理,不需要开发人员手动回收;而引用数据类型需要开发人员手动进行内存管理,包括创建、使用、销毁等。

(二)ES6/ES5如何实现继承?

 在 JavaScript 中,ES5 通过原型链和构造函数来实现继承,而 ES6 引入了类(class)这一概念,提供了更清晰的方式实现继承。

ES5 实现继承(原型链继承):

 1.使用原型链来实现继承,通过指定子类的原型为父类的实例来实现。
function Parent() {
  this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
  console.log('Hello, ' + this.name);
}
function Child() {
  this.age = 10;
}
Child.prototype = new Parent();
var child = new Child();
child.sayHello(); // 输出:Hello, Parent

 缺点是子类实例共享父类实例的属性和方法,无法向父类传递参数。子类实例共享父类的属性,   可能导致属性污染。

2.构造函数继承: 构造函数继承通过在子类构造函数中调用父类构造函数来实现继承,可以继承父类的属性。但是无法继承父类的原型上的方法。
function Parent(name) {
  this.name = name;
}
function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}
var child = new Child('Alice', 10);
console.log(child.name); // 输出:Alice
 3、组合继承:组合继承是将原型链继承和构造函数继承结合起来使用的方式,既继承了父类的属性和方法,又不共享父类实例的属性和方法
function Parent(name) {
  this.name = name;
}
Parent.prototype.sayHello = function() {
  console.log('Hello, ' + this.name);
}
function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child('Alice', 10);
child.sayHello(); // 输出:Hello, Alice
4、原型式继承:原型式继承使用一个中间对象作为父类,通过浅拷贝的方式来实现继承。
function createObject(obj) {	//定义一个函数createObject,接受一个参数obj,表示要继承的对象。
  function F() {}
  F.prototype = obj;	//将F的原型对象设置为obj
  return new F();
}
var parent = {
  name: 'Parent',
  sayHello: function() {
    console.log('Hello, ' + this.name);
  }
}
var child = createObject(parent);
child.name = 'Child';
child.sayHello(); // 输出:Hello, Child

 ES6 实现继承(原型链继承):

1、class继承:ES6引入了class的概念,可以使用class关键字来定义类。通过extends关键字来实现继承,子类继承了父类的属性和方法。
class Parent {
  constructor(name) {
    this.name = name;
  }
  sayHello() {
    console.log('Hello, ' + this.name);
  }
}
class Child extends Parent {
  constructor(name, age) {
    super(name);
    this.age = age;
  }
}
let child = new Child('Alice', 10);
child.sayHello(); // 输出:Hello, Alice
2、Object.create()方法:Object.create()方法可以创建一个新对象,并将该对象的原型指向指定的对象。通过这种方式可以实现对象的继承。
let parent = {
  name: 'Parent',
  sayHello: function() {
    console.log('Hello, ' + this.name);
  }
};
let child = Object.create(parent);
child.name = 'Child';
child.sayHello(); // 输出:Hello, Child
3、Object.setPrototypeOf()方法:Object.setPrototypeOf()方法可以将一个对象的原型指向另一个对象,从而实现对象的继承。
let parent = {
  name: 'Parent',
  sayHello: function() {
    console.log('Hello, ' + this.name);
  }
};
let child = {
  age: 10
};
Object.setPrototypeOf(child, parent);
child.sayHello(); // 输出:Hello, Parent

  

4、扩展运算符:通过扩展运算符(…)可以将一个对象的属性和方法复制到另一个对象上,从而实现对象的继承。
let parent = {
  name: 'Parent',
  sayHello: function() {
    console.log('Hello, ' + this.name);
  }
};
let child = {
  age: 10,
  ...parent
};
child.sayHello(); // 输出:Hello, Parent

(三)HTML

   1.html5有哪些新特性?

HTML5 引入了许多新特性和 API,它是 HTML 最新的版本,提供了更多的功能和工具,旨在提升网页开发的体验和性能。以下是一些 HTML5 的主要特性:

1.1. 语义化标签:引入了语义化标签(如 <header><footer><section><article><nav> 等)来更清晰地标识页面结构和内容,有助于搜索引擎优化和可访问性。

1.2. 表单增强:新的表单控件(例如日期选择器、颜色选择器、范围选择器、邮箱、URL 等类型的输入)和属性(placeholderrequiredpattern 等),提供更丰富的表单交互。

1.3. 多媒体:<audio><video> 元素使得在网页中嵌入音频和视频更为简单。canvas 元素允许通过 JavaScript 渲染图形和动画。WebRTC 提供了实时通信的 API,支持音视频通话等。

1.4. 本地存储:localStoragesessionStorage 提供了在客户端本地存储数据的功能,允许离线使用网页应用。IndexedDB 提供了浏览器端的数据库存储功能。

1.5. 地理定位:Geolocation API 允许网页获取用户的地理位置信息。

1.6. 通信:WebSockets 提供了全双工通信,允许浏览器和服务器之间建立持久的连接。Server-Sent Events (SSE) 允许服务器推送事件到浏览器。

1.7. WebGL:基于 OpenGL ES 的 3D 图形库,允许在浏览器中呈现 3D 图形。

1.8. Web Workers:允许在浏览器后台运行 JavaScript 脚本,提高网页性能。

1.9. Responsive Web Design 支持:提供更多的 CSS 功能以支持响应式网页设计。

1.10. 其他特性:支持拖放操作、新的事件处理方式、内容编辑功能的增强等。

2.重绘和重排(回流)是什么?如何优化?

在前端开发中,"重绘" 和 "重排" 是性能优化中常遇到的两个概念,也称为回流(Reflow)和重绘(Repaint)。

重绘(Repaint):重绘是指更改元素的非布局属性(例如颜色、背景等),不会影响布局的变化。当一个元素的外观属性发生改变时,浏览器会根据新的样式重新绘制元素。

重排(Reflow):重排是指更改元素的布局属性(例如尺寸、位置等),导致页面上其他元素的尺寸、位置或布局属性发生变化。浏览器需要重新计算元素的几何属性,并重新构建渲染树。

优化重绘和重排的方法:

  1. 减少 DOM 操作:对 DOM 树的修改次数越少越好,尽量一次性对 DOM 进行多个修改。
  2. 避免多次读取布局属性:在计算前,避免读取会导致重排的属性(如 offsetLeft、clientTop、scrollTop 等)。
  3. 使用样式类操作:在可能的情况下,通过修改样式类而不是单独样式来修改元素的样式。
  4. 离线操作:将要进行多次修改的元素先脱离文档流,修改完成后再放回文档流。
  5. 合并多次样式修改:通过一次修改完成多个样式的变化,减少多次修改样式属性带来的重排和重绘。
     

(三)css

   1.如何实现一个宽高不定的元素垂直水平居中?

      1.1 使用 Flexbox 方法:

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  /* 可选项:若需要水平居中,使用 flex-direction: column; */
  /* flex-direction: column; */
}

/* HTML 结构 */
<div class="container">
  <div class="centered-element">内容</div>
</div>

    1.2使用绝对定位与 transform 方法:

  

.container {
  position: relative;
}

.centered-element {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

    2.什么是伪类?什么是伪元素?

伪类(Pseudo-classes):

伪类是一种用于向选择器添加特殊效果的关键字。它们允许根据文档的状态或特定条件选择元素,例如:链接状态、鼠标悬停、选择的状态、父元素的状态等。

一些常见的伪类包括:

  • :hover - 鼠标悬停时应用的样式。
  • :focus - 元素获取焦点时应用的样式。
  • :active - 元素被激活时(例如鼠标点击)应用的样式。
  • :nth-child() - 选择特定顺序的子元素。
伪元素(Pseudo-elements):

伪元素允许你为选中的元素的特定部分应用样式,例如元素的前面、后面或者特定内容的前面、后面。它们实际上创建了文档中不存在的虚拟元素,而不是选择文档中已有的元素。

一些常见的伪元素包括:

  • ::before - 在选中元素的内容之前插入内容。
  • ::after - 在选中元素的内容之后插入内容。
  • ::first-line - 选择元素的第一行。
  • ::first-letter - 选择元素的第一个字母。

总结:

  • 伪类 选择器是用于选择元素的特定状态的关键字,根据元素的状态来应用样式。

  • 伪元素 选择器则是为元素的特定部分而创建的虚拟元素,允许对元素的某些部分应用样式。

     

     3.css position定位

  • relative 不脱离文档流,参考自身静态位置通过 top(上),bottom(下),left(左),right(右) 定位,并且可以通过z-index进行层次分级。

    absolute 脱离文档流,通过 top,bottom,left,right 定位。选取其最近的父级定位元素,当父级 position 为 static 时,absolute元素将以body坐标原点进行定位,可以通过z-index进行层次分级。

    fixed 固定定位,这里他所固定的对像是浏览器可视窗口而并非是body或是父级元素。可通过z-index进行层次分级。

    CSS中定位的层叠分级:z-index: auto | number;

    auto 遵从其父对象的定位

    number 无单位的整数值。可为负数


  • 4.css 样式优先级

    内联样式 > 内部样式 > 外部样式

    !important > 内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器 > 通配选择器 > 浏览器默认样式

    如果两组样式具有相同的优先级,则后定义的样式将覆盖先定义的样式。

  (四)vue

      1.Vue.js 2 和 Vue.js 3 的主要区别:

     1.1  响应性原理:Vue 3 使用 Proxy 代替 Object.defineProperty 实现响应式。

     1.2  性能提升:Vue 3 在性能方面做出了很多优化,如静态树提升、基于 ES2015 Proxy 的响应式追踪等。

     1.3.  Composition API:Vue 3 引入了 Composition API,允许按逻辑相关性组织代码,解决了 Vue 2 中复杂组件难以维护的问题。

     1.4  Tree-Shaking 支持:Vue 3 更加支持 Tree-Shaking,可以更好地按需加载。

     1.5   TypeScript 支持:Vue 3 在核心库中提供了对 TypeScript 更好的支持。

      1.6    Fragment 和 Teleport 组件:Vue 3 引入了 Fragment 和 Teleport 组件,提供更灵活的渲染选项。

2.Vue.js 3 中 Composition API 的优势:

  1. 逻辑关注点分离:更易维护和重用代码。
  2. 代码组织性:更加灵活地组织逻辑代码。
  3. 逻辑复用:更好地支持逻辑复用和抽象。
  4. TypeScript 支持:更好地支持 TypeScript 的类型推断和编辑器的提示。

3.Vue 实例生命周期

Vue 实例生命周期由一系列钩子函数组成,这些钩子函数允许您在 Vue 实例中的不同阶段执行代码。Vue 实例的生命周期主要包括创建、挂载、更新和销毁等阶段。

创建阶段(Creation)
  1. beforeCreate:在实例初始化之后、数据观测和事件配置之前被调用。此时实例的选项和默认数据都初始化完成,但实例还未创建。

  2. created:实例已经完成创建,数据观测、属性和方法的运算、watch/event 事件回调都已被设置。这是一个适合初始化数据的阶段。

挂载阶段(Mounting)
  1. beforeMount:在挂载开始之前被调用,相关的 render 函数首次被调用。

  2. mounted:在实例被挂载后调用。此时组件已经在 DOM 中显示,可以进行 DOM 操作。

更新阶段(Updating)
  1. beforeUpdate:数据更新时,在重新渲染之前调用。

  2. updated:在数据更改导致虚拟 DOM 重新渲染和 patch 更新真实 DOM 后调用。

销毁阶段(Destroying)
  1. beforeDestroy:在实例销毁之前调用。此时实例仍然可用。

  2. destroyed:在实例销毁之后调用,所有事件监听器和子实例被销毁。

4.组件通信

       在 Vue 中,组件通信是指不同组件之间传递数据或信息。Vue 提供了多种方法用于实现组件通信,其中包括父子组件之间、兄弟组件之间以及跨级组件之间的通信方式:

父子组件通信:
  1. Props(属性):父组件通过 props 向子组件传递数据。
  2. Events(事件):子组件通过 $emit 触发事件,父组件通过 @event 监听并接收数据。
  3. $refs:父组件可以通过 ref 获取子组件的引用,直接调用子组件的方法或访问其数据。
子父组件通信:
  1. $emit 和 $on:子组件通过 $emit 触发事件,父组件通过 $on 监听事件来传递数据。
  2. $attrs 和 $listeners:父组件通过 v-bind="$attrs" 和 v-on="$listeners" 传递所有属性和事件给子组件。
兄弟组件通信:
  1. 事件总线(Event Bus):通过创建一个空的 Vue 实例作为中央事件总线来实现兄弟组件通信。
  2. Vuex 状态管理:使用 Vuex 来实现兄弟组件之间的状态共享。
跨级组件通信:
  1. Provide 和 Inject:父组件通过 provide 提供数据,子孙组件通过 inject 注入获取数据。
  2. $root 和 $parent:通过 $root 和 $parent 可以访问根实例和父组件的数据和方法,但不推荐过度使用,容易导致组件耦合性过高。
    // 创建一个空的 Vue 实例作为事件总线
    const bus = new Vue();
    
    // 在发送组件中发送事件
    bus.$emit('event-name', data);
    
    // 在接收组件中监听事件
    bus.$on('event-name', (data) => {
      // 处理接收到的数据
    });
    

5.计算属性和侦听器

   在 Vue 中,计算属性和侦听器是用于响应数据变化并执行相应逻辑的两种重要方法。

   计算属性(Computed Properties):
  1. 作用:用于基于其他数据进行计算,产生一个新的衍生数据。
  2. 语法:使用 computed 属性来定义计算属性。
  3. 特点:计算属性会缓存计算结果,只有在依赖的响应式数据改变时才会重新计算。
  4. 示例
    new Vue({
      data: {
        radius: 5
      },
      computed: {
        // 计算属性,根据 radius 计算圆的面积
        circleArea() {
          return Math.PI * this.radius ** 2;
        }
      }
    });
    
    侦听器(Watchers):
    1. 作用:用于监听响应式数据的变化,并执行异步或开销较大的操作。
    2. 语法:使用 watch 属性来定义侦听器。
    3. 特点:当需要执行异步操作或对数据的改变做出响应时使用。
    4. 示例
    new Vue({
      data: {
        radius: 5
      },
      watch: {
        // 监听 radius 的变化
        radius(newValue, oldValue) {
          console.log(`Radius changed from ${oldValue} to ${newValue}`);
          // 在这里执行一些异步操作或其他处理
        }
      }
    });
    
     区别:
     计算属性适合进行简单的、基于响应式数据的计算,它会根据相关数据的变化进行更新,并   且具有缓存特性。
     侦听器适用于执行异步操作或当需要对数据的变化做出响应时,它能监听某个数据的变化,   并在数据变化时执行相应的操作。

6.虚拟 DOM

虚拟 DOM(Virtual DOM)是前端框架(如 React 和 Vue 等)的核心概念之一,用于提高页面渲染性能。

什么是虚拟 DOM?

虚拟 DOM 是浏览器端的一个 JavaScript 对象表示,类似于真实 DOM 的抽象层。它是框架在内存中对 DOM 结构的一种描述,包含了大部分 DOM 的属性。当数据发生变化时,框架会先更新虚拟 DOM,然后和之前的虚拟 DOM 进行比对(称为 diff 算法),找出差异后批量更新实际的 DOM。

工作原理:
  1. 当数据发生变化时,框架会创建一个新的虚拟 DOM。
  2. 框架通过 diff 算法比较新旧虚拟 DOM,找出差异。
  3. 仅将真正变化的部分更新到实际的 DOM 结构。
虚拟 DOM 的优势:
  1. 性能优化:通过批量更新变化部分,减少对实际 DOM 的操作,提高页面渲染性能。
  2. 跨平台应用:虚拟 DOM 是框架内部的抽象层,使得同一套代码可以用于不同平台,如 Web、React Native 等。
  3. 简化复杂度:操作虚拟 DOM 比直接操作实际 DOM 更简单,提高开发效率。

注意事项:

  1. 虚拟 DOM 的使用需要权衡,对于简单页面可能会带来额外开销。
  2. 虚拟 DOM 不是万能的,合理使用能提高性能,但不是所有情况都适用。

框架使用虚拟 DOM 的方式在一定程度上提高了页面的性能和开发效率,但需要结合具体场景和需求选择最佳的优化方案。

7.服务端渲染 (SSR)

服务端渲染(Server-Side Rendering,SSR)是一种在服务器端生成 HTML 内容并将其发送到客户端的技术。与传统的单页面应用(SPA)相比,SSR 使得页面在服务器端首次渲染完整 HTML 页面,而不是依赖客户端 JavaScript 在浏览器中进行渲染。

工作原理:
  1. 请求处理:客户端发起请求到服务器。
  2. 服务器端渲染:服务器生成 HTML 页面,包含初始内容和数据。
  3. 响应返回:服务器将渲染好的 HTML 页面发送给客户端。
  4. 客户端激活:客户端接收 HTML 页面并激活,成为一个 SPA,接管页面交互,绑定事件和路由。
优势:
  1. 更快的首屏加载:由于服务端返回渲染好的 HTML,用户能更快地看到内容,有助于改善首屏加载性能。
  2. SEO 友好:搜索引擎能更好地爬取和索引服务器返回的 HTML 内容,提高网页在搜索引擎中的排名。
  3. 更好的性能:降低客户端渲染的时间,减轻客户端设备的压力。
Vue.js 中的 SSR:

Vue.js 也支持服务端渲染,称为 Vue SSR。通过在服务端渲染 Vue 组件,可以在服务器端生成完整的 HTML 页面,然后在客户端激活为 SPA。Vue SSR 基于同构(Isomorphic)的思想,即相同的代码可以在服务器和客户端同时运行。

注意事项:
  1. SSR 带来服务器端的计算压力,需要考虑服务器资源。
  2. 路由管理和状态管理需要特别注意,要确保客户端和服务器端的状态一致性。

服务端渲染在提高首屏加载速度和 SEO 上具有明显优势,但需要权衡服务器压力和复杂性,选择合适的场景来应用。

8.Vuex 状态管理,pinia

「Vuex」和「Pinia」都是用于 Vue.js 应用中状态管理的工具。它们提供了一种有效的方式来管理应用中的状态,但在实现方式和一些特性上有一些差异。

Vuex(Vue.js 官方状态管理工具)
  • 特点:Vuex 是官方提供的状态管理模式库,用于 Vue 应用中管理应用级的状态。
  • 核心概念:包括 state(状态)、mutations(更改状态的方法)、actions(处理异步操作的方法)和 getters(对 state 进行筛选和派生)。
  • 优势:完全集成在 Vue 应用中,提供强大的工具和开发者社区支持。
  • 适用场景:大型应用中需要管理大量全局状态时,尤其在多个组件之间共享状态时。
Pinia
  • 特点:Pinia 是一个由 Vue 核心团队开发的基于 Vue 3 Composition API 的状态管理库。
  • 核心概念:采用类似 Vuex 的模块化状态管理方式,但使用 Composition API 编写逻辑更加灵活。
  • 优势:更轻量、更灵活,能够更好地利用 Composition API 的优势,支持 TypeScript,易于测试。
  • 适用场景:对 Vue 3 Composition API 有较深了解,或希望更轻量灵活的状态管理方案。
选择:
  • 对于 Vue 2 应用,尤其是大型应用,Vuex 是一个成熟且强大的选择。
  • 对于 Vue 3 应用,如果喜欢使用 Composition API 并希望更轻量的状态管理,Pinia 是一个很好的选择。

选择使用哪种状态管理工具,取决于你的应用场景、团队的经验以及个人偏好。它们都旨在帮助管理 Vue 应用中的状态,并根据需求提供不同的特性和灵活性。

9.pinia的了解和使用

Pinia 是一个基于 Vue 3 Composition API 的状态管理库。它提供了一种简单、轻量级且灵活的方式来管理 Vue 应用中的状态。以下是一些关于 Pinia 的了解和使用:

了解 Pinia:
  1. 基于 Composition API:Pinia 是基于 Vue 3 Composition API 构建的,利用 Composition API 的优势进行状态管理。
  2. 轻量级:相较于 Vuex,Pinia 更为轻量,提供了一种更简洁的状态管理方案。
  3. 支持 TypeScript:Pinia 支持 TypeScript,能够提供更好的类型支持。
  4. 模块化的状态管理:类似 Vuex 的模块化状态管理,允许你在应用中创建多个 store,每个 store 可以管理自己的状态和方法。
  5. 易于测试:Pinia 结合了 Composition API 的优势,使得代码更容易测试和维护。


    使用 Pinia:
    安装 Pinia:
    npm install pinia
    

    创建一个 Pinia store:
     

    // 创建 store
    import { defineStore } from 'pinia';
    
    export const useCounterStore = defineStore({
      id: 'counter',
      state: () => ({
        count: 0,
      }),
      actions: {
        increment() {
          this.count++;
        },
      },
    });
    
    在 Vue 组件中使用 Pinia:
    <template>
      <div>
        <p>Count: {{ counter.count }}</p>
        <button @click="increment">Increment</button>
      </div>
    </template>
    
    <script>
    import { useCounterStore } from '@/store';
    
    export default {
      setup() {
        const counter = useCounterStore();
    
        const increment = () => {
          counter.increment();
        };
    
        return {
          counter,
          increment,
        };
      },
    };
    </script>
    

   10. vue中key的作用?

      Vue 中的 key 是用来给每一个节点做唯一标识的属性,它的作用是帮助 Vue 进行 DOM 元素的复用。具体来说,当 Vue 更新 DOM 时,它会基于新数据生成一棵 Virtual DOM 树,并将其与旧的 Virtual DOM 树进行对比,通过对比找到需要进行更新的节点,从而达到更新视图的目的。在进行对比时,Vue 会根据 key 的值来判断新旧节点是否相同,从而决定是否进行复用,而不是仅根据节点的标签名和属性等静态信息来判断。
     key 属性是 Vue 中一个重要的概念,可以帮助我们优化页面性能,确保数据渲染的正确性。在使用时,需要注意确保 key 值的唯一性,并且避免直接修改数据源。

11.vue2中如何实现双向绑定?

Vue2采用数据劫持并结合发布者-订阅者模式的方式,通过ES6的object.defineProperty()方法去劫持各个属性的setter/getter方法,在数据发生变化的时候,发布消息给订阅者,触发相应的监听回调。

具体步骤如下:

1、需要observe(观察者)的数据对象进行遍历,包括子属性对象的属性,都加上setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到数据的变化。

2、compile(解析)解析模版指令,将模版中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。

3、watcher(订阅者)是observer和compile之间通信的桥梁,主要做的事情是:

Ⅰ、在实例化时往属性订阅器(dep)里添加自己;

Ⅱ、自身必须有一个update()方法;

Ⅲ、待属性变动dep.notice()通知时,能够调用自身的update()方法,并触发compile中绑定的回调。

4、MVVM作为数据绑定入口,整合observer,compile和watcher来监听自己的model数据变化,通过compile来解析编译模版,最终利用watcher搭起observer和compile之间的通信桥梁,达到数据变化->更新视图:视图交互变化->数据model变更的双向绑定效果。

12.vue2中数组的响应式?

遍历它所有的元素,然后使用Object.defineProperty方法对每个元素进行劫持。然后还会重写数组原型上push、pop、shift、unshift、sort、reverse、splice七个方法。

13.懒加载的原理?
懒加载的原理:页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片,只有通过javascript设置了图片路径,浏览器才会发送请求。 懒加载的原理就是先在页面中把所有的图片统一使用一张占位图进行占位,==把真正的路径存在元素的“data-url”==的自定义属性里,要用的时候就取出来,再设置;

(五)浏览器

     浏览器工作原理:

    1.解释浏览器的渲染过程(从输入 URL 到页面显示)是如何进行的?    

1.1  URL 解析和加载:

  1. 地址解析:浏览器获取用户输入的 URL,并进行地址解析,确定协议、域名和路径等信息。

  2. 发起 HTTP 请求:浏览器向服务器发起请求,请求相应页面资源。

  3. 建立连接:如果协议是 HTTP 或 HTTPS,浏览器与服务器建立 TCP 连接。

  4. 收发数据:浏览器发送请求,并接收服务器返回的数据,这些数据包含 HTML、CSS、JavaScript 文件等。

1.2 构建 DOM 树和 CSSOM 树:

  1. 解析 HTML:浏览器根据接收到的 HTML 文件构建 DOM 树(文档对象模型),表示页面的结构。

  2. 解析 CSS:解析 CSS 文件并构建 CSSOM 树(CSS 对象模型),表示页面的样式。

1.3 创建渲染树(Render Tree):

  1. 合并 DOM 树和 CSSOM 树:将 DOM 树和 CSSOM 树结合,创建渲染树,该树只包含需要显示的元素及其样式信息。

  2. 计算渲染树布局:计算渲染树中每个节点的位置和大小。

1.4. 页面绘制(Painting):

  1. 绘制页面:根据渲染树布局信息,绘制整个页面的像素。

  2. 显示页面:渲染完成后将像素信息发送给 GPU,最终显示在用户的屏幕上。

1.5  交互和用户输入响应:

  1. 页面交互:用户可以与页面交互,触发事件并执行 JavaScript 代码。

  2. 重新渲染:页面内容变化时,浏览器重新进行上述流程,重新构建、布局和绘制页面。

    2.什么是浏览器的同源策略?它是如何工作的?

同源策略的规则:

  1. 协议相同:请求的协议必须与目标资源的协议相同(如 HTTP 和 HTTPS)。

  2. 域名相同:主域名必须相同,子域名不同。

  3. 端口相同:端口号必须相同,如果有指定端口的话。

同源策略的限制:

  1. Cookie、LocalStorage 和 IndexDB:不同源的页面不能访问对方的 Cookie、LocalStorage 和 IndexDB 等数据。

  2. DOM 和 JavaScript 对象:不同源的页面无法操作对方的 DOM 和 JavaScript 对象。

  3. AJAX 请求:XMLHttpRequest 和 Fetch 等请求受到同源策略的限制,无法进行跨域请求。

  4. Frame 和 iframe:嵌入的 frame 和 iframe 受同源策略限制,无法直接操作对方的文档。

如何突破同源策略:

  1. 跨域资源共享(CORS):服务端设置 HTTP 头部,允许跨域访问。

  2. JSONP:通过 <script> 标签实现跨域请求。

  3. 代理:使用服务器端代理请求来绕过同源策略。

同源策略是浏览器的一个基本安全特性,有效保护了用户的隐私和数据安全。突破同源策略的方法需要服务器端的支持,且需要慎重考虑安全问题。

     性能优化:

         1.2. 文件压缩和合并:
         CSS 和 JavaScript 文件压缩:使用压缩工具(例如 UglifyJS、Terser、CSSNano)压缩文件。
          文件合并:将多个 CSS 或 JavaScript 文件合并为单个文件,减少请求次数。
         
         1.3. 缓存控制:
             使用缓存:利用浏览器缓存,通过设置 HTTP 头部中的缓存控制,减少重复下载。
             资源版本控制:给文件添加版本号或哈希值,使浏览器能够识别新文件,强制重新加载。 

         1.4. 网络请求优化:
            减少请求次数:合并文件、使用雪碧图、使用字体图标等,减少资源请求次数。
        
         1.5. 懒加载和预加载:
         图片懒加载:延迟加载图片,仅当用户滚动至图片部分时才加载图片。
         预加载重要资源:使用 <link rel="preload"> 预加载关键资源,提前加载需要的资源。

         1.6. 页面渲染优化:
        异步加载脚本:使用 async 或 defer 属性加载 JavaScript,不阻塞页面渲染。
        优化关键渲染路径:通过合理布局和 CSS 优化,减少页面渲染时间。
        使用 CDN:将静态文件托管到内容分发网络(CDN),加速文件加载。

     2. 什么是懒加载?为什么它对性能优化有帮助?    

  1. 解释一些优化网页性能的方法。

    1.1. 图片优化:
    压缩图片:使用压缩工具减小图片文件大小,如 TinyPNG、ImageOptim 等。
    响应式图片:使用 srcset 和 sizes 属性提供不同分辨率的图片,根据设备分辨率加载不同大小的图片。

懒加载(Lazy Loading)是一种延迟加载资源的技术,通常用于网页和应用程序的优化。它指的是在需要时才加载特定资源,而不是在页面或应用初始化时就全部加载。

懒加载对性能优化有帮助的几个方面:

加快初始加载速度: 初始加载速度是用户体验的关键因素之一。通过延迟加载不必要的资源,页面或应用程序可以更快地加载并呈现内容。当用户首次访问页面时,只加载所需的内容,而不是等待所有资源完全加载。

节省带宽和资源: 懒加载可以减少页面初始加载时所需的带宽和资源需求。这对于移动设备和网络速度较慢的环境尤其有益。仅在需要时加载资源可以节省宝贵的带宽,并降低加载所需的数据量。

懒加载技术通常应用在图片、视频和其他多媒体内容上,典型的应用场景是在网页上,特别是对于长页面或内容丰富的页面。这些技术可以通过 JavaScript 或特定的库来实现,当用户滚动到特定部分或触发某些事件时,相应的资源才会被加载,从而提高整体用户体验和性能表现。
 

优化性能: 页面中存在大量图片、视频或其他大型资源时,懒加载可以显著提高性能。只有当用户滚动到内容区域时,相关资源才会被加载,从而减少页面初始加载时的压力,加快页面响应速度。

    3.什么是CDN(内容分发网络)?它如何提高网站性能?

     安全性:

  1. 什么是 XSS(跨站脚本攻击)?如何防止它?

      XSS(跨站脚本攻击)是一种网络安全漏洞,攻击者利用它在受害者的浏览器中注入恶意脚本。这些恶意脚本会执行在用户浏览器中,可用于窃取用户信息、劫持会话、修改网页内容或执行其他恶意行为。    

XSS 攻击通常分为三种类型:

  1. 存储型 XSS(Stored XSS): 攻击者将恶意脚本存储在服务器上,当用户访问包含这些恶意脚本的页面时,脚本会被执行。

  2. 反射型 XSS(Reflected XSS): 攻击者将恶意脚本作为参数附加到 URL 中或通过输入框等用户可控制的内容传递给服务器。服务器返回的内容中包含了恶意脚本,用户在访问这些 URL 时会执行脚本。

  3. DOM-based XSS: 这种类型的攻击不涉及服务器端,而是发生在浏览器中。恶意脚本利用了前端页面的漏洞,直接修改了页面的内容。

     防止 XSS 攻击的方法包括但不限于:

  1. 输入验证和过滤: 对于所有用户输入的数据(包括表单提交、URL 参数、Cookie 等),要进行严格的输入验证和过滤,防止恶意脚本的注入。

  2. 转义输出内容: 在向用户展示动态生成的内容时,特别是在 HTML、JavaScript、CSS 中,需要对输出内容进行适当的转义,确保浏览器不会将其解释为代码。

  3. 使用 HTTP 头部中的安全标志: 设置适当的安全头部,如 Content Security Policy(CSP)、X-Content-Type-Options、X-XSS-Protection 等,以限制浏览器行为,阻止潜在的攻击。

  1. CSRF(跨站请求伪造)是什么?如何预防?

       CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种网络安全攻击,它利用受害者当前已经登录的身份,在用户不知情的情况下伪造请求,以执行未经授权的操作。攻击者可以欺骗用户使其在其他网站上执行恶意请求,例如更改密码、发起支付等。攻击通常利用用户对特定站点的已验证身份进行操作。        

预防 CSRF 攻击的方法包括但不限于:

  1. 使用 CSRF Token: 在关键操作(比如修改密码、支付请求)的请求中,应该包含一个仅服务器知道并验证的令牌(CSRF Token)。攻击者难以伪造这个 token,因此即使攻击者能够发起伪造请求,但缺乏正确的 token,服务器会拒绝这个请求。

  2. 同源策略: 保持严格的同源策略,确保网站中的敏感操作仅限于特定域内。这意味着只有同一域名下的页面才能相互访问彼此的数据。

  3. 使用 Cookie 标记: 针对敏感操作的 Cookies,使用 SameSite 属性,限制第三方站点对其的访问。这可以防止 CSRF 攻击,因为浏览器会拒绝从另一个域发送的 Cookie。

  4. 双重认证: 对于特别敏感的操作,如金融交易,应该实施双因素认证,确保用户身份的双重验证,即使攻击者成功发起伪造请求,也需要额外的验证信息。

这些缓存策略结合使用可以有效减少对服务器资源的请求,加快页面加载速度,提高用户体验。然而,缓存的正确配置和管理对于确保网站或应用程序的正确运行和最新内容的呈现也至关重要。

 其他问题:

1.浏览器缓存机制是什么?常见的缓存策略有哪些?

浏览器缓存机制是一种用于提高网页性能和用户体验的技术,它通过存储先前请求的资源(如图片、脚本、样式表等)来减少页面加载时间。当用户再次访问相同页面或资源时,浏览器可以从缓存中直接加载资源,而无需重新从服务器下载。

常见的浏览器缓存策略包括:
强缓存(Expires 和 Cache-Control): 强缓存是通过设置 HTTP 响应头来实现的,通常包括 Expires 和 Cache-Control。Expires 是一个过时的响应头,它设置资源过期的日期,而 Cache-Control 则是更为现代和灵活的方式,其中 max-age 指定资源在多少秒内可以被缓存,no-cache 意味着浏览器缓存资源,但在使用之前需要验证其有效性。
协商缓存(Last-Modified 和 ETag): 协商缓存策略依赖于服务器验证缓存的有效性。服务器通过 Last-Modified 响应头提供资源的最后修改时间,浏览器下次请求资源时会通过 If-Modified-Since 头部将上次获取资源的时间发送给服务器,由服务器判断是否需要重新发送资源。另一种是使用 ETag(实体标签),它是资源的唯一标识,服务器可以根据 If-None-Match 请求头验证资源是否被修改过。
资源版本控制: 通过在资源 URL 中添加版本号或者 hash 码,每次资源变化时 URL 也会随之改变,这迫使浏览器获取新的资源。例如,styles.css?v=2script.js?hash=xxxx.
Service Worker 缓存: Service Worker 是运行在浏览器背后的脚本,它可以缓存请求的资源,并在脱机状态下提供内容。开发人员可以通过 Service Worker 控制缓存策略,包括缓存优先级和缓存的生命周期。
2. 解释浏览器的事件循环(Event Loop)。
浏览器的事件循环(Event Loop)是浏览器用于处理 JavaScript 代码执行和事件处理的机制。JavaScript 是单线程语言,它在浏览器中通过事件循环来处理异步和同步任务,以保持界面的响应性。
事件循环的基本概念如下:
主线程: JavaScript 在浏览器中通常在主线程上执行。这个线程负责处理 JavaScript 代码的执行、DOM 操作和事件处理。
任务队列(Task Queue): 事件循环中有多个任务队列。其中最重要的是任务队列(Task Queue),也称为消息队列(Message Queue)。异步任务产生的事件会被放入这个队列中等待执行。
微任务队列(Microtask Queue): 另一个重要的队列是微任务队列,用于存储微任务(例如 Promise 的回调、MutationObserver)。微任务会优先于宏任务执行。
宏任务和微任务: 在事件循环中,宿主环境(浏览器)产生的任务被称为宏任务(macrotask)。它们包括定时器回调、I/O 操作、事件处理等。微任务是相对较小的任务,它们会在宏任务执行完成后立即执行。


3.http协议

HTTP(HTTP协议是什么)是一种用于在Web浏览器和Web服务器之间交换数据的应用层协议。通过HTTP,Web浏览器可以向Web服务器发送请求并获取响应,从而实现Web页面的访问和传输。HTTP使用TCP作为传输层协议,并采用请求-响应模型来进行通信。在HTTP中,请求消息由请求行、请求头和请求正文组成,响应消息由状态行、响应头和响应正文组成。HTTP协议的设计旨在使其简单易用、可扩展性强,并具有高度的互操作性。


会继续更新~~~~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值