前端面试题积累

本文介绍了JavaScript中的H5新特性,包括ES6中的变量声明(var、let、const),类型转换(强制和隐式),深度比较方法,以及Lodash库中的isEqual函数实现。同时,讨论了JavaScript的事件代理机制、闭包的概念和作用,以及DOM操作的相关方法。此外,还涵盖了数组和字符串的方法,如split和join,以及数组的slice和splice的区别。
摘要由CSDN通过智能技术生成

H5

CSS

JS

var 和 let const 的区别

var是ES5引入的变量声明方式,而let和const是ES6引入的变量声明方式,let和const相对于var更加安全,能够有效避免变量声明和赋值的一些潜在问题。

var 的变量可以在声明前使用(变量提升)

var声明的变量是函数级作用域或全局作用域,而使用let和const声明的变量是块级作用域,即在{}内部定义的变量只在该代码块内有效。

var、let声明的为变量可修改;const声明的是常量不能修改

typeof返回那些类型

  • “undefined”:表示变量未定义或值未被赋值。
  • “boolean”:表示布尔值。
  • “number”:表示数值类型,包括整数和浮点数。
  • “bigint”:表示大整数类型,这是ES2020新增的类型。
  • “string”:表示字符串类型。
  • “symbol”:表示符号类型,这是ES6新增的类型。
  • “object”:表示对象类型,包括数组、函数、对象和null(虽然null的类型实际上是object)。
  • “function” - 如果值是函数类型。

需要注意的是,typeof操作符对于一些特殊类型的值并不能准确地返回它们的类型,例如null会被认为是对象类型,而NaN会被认为是数值类型。另外,对于函数类型的值,typeof操作符会返回"function",而不是"object"。

列举强制类型转换和隐式类型转换

JavaScript中的强制类型转化:

  • Number() - 将值转换为数字类型。如果值是字符串,它将尝试将其转换为数字。如果无法转换,则返回 NaN。
  • String() - 将值转换为字符串类型。如果值是数字类型,它将将其转换为相应的字符串。
  • Boolean() - 将值转换为布尔类型。所有值都可以转换为布尔值,但具体的转换规则可能与预期不同。通常来说,以下值会转换为 false:0, NaN, null, undefined, “”(空字符串)。
  • parseInt() - 将字符串解析为整数类型。如果无法解析,则返回 NaN
  • parseFloat() - 将字符串解析为浮点数类型。如果无法解析,则返回 NaN。

JavaScript中的隐式类型转换:

  1. 字符串拼接
    当字符串与其他数据类型相加时,JavaScript会将其他数据类型隐式地转换为字符串类型,然后将它们拼接在一起。
var str = "hello" + 42;
console.log(str); // "hello42"
  1. 数字计算
    当数字与其他非数字数据类型相加时,JavaScript会将这些非数字类型隐式地转换为数字类型,然后进行计算。
var num = 5 + "2";
console.log(num); // "52"
  1. 布尔值判断
    在逻辑判断的情况下,JavaScript会将其他数据类型隐式地转换为布尔值类型。
if ("") {
  console.log("这行代码不会被执行");
}

if (0) {
  console.log("这行代码不会被执行");
}

if (null) {
  console.log("这行代码不会被执行");
}

if (undefined) {
  console.log("这行代码不会被执行");
}

if (NaN) {
  console.log("这行代码不会被执行");
}

在上面的例子中,空字符串、0、null、undefined和NaN都会被隐式转换为false。

  1. 对象转换
    当JavaScript需要将一个对象转换为字符串或数字时,它会调用对象的toString()或valueOf()方法进行转换。
var obj = {
  toString: function() {
    return "hello";
  }
};

var str = "object: " + obj;
console.log(str); // "object: hello"

在上面的例子中,对象obj被隐式地转换为字符串类型,JavaScript调用了它的toString()方法返回了字符串"hello"。

需要注意的是,隐式类型转换在某些情况下可能会导致不可预期的结果,因此在编写JavaScript代码时,应该尽可能避免依赖隐式类型转换,尽可能显式地进行类型转换。

手写深度比较,模拟Lodash isEqual

在JavaScript中进行对象比较时,使用"===" 或 "= ="运算符只能比较对象的引用地址,而不能比较对象的属性值,如果需要比较对象的属性值是否相等,就需要进行深度比较

深度比较是指比较两个对象的属性值是否完全相等,包括对象的嵌套属性。以下是一些常见的实现深度比较的方法:

  1. 递归比较对象属性值
    可以编写一个递归函数,遍历对象的属性,依次比较每个属性的值是否相等。
function deepEqual(obj1, obj2) {
  // 先比较两个对象的类型
  if (typeof obj1 !== typeof obj2) {
    return false;
  }

  // 如果是基本类型,则直接比较值
  if (typeof obj1 !== "object" || obj1 === null || obj2 === null) {
    return obj1 === obj2;
  }

  // 如果是数组,则递归比较每个元素
  if (Array.isArray(obj1) && Array.isArray(obj2)) {
    if (obj1.length !== obj2.length) {
      return false;
    }
    for (var i = 0; i < obj1.length; i++) {
      if (!deepEqual(obj1[i], obj2[i])) {
        return false;
      }
    }
    return true;
  }

  // 如果是对象,则递归比较每个属性
  var keys1 = Object.keys(obj1);
  var keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) {
    return false;
  }
  for (var i = 0; i < keys1.length; i++) {
    var key = keys1[i];
    if (!obj2.hasOwnProperty(key) || !deepEqual(obj1[key], obj2[key])) {
      return false;
    }
  }
  return true;
}

上面的函数可以比较基本类型、数组和对象类型的属性值是否相等。

  1. 使用第三方库
    也可以使用第三方库,例如Lodash或Underscore.js中的深度比较函数来进行比较。
var obj1 = {a: 1, b: [2, 3]};
var obj2 = {a: 1, b: [2, 3]};

_.isEqual(obj1, obj2); // true

Lodash中的isEqual()函数可以比较对象、数组、Date对象、RegExp对象等类型的属性值是否相等。

‘= =’和‘= = =’的不同

== 和 === 都是JavaScript中用来比较两个值的运算符,但是它们之间有一些重要的不同之处。

== 是相等运算符,它用来比较两个值是否相等,但不考虑它们的数据类型。如果两个值的类型不同,JavaScript会自动将它们转换为相同的类型,然后再进行比较。例如,1 == ‘1’ 会返回 true,因为JavaScript会将字符串’1’转换为数字1,然后比较两个数字是否相等。

=== 是严格相等运算符,它用来比较两个值是否相等,并且要求它们的类型也必须相同。如果两个值的类型不同,=== 运算符会直接返回 false。例如,1 === ‘1’ 会返回 false,因为数字和字符串的类型不同。

在实际开发中,建议尽可能使用 === 运算符,因为它可以避免由于类型转换引起的不必要的错误。当你需要比较两个值是否完全相等时,应该使用 === 运算符。只有在你确实需要比较两个值的值是否相等时,才应该使用 == 运算符。

JavaScript中常见的字符串方法

  • charAt():返回指定位置的字符。
  • charCodeAt():返回指定位置字符的Unicode编码。
  • concat():连接两个或多个字符串。
  • indexOf():返回指定字符在字符串中第一次出现的位置。
  • lastIndexOf():返回指定字符在字符串中最后一次出现的位置。
  • match():使用正则表达式匹配字符串,并返回匹配结果。
  • replace():替换字符串中的匹配子串。
  • search():返回指定字符在字符串中第一次出现的位置。
  • slice():提取字符串中的一部分。
  • split():将字符串按照指定的分隔符分割成一个数组。
  • substr():从指定位置开始提取字符串中指定长度的字符。
  • substring():提取字符串中两个指定位置之间的字符。
  • toLowerCase():将字符串转换为小写。
  • toUpperCase():将字符串转换为大写。
  • trim():去除字符串两端的空格。

手写字符串trim保证浏览器兼容性

if (!String.prototype.trim) {
  String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g, '');
  };
}

split()和 join()的区别

  • split()用于将一个字符串分割成数组
    split()方法将一个字符串按照指定的分隔符分割成一个数组。例如,如果有一个以逗号分隔的字符串,可以使用split(“,”)方法将其分割成一个数组。
var str = "apple,banana,orange";
var arr = str.split(",");
console.log(arr); // ["apple", "banana", "orange"]
  • join()将一个数组合并成一个字符串
    join()方法将一个数组的所有元素组合成一个字符串,并且可以指定一个字符串作为分隔符。例如,如果有一个数组,可以使用join(“,”)方法将其所有元素合并成一个以逗号分隔的字符串。
var arr = ["apple", "banana", "orange"];
var str = arr.join(",");
console.log(str); // "apple,banana,orange"

JavaScript中常见的数组的方法

  • concat():连接两个或多个数组。
  • push():向数组末尾添加一个或多个元素。
  • pop():删除并返回数组的最后一个元素。
  • shift():删除并返回数组的第一个元素。
  • unshift():向数组开头添加一个或多个元素。
  • slice():返回数组中指定的部分。
  • splice():向数组中添加或删除元素。
  • sort():对数组进行排序。
  • reverse():反转数组中的元素顺序。
  • join():将数组中的所有元素连接成一个字符串。
  • toString():将数组转换成一个字符串。
  • indexOf():返回指定元素在数组中第一次出现的位置。
  • lastIndexOf():返回指定元素在数组中最后一次出现的位置。
  • filter():过滤数组中的元素。
  • map():将数组中的每个元素通过函数处理后生成一个新的数组
  • reduce():对数组中的每个元素进行累积操作。

数组slice和splice区别

  • slice()方法
    slice()方法返回一个新的数组,包含从原始数组中指定起始索引和终止索引之间的元素。该方法不会修改原始数组,只是创建一个浅拷贝。slice()方法有两个参数,第一个参数指定起始索引,

第二个参数指定终止索引,但是终止索引不包含在结果数组中。

const arr = [1, 2, 3, 4, 5];
const newArr = arr.slice(1, 4);
console.log(newArr); // [2, 3, 4]
console.log(arr); // [1, 2, 3, 4, 5]
  • splice()方法
    splice()方法用于删除原始数组中的一部分元素,并在删除的位置添加新的元素。该方法会修改原始数组。splice()方法有三个参数,第一个参数指定起始索引,第二个参数指定要删除的元素个数,第三个参数以后的参数是要添加到数组中的元素。
const arr = [1, 2, 3, 4, 5];
const removed = arr.splice(1, 3, 'a', 'b', 'c');
console.log(arr); // [1, 'a', 'b', 'c', 5]
console.log(removed); // [2, 3, 4]

注意:splice()方法会改变原始数组,并返回被删除的元素。如果第二个参数为0,表示不删除任何元素,只是添加新的元素

总结:slice()方法用于创建一个新的数组,包含原始数组中指定范围内的元素。而splice()方法则是用于删除原始数组中的一部分元素,并在删除的位置添加新的元素。

数组map()

在JavaScript中,map()是数组原型对象上的一个方法,它可以对数组中的每个元素进行操作,并返回一个新的数组,新数组中的每个元素都是对原数组中相应元素应用操作后的结果

例如,假设我们有一个数字数组 [1, 2, 3, 4, 5],我们想要将每个元素都乘以2,可以使用map()方法来实现:

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

const newArray = originalArray.map((number) => {
  return number * 2;
});

console.log(newArray); // [2, 4, 6, 8, 10]

在上面的例子中,我们使用 map() 方法将原数组中的每个数字都乘以2,并将结果存储在一个新数组中。

除了上面的示例外,map()方法还可以用于执行其他任意类型的转换或操作,例如过滤、排序等。

[10,20,30].map(parseInt)输出结果是什么?

const res = [10,20,30].map(parseInt)
console.log(res) // [10,NaN,NaN]

// 拆解
[10,20,30].map((num,index)=>{
	return parseInt(num,index) 
	// parseInt(10,0) 10 
	// parseInt(20,1) NaN 
	// parseInt(30,2) NaN 
})

parseInt() 是一个全局函数,它将字符串解析为整数。它接受两个参数,第一个参数是要被解析的字符串,第二个参数是表示要解析的数字的进制数(可选参数)。

函数call、apply、bind的区别

call()、apply() 和 bind() 都是 JavaScript 函数的内置方法,用于控制函数的上下文和参数。

它们之间的区别如下:

  1. call() 和 apply() 方法都允许你在调用函数时显式地指定函数的上下文(即 this 值)。它们的主要区别在于传递参数的方式。call() 方法接受一组参数列表,而 apply() 方法接受一个数组作为参数。示例如下:
function greet() {
  console.log(`Hello, ${this.name}!`);
}

const person = { name: 'Alice' };

greet.call(person); // 输出 "Hello, Alice!"
greet.apply(person); // 输出 "Hello, Alice!"
  1. bind() 方法也允许你指定函数的上下文,但它不会立即调用函数。相反它会返回一个新的函数,这个新函数的上下文已经被绑定到指定的对象。你可以稍后调用这个新函数,并传递任何必要的参数。示例如下:
function greet(greeting) {
  console.log(`${greeting}, ${this.name}!`);
}

const person = { name: 'Alice' };
const greetPerson = greet.bind(person, 'Hello');

greetPerson(); // 输出 "Hello, Alice!"

在这个例子中,greet.bind(person, ‘Hello’) 返回一个新的函数 greetPerson,其中 this 值已经被绑定到 person 对象,而 “Hello” 被预先传递给了 greeting 参数。最后,我们调用 greetPerson(),这个函数输出了 “Hello, Alice!”。

  1. call() 和 apply() 方法可以用来动态调用函数,并以不同的上下文和参数运行它。bind() 方法主要用于创建一个新的函数,这个新函数的上下文和参数已经被预先设置好了。它通常用于将函数作为回调函数传递给其他函数或将函数赋值给变量。

事件代理(委托)是什么

在 JavaScript 中,事件代理(也称为事件委托)是一种在网页上处理事件的技术。事件代理利用事件冒泡的机制,将事件处理函数绑定在父元素上,以代替将其绑定在每个子元素上。当子元素触发事件时,事件会冒泡到父元素上,父元素就可以根据事件的具体信息(如事件类型、目标元素等)来决定如何处理这个事件。

使用事件代理的好处在于可以减少事件处理函数的数量,从而提高性能和简化代码。例如,如果有一个列表中的每个列表项都需要添加一个点击事件处理函数,使用事件代理就可以将点击事件处理函数附加到整个列表的父元素上,而不是将其附加到每个列表项上。这样,不管列表有多少个子元素,只需要一个事件处理函数就可以了。
代码示例:
HTML 代码:

<ul id="myList">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>

JavaScript 代码:

// 获取列表的父元素
const list = document.getElementById("myList");

// 附加点击事件处理函数到父元素
list.addEventListener("click", function(event) {
  // 确定点击的目标元素是列表项
  if (event.target.tagName === "LI") {
    // 执行相应的操作,例如将文本内容输出到控制台
    console.log("Clicked item:", event.target.textContent);
  }
});

在上面的代码中,我们首先获取了列表的父元素,并使用 addEventListener 方法将点击事件处理函数附加到父元素上。当用户单击列表项时,事件会冒泡到父元素上,父元素上的事件处理函数将执行。在事件处理函数中,我们使用 event.target 属性来确定实际点击的元素是列表项,并执行相应的操作。

闭包是什么?

当一个函数内部定义了另一个函数,并且内部函数使用了外部函数的变量时,就形成了一个闭包。这样,内部函数可以访问外部函数的变量,因为外部函数的作用域在内部函数中依然存在,即使外部函数已经执行完毕,内部函数依然可以访问到外部函数的变量。

以下是一个使用闭包实现计数器的示例代码:

function createCounter() {
  let count = 0;

  return function() {
    count++;
    console.log(count);
  };
}

const counter = createCounter();

counter(); // 输出 1
counter(); // 输出 2
counter(); // 输出 3

在上面的示例代码中,我们定义了一个名为 createCounter 的函数,该函数返回了一个内部函数,内部函数每次调用都会增加外部变量 count 的值并将其输出。由于 createCounter 函数返回的是一个函数,所以我们可以将其赋值给一个变量 counter 并调用该变量来使用内部函数。每次调用 counter 变量时,都会执行内部函数并输出递增的计数器值。

在这个例子中,内部函数使用了外部变量 count,即使 createCounter 函数已经执行完毕,内部函数依然可以访问到 count 变量,并且每次调用都会在原来的基础上进行计数。这就是闭包的典型应用场景。

闭包的优劣

优点:

  1. 允许在函数外部访问函数内部的变量,可以实现私有变量的效果;
  2. 可以延长变量的生命周期,当函数执行完毕后,闭包中的变量仍然可以被访问和使用;
  3. 可以减少全局变量的使用,避免命名冲突和变量污染;
  4. 可以实现柯里化和高阶函数等函数式编程的特性。

缺点:

  1. 闭包会占用更多的内存空间,因为它需要保存函数和环境;
  2. 可能会导致内存泄漏,如果闭包中的变量不再需要使用但是闭包没有被释放,会一直占用内存;
  3. 可能会导致不必要的性能开销,因为闭包中的变量需要额外的查找和访问。

闭包和普通函数的区别是什么?

在JavaScript中,闭包和普通函数的区别在于他们对于作用域的处理方式。普通函数执行完毕后,其中的局部变量通常会销毁,而闭包中的局部变量则会在内部函数执行完毕后依然保存在内存中,供下一次使用。

具体来说,闭包和普通函数的区别在于:

  1. 闭包可以访问外部函数的变量,而普通函数只能访问自己的局部变量和全局变量。
  2. 闭包中的变量在函数执行完毕后不会被销毁,而普通函数的局部变量则会被销毁。

因此,闭包可以实现一些高级的功能,如保存私有状态、实现函数记忆、实现柯里化、实现惰性计算等。同时,由于闭包中的变量不会被销毁,如果使用不当会造成内存泄漏的问题,需要注意内存管理。

需要注意的是,使用闭包时应该尽量避免出现多层嵌套的闭包,因为这样会增加代码的复杂度和内存消耗,也不利于代码的维护和调试。如果需要使用多层嵌套的闭包,可以考虑使用模块化的方式来组织代码。

函数声明和函数表达式的区别

函数声明 function fn(){}
函数表达式 const fn = function(){}
函数声明在代码执行前预加载,而函数表达式不会

如何使用JS实现继承

class继承
prototype继承

new Object()和Object.create()的区别

new Object() 和 Object.create() 都可以用来创建一个新的对象,但是它们的实现方式和用法略有不同。

new Object() 是通过 Object 构造函数创建一个新的对象。语法如下:

var obj = new Object();

new Object() 可以省略括号,语法为 var obj = new Object;。

在使用 new Object() 创建对象时,还可以向构造函数传递一个参数,这个参数会被作为新对象的初始值:

var obj = new Object({ name: "John", age: 30 });

Object.create() 是通过指定原型对象来创建一个新对象。语法如下:

var obj = Object.create(proto);

其中 proto 是一个对象,它将作为新对象的原型。通过 Object.create() 创建的对象会继承原型对象的属性和方法。

var person = {
  name: "John",
  age: 30,
  greet: function() {
    console.log("Hello, my name is " + this.name);
  }
};

var john = Object.create(person);
john.greet(); // 输出 "Hello, my name is John"

Object.create() 的第二个参数可以是一个对象,用于指定新对象的属性和方法。这个参数的格式与 Object.defineProperties() 的参数类似。
总的来说,new Object() 适用于创建简单的对象,而 Object.create() 则适用于创建复杂的对象,并且可以更好地利用原型链的特性。

关于this的使用场景

在 JavaScript 中,this 是一个特殊的关键字,用于引用当前执行上下文的对象。this 的值是在运行时确定的,取决于函数的调用方式。以下是一些常见的 this 的使用场景:

  1. 全局上下文:在全局上下文中,this 指向全局对象 window。
console.log(this); // 输出全局对象 window

函数上下文:在函数上下文中,this 的值取决于函数的调用方式。

  1. 作为函数调用:当函数作为普通函数调用时
  • this 指向全局对象 window。
function foo() {
  console.log(this);
}
foo(); // 输出全局对象 window
  • 作为方法调用:当函数作为对象的方法调用时,this 指向调用该方法的对象。
var obj = {
  name: "John",
  greet: function() {
    console.log("Hello, my name is " + this.name);
  }
};
obj.greet(); // 输出 "Hello, my name is John"
  • 作为构造函数调用:当函数作为构造函数调用时,this 指向新创建的对象。
function Person(name) {
  this.name = name;
  this.greet = function() {
    console.log("Hello, my name is " + this.name);
  }
}
var john = new Person("John");
john.greet(); // 输出 "Hello, my name is John"
  • 作为 call 或 apply 调用:使用 call 或 apply 方法可以改变函数执行时的 this 值。
var obj1 = { name: "John" };
var obj2 = { name: "Tom" };
function greet() {
  console.log("Hello, my name is " + this.name);
}
greet.call(obj1); // 输出 "Hello, my name is John"
greet.call(obj2); // 输出 "Hello, my name is Tom"
  • 作为箭头函数的上下文:箭头函数中的 this 值取决于外层的函数作用域,而不是调用方式。
var obj = {
  name: "John",
  greet: function() {
    setTimeout(() => {
      console.log("Hello, my name is " + this.name);
    }, 1000);
  }
};
obj.greet(); // 输出 "Hello, my name is John"(在 1 秒后)

总的来说,this 的使用场景很广泛,可以根据不同的情况来灵活使用。需要注意的是,在某些情况下,this 的值可能会出现意外的变化,因此需要仔细考虑代码的结构和调用方式。

如何阻止事件冒泡和默认行为

阻止事件冒泡和默认行为可以通过在事件处理函数中使用事件对象(Event Object)的方法来实现。具体来说,可以使用以下方法:

  1. 阻止事件冒泡:在事件处理函数中调用事件对象的 stopPropagation() 方法,可以阻止事件冒泡到父元素或祖先元素。
document.getElementById("inner").addEventListener("click", function(event) {
  console.log("inner clicked");
  event.stopPropagation();
});

document.getElementById("outer").addEventListener("click", function(event) {
  console.log("outer clicked");
});
  1. 阻止默认行为:在事件处理函数中调用事件对象的 preventDefault() 方法,可以阻止事件的默认行为,例如阻止链接的跳转、表单的提交等。
document.getElementById("link").addEventListener("click", function(event) {
  console.log("link clicked");
  event.preventDefault();
});

需要注意的是,事件对象的 stopPropagation() 和 preventDefault() 方法需要在事件处理函数的最开始调用,否则可能无法阻止事件的冒泡或默认行为。

操作DOM的常见方法

操作DOM(Document Object Model)是Web开发中的一个重要部分,通过操作DOM,可以实现动态更新和交互性的效果。以下是操作DOM的常见方法:

  1. 选择元素:可以使用 document.querySelector() 或 document.querySelectorAll() 方法选择元素。其中,document.querySelector() 方法返回匹配指定选择器的第一个元素,而 document.querySelectorAll() 方法返回匹配指定选择器的所有元素。
var element = document.querySelector("#myElement"); // 选择 id 为 myElement 的元素
var elements = document.querySelectorAll(".myClass"); // 选择所有 class 为 myClass 的元素
  1. 获取和设置元素属性:可以使用元素的属性(attribute)来获取或设置元素的属性值,或使用元素的属性方法(property)来获取或设置元素的属性值。
var element = document.getElementById("myElement"); // 获取 id 为 myElement 的元素
var value = element.getAttribute("data-value"); // 获取元素的 data-value 属性值
element.setAttribute("data-value", "new value"); // 设置元素的 data-value 属性值为 new value
var className = element.className; // 获取元素的 class 属性值
element.className = "newClass"; // 设置元素的 class 属性值为 newClass
  1. 创建和插入元素:可以使用 document.createElement() 方法创建元素,使用元素的方法将元素插入到文档中。
var newElement = document.createElement("div"); // 创建一个 div 元素
newElement.innerHTML = "new element"; // 设置新元素的内容
var parentElement = document.getElementById("parentElement"); // 获取父元素
parentElement.appendChild(newElement); // 将新元素插入到父元素中
  1. 移除元素:可以使用元素的removeChild()方法将元素从文档中移除。
var element = document.getElementById("myElement"); // 获取要移除的元素
var parentElement = element.parentNode; // 获取父元素
parentElement.removeChild(element); // 从父元素中移除该元素
  1. 监听事件:可以使用元素的 addEventListener() 方法监听事件。
var element = document.getElementById("myElement"); // 获取要监听事件的元素
element.addEventListener("click", function() {
  // 处理点击事件
});

以上是常见的操作DOM的方法,通过这些方法可以实现对元素的选择、属性的设置、元素的创建、插入和移除以及事件的监听等操作。

为什么要减少DOM操作?如何减少?

减少DOM操作是Web开发中的一个优化技巧。因为DOM操作是一种比较耗费资源的操作,每次对DOM进行操作都需要重新计算布局和渲染,这可能导致Web应用程序的性能下降。

具体来说,DOM操作会带来以下的问题:

  1. 重排和重绘:每次对DOM进行操作,都会导致浏览器重新计算布局和渲染,这可能会带来性能问题。
  2. 资源占用:DOM节点越多,占用的内存和CPU资源就越多。
  3. 渲染延迟:大量的DOM操作可能导致浏览器出现渲染延迟,影响用户体验。

因此,减少DOM操作可以提高Web应用程序的性能。以下是一些减少DOM操作的方法:

  1. 使用缓存:在对DOM进行操作之前,可以先缓存元素的引用,以避免重复查询DOM树。
  2. 批量操作:将多个DOM操作尽可能地合并到一起,以减少重排和重绘。
  3. 使用事件委托:使用事件委托可以将事件处理程序绑定到祖先元素上,以避免在每个子元素上绑定事件处理程序。
  4. 使用文档碎片:可以将要插入的DOM节点先添加到文档碎片中,最后再一次性将文档碎片插入到文档中,以减少重排和重绘。
  5. 避免样式操作:避免在JS中对元素样式进行频繁的操作,尽量使用CSS来控制元素的样式。

总之,减少DOM操作可以提高Web应用程序的性能,避免浪费资源,从而提供更好的用户体验。

document中load和ready的区别

load和ready都是页面加载事件,但它们之间有一些区别。

load事件在文档以及所有外部资源(如图像和样式表)完全加载后触发。这意味着,在load事件触发之前,页面中的所有元素和资源都已经完全加载,并且可以在JavaScript中访问。

window.addEventListener('load', function() {
  // 在页面和所有资源加载完全之后执行代码
});

相反,ready事件在文档结构已经准备好,但是外部资源如图像和样式表可能尚未加载完毕时触发。这意味着,在ready事件触发时,页面的DOM结构已经可以在JavaScript中访问和操作,但某些元素(例如图像)可能尚未完全加载。

document.addEventListener('DOMContentLoaded', function() {
  // 在文档结构准备好之后执行代码,但某些外部资源可能尚未加载完毕
});

由于ready事件触发时,页面中的某些元素可能尚未完全加载,因此它比load事件触发更快。如果您只需要访问页面的DOM结构,而不需要等待所有外部资源完全加载,那么使用ready事件可以更快地执行JavaScript代码。

ajax请求的原理

AJAX(Asynchronous JavaScript and XML)是一种利用JavaScript和XML进行异步通信的技术。它可以在不刷新整个页面的情况下,向服务器发起请求,并获取数据,然后通过JavaScript动态更新页面内容。下面是AJAX请求的基本原理:

  1. 创建XMLHttpRequest对象
    AJAX请求的第一步是创建一个XMLHttpRequest对象,它是浏览器提供的一个API,可以在JavaScript中发起HTTP请求。

  2. 发起HTTP请求
    使用XMLHttpRequest对象的open()方法设置请求方式(GET或POST)、请求地址和是否异步,然后使用send()方法发送请求。在发送请求前,可以通过setRequestHeader()方法设置HTTP请求头部信息,如Content-Type、Accept等。

  3. 接收HTTP响应
    一旦服务器接收到请求并处理完毕,就会返回HTTP响应。XMLHttpRequest对象会在接收到响应时触发readystatechange事件,可以通过onreadystatechange属性设置回调函数,获取响应的状态和数据。

  4. 处理响应数据
    在接收到HTTP响应后,可以使用XMLHttpRequest对象的responseText或responseXML属性获取响应数据。responseText返回的是字符串形式的响应数据,responseXML返回的是XML文档对象。然后根据响应数据的格式,使用JavaScript动态更新页面内容。

AJAX技术的优点是可以实现异步请求,不需要刷新整个页面,从而提高页面的响应速度和用户体验。同时,由于只需要请求需要的数据,减少了不必要的数据传输,节省了带宽。

ajax请求get和post的区别

  • 数据传输方式:GET 请求通过URL传输数据,而 POST 请求通过 HTTP 请求体传输数据。
  • 数据长度限制:GET 请求的数据长度有限制,通常为2048个字符,而 POST 请求没有限制。
  • 缓存处理:GET 请求可以被缓存,而 POST 请求不能被缓存,因为每次提交的数据都可能不同。
  • 安全性:由于 GET 请求将数据暴露在URL中,因此它的安全性比 POST请求低。POST 请求将数据放在请求体中,因此更加安全。

一般来说,GET 请求适用于只需要从服务器获取数据而不需要向服务器提交数据的情况,例如获取博客文章列表、获取用户信息等。而 POST 请求则适用于向服务器提交数据的情况,例如创建新的博客文章、提交表单数据等。

需要注意的是,GET 请求的数据传输方式不够安全,因此不适合传输敏感信息(如密码等)。此外,虽然 POST 请求可以传输大量的数据,但由于没有数据长度限制,如果发送的数据过大,可能会导致服务器拒绝请求或者请求超时。因此,我们应该根据实际情况选择使用 GET 或 POST 请求。

获取当前页面url参数

  • 使用 window.location.search 属性获取 URL 中的查询字符串,然后使用正则表达式或其他字符串操作方法进行解析,例如:
// 从 URL 中获取指定参数的值
function getParameterByName(name) {
  // 获取查询字符串部分,包括问号(?)和其后面的参数
  var url = window.location.search;
  // 对参数名进行转义,以便在正则表达式中使用
  name = name.replace(/[\[\]]/g, '\\$&');
  // 定义正则表达式,用于匹配参数名和参数值
  var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
  // 在查询字符串中搜索匹配的参数,并返回结果
  var results = regex.exec(url);
  if (!results) return null;
  if (!results[2]) return '';
  // 对参数值进行解码,并返回结果
  return decodeURIComponent(results[2].replace(/\+/g, ' '));
}

  • 使用 URLSearchParams API,该 API 可以将查询字符串解析为一个键值对集合,例如:
// 从 URL 中获取指定参数的值
function getParameterByName(name) {
  // 创建一个 URLSearchParams 对象,并将查询字符串作为输入
  var params = new URLSearchParams(window.location.search);
  // 调用 get() 方法获取指定参数的值
  return params.get(name);
}

  • 如果支持 HTML5 的浏览器,可以使用 window.history.state 属性获取 URL 中的参数,例如:
// 从 URL 中获取指定参数的值
function getParameterByName(name) {
  // 获取包含页面状态的对象
  var state = window.history.state;
  if (!state) return null;
  // 返回指定参数的值
  return state[name];
}

浏览器的同源策略是什么?

同源策略(Same-Origin Policy)是一种安全机制,旨在防止在不同源(域、协议或端口)的文档之间进行恶意行为。同源策略的基本原则是:文档从同一源加载的其他文档只能访问彼此的资源,而不能访问其他源的资源。

同源策略的实现是通过浏览器的限制,防止一个源的脚本或应用程序通过编程方式访问来自不同源的数据。具体来说,同源策略要求以下条件必须全部满足,两个URL才算是同源:

  1. 协议相同
  2. 域名相同
  3. 端口相同

如果两个URL在这三个方面有任何一个不同,就被视为不同源。同源策略限制了脚本只能访问与其来源相同的文档对象模型(DOM),不能访问不同源的DOM。这是浏览器安全的基本保障,它可以防止来自其他网站的脚本偷窥或篡改当前网页的信息。

同源策略有一些例外情况,比如允许在页面中使用标签(如img、link、script等)来加载来自其他源的资源,或者允许通过CORS(跨域资源共享)来实现不同源之间的数据交换。

JS实现跨域请求的方法有哪些?

在Web开发中,跨域请求是指从一个域名的网页去请求另一个域名的资源,由于浏览器的同源策略限制,直接发起跨域请求是不被允许的。以下是JS实现跨域请求的几种方法:

  1. JSONP
    JSONP(JSON with Padding)是一种非官方的跨域请求,它利用了script 标签可以跨域请求的特性。JSONP的原理是在客户端动态创建一个script标签,src属性指向一个跨域地址,服务器放回一段JavaScript代码,并在其中调用一个回调函数,将数据作为参数传递给回调函数。由于script标签可以跨域请求,因此可以绕过浏览器的同源策略限制。JSONP的缺点是只支持GET请求,且只能返回JSON格式数据。
  2. CORS
    CORS(Cross-Origin Resource Sharing)是一种官方的跨域请求方式,它需要服务器端支持。CORS通过在HTTP头部添加特定的字段,告诉浏览器是否允许跨域请求。对于支持CORS的服务器,可以在响应头部添加Access-Control-Allow-Origin字段,指定允许的域名;对于复杂请求(如请求方法为PUT、DELETE等),还需要在响应头部添加其他字段。客户端在发起请求时,会先发送一个OPTIONS请求,称为预检请求,服务器返回的响应头部中会包含允许的请求头部、请求方法等信息,客户端根据这些信息判断是否允许跨域请求。
  3. postMassage
    postMessage是HTML5提供的一种跨窗口通信的机制,它可以在不同窗口之间传递消息,包括不同域名、协议和端口。客户端可以在一个页面中创建一个iframe或者打开一个新窗口,将需要跨域请求的数据作为消息发送给目标窗口,目标窗口接收到消息后执行相应的操作,然后将执行结果返回给客户端。由于postMessage是异步的,因此需要使用回调函数或者事件监听来处理返回结果。
  4. 代理服务器
    代理服务器是一种常见的解决跨域请求的方式,它可以在服务器端发起跨域请求,然后将结果返回给客户端。客户端只需要请求代理服务器的接口,由代理服务器转发请求,并将响应结果返回给客户端。这种方式的缺点是需要额外的服务器开销,并且可能会影响性能。

jsonp的原理是什么?

JSONP的原理是通过动态创建一个script标签来请求跨域的数据,而不是通过XMLHttpRequest对象来请求。这是因为XMLHttpRequest对象在跨域请求数据时会受限制,而script标签则没有这个限制。

具体来说,JSONP的实现过程如下:

  1. 前端页面通过一个JavaScript函数来请求跨域的数据,同时将一个回调函数作为参数传递给服务器。回调函数的名称可以是任意的,一般由前端页面指定。
  2. 服务器接收到请求后,将需要返回的JSON数据包装在回调函数中,并将整个字符串作为响应发送给前端页面。响应的Content-Type通常是text/javascript。
  3. 前端页面接收到响应后,浏览器会自动执行回调函数,并将JSON数据作为参数传递给它。这样,前端页面就可以使用这些数据来更新页面或执行其他操作。

需要注意的是,服务器返回的数据必须是合法的JavaScript代码,因为它将直接被浏览器执行。如果返回的数据不是合法的JavaScript代码,会导致解析错误,从而无法正确执行回调函数。

总的来说,JSONP的原理就是利用动态创建script标签的方式来绕过浏览器的同源策略,从而实现跨域请求数据。

如何捕获js中的异常

在 JavaScript 中,可以使用 try…catch 语句来捕获异常。try 代码块中包含可能会抛出异常的代码,catch 代码块则用于处理异常情况。以下是一个简单的示例:

try {
  // 可能会抛出异常的代码
  var result = 1 / 0;
} catch (e) {
  // 处理异常情况
  console.log('Error: ' + e.message);
}

在上面的示例中,try 代码块中的除法操作可能会抛出一个异常,如果抛出异常,则会跳转到 catch 代码块中,并将异常对象作为参数传递给 catch 代码块。在 catch 代码块中,我们可以使用异常对象的 message 属性来获取异常信息。

除了 try…catch 语句外,JavaScript 还提供了 window.onerror 方法来全局捕获异常。当页面上的代码抛出异常时,window.onerror 方法会被调用,并传递异常信息、异常发生的文件和行号等信息。以下是一个简单的示例:

// 自动捕获异常
window.onerror = function(message, file, line) {
  console.log('Error: ' + message + ' at ' + file + ':' + line);
  // 第一对跨域的js,如CDN的,不会有详细报错
  // 第二对于压缩的js,还要配合sourceMap反查到未压缩代码的行、列
};

需要注意的是,window.onerror 方法只能捕获一部分异常,例如语法错误和一些运行时错误。对于异步代码和一些其他类型的异常,可能需要使用其他方式进行处理。

什么是JSON?

  • json是一种数据格式,本质是一段字符串
 {
  "name": "John",
  "age": 30,
  "city": "New York",
  "hobbies": ["reading", "traveling", "music"]
}
  • json格式和JS对象结构一致,对JS语言更友好
  • window.JSON是全局对象:JSON.stringify JSON.parse

Vue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值