面试题JS篇

news2024/10/2 8:32:48

目录

  • Js 基本数据类型有哪些
  • Ajax 如何使用
  • 如何判断一个数据是 NaN?
  • Js 中 null 与 undefined 区别
  • 闭包是什么?有什么特性?对页面会有什么影响
  • JS中模块化的方法
  • Js 中常见的内存泄漏
  • 什么是事件冒泡?
  • 事件委托是什么?如何确定事件源?
  • 对比Cookie、localStorage、sessionStorage的异同
  • ES6 新特性
  • for...of 和 for...in的区别
  • Let 与 var 与 const 的区别
  • 数组方法有哪些请简述
  • Json 如何新增/删除键值对
  • 改变函数内部 this 指针的指向函数(bind,apply,call 的区别)
  • 手写bind
  • window和document的关系?
  • 什么是面向对象请简述
  • 普通函数和构造函数的区别
  • 请简述原型/原型链/(原型)继承
  • JS 的 new 操作符做了哪些事情?
  • 如何实现继承?/ 请写出一个简单的类与继承
  • Promise 的理解
  • 介绍一下async/await
  • 我们用 Promise 来解决什么问题?
  • 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
  • get 请求传参长度的误区
  • 说说前端中的事件流
  • clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop 的区别?
  • 鼠标的位置
  • JS 拖拽功能的实现
  • JS的垃圾回收机制
    • 有哪些垃圾回收策略?
  • DOM VS BOM
  • JS 监听对象属性的改变
  • JS 怎么控制一次加载一张图片,加载完后再加载下一张
  • 深拷贝VS浅拷贝?
  • 实现 JS 中所有对象的深度克隆(包装对象,Date 对象,正则对象)
  • 能来讲讲 JS 的语言特性
  • 为什么会造成跨域/请简述同源策略?
  • 请输出三种减少页面加载时间的方式
  • 什么是 jsonp 工作原理是什么?他为什么不是真正的 ajax
  • 请掌握 2 种以上数组去重的方式
  • 箭头函数与普通函数的区别
  • 常见的 HTTP状态码
  • 预加载和懒加载的使用场景
  • Js 的函数节流和函数防抖的区别
  • 介绍一下js中的计时器

参考资料

Js 基本数据类型有哪些

7:string,number,boolean,null,undefined,object,symbol(ES6引入)

Ajax 如何使用

ajax是什么?
实现在 不刷新整个页面的情况下更新部分内容 的技术
与axios的区别?
ajax是原生的(浏览器提供的 XMLHttpRequest 对象来实现),axios是对原生的一个包装

一个完整的 AJAX 请求包括以下五个步骤:

  1. 创建 XMLHttpRequest 对象,var xhr = new XMLHttpRequest();
  2. 设置请求参数 xhr.open(“GET”, “https://example.com/api/data”, true);
  3. 设置回调函数
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4 && xhr.status == 200) {
    // 处理服务器响应的逻辑
    console.log(xhr.responseText);
  }
};
  1. 发送请求 xhr.send();
  2. 更新页面

如何判断一个数据是 NaN?

  • isNaN()函数
  • 对字符串,这个函数会把字符串转为数字,产生误判
  • 解决:typeof value === ‘number’ && isNaN(value)

Js 中 null 与 undefined 区别

  • null:空值,主动赋值了一个空指针,可以用于清空对象引用;
  • und:声明了但没有赋值,表示缺少
  • Number(null) 的结果是 0。Number(undefined) 的结果是 NaN

闭包是什么?有什么特性?对页面会有什么影响

是什么
是一种语言特性:在一个函数作用于内部定义的函数,能够访问该作用域内的变量,当这个内部函数在其他地方被调用时,依然可以访问那个作用域的变量。
特点
1.函数嵌套函数。
2.函数内部可以引用外部的参数和变量。
3.内部函数所引用的参数和变量不会被垃圾回收机制回收。
优点
1.用于封装私有变量,隐藏细节,暴露接口
2.防止全局变量的滥用和污染
3.模块化,将一组相关的功能封装在一个单独的闭包中,使其具有独立的作用域,从而提高代码的可维护性和可重用性。
模块化示例
var module = (function() {
    var privateVariable = "I am private";

    return {
        publicMethod: function() {
            console.log(privateVariable);
        }
    };
})();

module.publicMethod(); // 输出:I am private

缺点:
内存泄露:程序中已经不再需要使用的内存没有被正确释放或回收,导致系统的可用内存减少,最终可能影响系统的性能

JS中模块化的方法

  1. COMMONJS(node.js上,是一种服务器端的模块化规范)使用的 requiremodule.exports
    // 在模块中导出
    var privateVariable = "I am private";
    module.exports = {
        publicMethod: function() {
            console.log(privateVariable);
        }
    };
    
    // 在另一个文件中导入
    var myModule = require('./myModule');
    myModule.publicMethod();
    
  2. ES6模块(浏览器上的模块化规范):importexport
    // 在模块中导出
    const privateVariable = "I am private";
    export const publicMethod = function() {
        console.log(privateVariable);
    };
    
    // 在另一个文件中导入
    import { publicMethod } from './myModule';
    publicMethod();
    

Js 中常见的内存泄漏

  1. 未及时清理定时器:定时器如果没有被清理,将一直保持对回调函数的引用
  2. 未解绑事件监听:如果在DOM元素被移除之前没有解绑事件监听,那么事件处理函数将一直存在于内存中,导致内存泄漏。使用 removeEventListener 解绑事件。
  3. 大对象未及时释放:在不再需要使用大型数据对象时,手动释放或将其设置为 null。
  4. 代码中无意间、不经意间创建的全局变量
  5. 闭包未释放
function createClosure() {
  var data = new Array(10000); // 大型数据结构

  return function() {
    // 使用 data
    console.log(data.length);
  };
}

var closure = createClosure(); // 创建闭包

// 在外部使用 closure,但未解除对 data 的引用
closure();

// 假设不再需要使用 closure,但没有解除对闭包的引用
// 这将导致 data 一直存在于内存中,无法被垃圾回收

什么是事件冒泡?

事件会从触发事件的元素一直冒泡到最外层的祖先元素。
例如父子都有点击事件,点儿子,先儿子输出,后父亲输出。

事件委托是什么?如何确定事件源?

  • 利用冒泡
  • 将事件监听器添加到父元素上,减少了对每个子元素都添加监听器的工作量。
  • 使用 event.target 确定事件源

对比Cookie、localStorage、sessionStorage的异同

都是用于在客户端存储数据的机制

特性CookielocalStoragesessionStorage
存储容量小(通常4KB)通常 5MB通常 5MB
生命周期可设置过期时间永久存储,除非手动清除页面关闭后自动清除
发送到服务器每次请求都会将 Cookie 数据包含在请求头中,如果 Cookie 中包含大量数据,会导致更多的带宽消耗不会自动发送不会自动发送
作用域可设置作用域全域名共享单个页面共享
安全性非常低,可能被劫持低,但相对较安全低,但相对较安全
主要用途跨页面通信、认证信息本地存储,长期数据本地存储,会话级别数据

ES6 新特性

  1. let 和 const 声明:

    • 引入了 letconst 关键字,用于声明块级作用域的变量和常量。
  2. 箭头函数(Arrow Functions):

    • 提供了更简洁的函数声明语法,同时修复了传统函数中 this 关键字的一些问题。
  3. 模板字符串(Template Strings):

    • 使用反引号 `` 包裹字符串,支持多行字符串和变量插值。
  4. 解构赋值(Destructuring Assignment):

    • 允许通过模式匹配从数组或对象中提取值并赋给变量。
  5. 对象字面量的扩展:

    • 支持简写方法声明、计算属性名、以及 Object.assign 方法用于对象的浅复制。
  6. 类(Classes):

    • 引入了类的概念,提供了更简洁、面向对象的语法。
  7. Promise 对象:

    • 提供了更方便的异步编程模式,解决了回调地狱的问题。
  8. 模块化(Modules):

    • 引入了对模块的官方支持,允许将代码分割为多个文件,使项目更易维护和组织。
  9. 迭代器和生成器(Iterators and Generators):

    • 提供了迭代器协议和生成器函数,使遍历数据结构更加灵活和可控。
  10. Symbol 数据类型:

    • 引入了一种新的基本数据类型 Symbol,用于创建唯一的标识符,通常用于对象属性的键名。const mySymbol = Symbol(‘key’); myObject[mySymbol] = ‘value’;
  11. Set 和 Map 数据结构:

    • 引入了 SetMap,分别用于存储唯一值和键值对,提供了更高效的数据存储和查找。
  12. 默认参数值:

    • 允许在函数声明时为参数提供默认值。
  13. Rest 和 Spread 操作符:

    • ... 用于表示剩余参数(Rest),或将数组或对象展开为参数序列(Spread)。

for…of 和 for…in的区别

  • for…in 循环用于遍历对象的可枚举属性,迭代的是对象的键名(属性名)
  • for…of 循环是用于遍历可迭代对象(如数组、字符串、Set、Map 等),迭代的是对象的,但是for-of不会遍历普通对象!普通对象默认不可迭代。

Let 与 var 与 const 的区别

特性varletconst
声明提升(声明提到作用域顶部)是(只提升声明 赋值undefined)是(但不可访问-死区)是(但不可访问-死区)
块级作用域不是
重复声明允许不允许不允许
初始值可选必须初始化
适用范围函数作用域块级作用域块级作用域
用途旧版 JavaScript 语法,全局作用域变量块级作用域变量,替代 var块级作用域常量
可变性可以重新赋值可以重新赋值不能重新赋值,常量
临时死区(声明之前不让用)

数组方法有哪些请简述

方法描述返回值
arr.push()从数组末尾添加元素,返回添加后数组的长度新数组的长度
arr.pop()从数组末尾删除元素,只能删除一个,返回被删除的元素被删除的元素
arr.shift()从数组头部删除元素,只能删除一个,返回被删除的元素被删除的元素
arr.unshift()从数组头部添加元素,返回添加后数组的长度新数组的长度
arr.splice(i, n)从索引 i 开始删除 n 个元素,返回被删除的元素数组被删除的元素数组
arr.concat()连接两个数组,返回连接后的新数组连接后的新数组
str.split()将字符串转化为数组转化后的数组
arr.sort()将数组进行排序,默认按照字符顺序排序,返回排序后的数组排序后的数组
arr.reverse()将数组反转,返回反转后的数组反转后的数组
arr.slice(start, end)切取索引值 start 到索引值 end 之间的数组,不包含 end切取的数组
arr.forEach(callback)遍历数组,无返回值,可以修改原数组
arr.map(callback)映射数组,返回一个新数组,根据回调函数的返回值生成新元素新数组
arr.filter(callback)过滤数组,返回满足条件的新数组满足条件的新数组

Json 如何新增/删除键值对

直接修改 JSON 字符串是不可行的,首先需要将其解析成 JavaScript 对象,然后对对象进行修改,最后将修改后的对象转换回 JSON 字符串。

JSON ->OBJ
JSON.parse()
OBJ->JSON
JSON.stringify()
  • 增:直接赋值新增键值对,myObject.newKey = ‘newValue’;
  • 删:使用 delete 操作符删除键值对 delete myObject.key1;

改变函数内部 this 指针的指向函数(bind,apply,call 的区别)

  • 都是用于改变this指向
  • bind不会立即执行而是返回一个新的函数
  • apply和call立即执行,apply第二个参数传入参数数组array,call的第二个参数直接传入参数
var obj = { value: 42 };

function getValue() {
    console.log(this.value);
}

var boundFunction = getValue.bind(obj,1, 2, 3);
boundFunction(); // 输出:42

getValue.apply(obj, args); 
getValue.call(obj, 1, 2, 3);

手写bind

学习视频

  1. 手写apply
    其实本质上就是把函数a放到person里面,执行一下然后删除掉
    function a(gender){
      console.log(`this ${gender}'s name is ${this.name}`);
      return {
        name:this.name,
        age:this.age
      }
    }
    const person = {
      name:'xiaoming',
      age:'15'
    }
    // a.apply(person,['boy'])
    Function.prototype.newApply = function(obj){
      let obj=obj||window
      let newArguments = [];
      for(let i=1;i<arguments.length;i++){
        newArguments.push(arguments[i])
      }
      //最重要的逻辑
      obj.tempFunc = this;
      let res=obj.tempFunc(...newArguments);
      delete obj.tempFunc;
      return res;
    }
    let b=a.newApply(person,['boy'])
    console.log(b)
    
  2. 手写bind
	function a(gender){
	  console.log(`this ${gender}'s name is ${this.name}`);
	  return {
	    name:this.name,
	    age:this.age
	  }
	}
	const person = {
	  name:'xiaoming',
	  age:'15'
	}
//   b=a.bind(person,'boy')
// b()
	Function.prototype.newBind=function(obj){
    console.log(this)
    let that=this //保存this,也就是调用这个newbind的函数
		//因为下面在函数里面用,this指向可能会发生变化
		let newArgus = [...(Array.from(arguments).slice(1))] 
		//注意这里arguments是个对象,要先转化为数组
    return function(){
      that.apply(obj,newArgus)//核心代码
    }
	}
a.newBind(person,'boy')()

优化后的:
在这里插入图片描述

window和document的关系?

document 表示当前加载的 HTML 文档。
它是 window 对象的属性之一,可以通过 window.document 或简写为 document 来访问

什么是面向对象请简述

  • 是一种编程思想
  • 把程序看做是对象相互作用的过程
  • 每个对象都有自己的属性和方法,并能够与其他对象交互
  • 类:是对象的模板,描述了对象的共同特征和行为。对象是类的实例,具体的实体
  • 三个特点:
  • 1.封装:把属性和方法封装在一个单元内,隐藏了对象的具体实现细节,只暴露必要的接口。提高代码的可维护性和可重用性。
  • 2.继承:允许 子继承父类的属性和方法,便于共享和扩充已有的代码。
  • 3.多态:不同对象的行为可以用相同的方法名调用,但是具体操作不同(其实是废话)

普通函数和构造函数的区别

  1. 普通函数就是用于执行某个特定的任务或操作
  2. 构造函数: 用于创建和初始化对象,它在对象被实例化时被调用,常通过new关键字调用,在JavaScript中,所有的函数实际上都可以被用作构造函数,包括普通函数。当你使用 new 关键字调用一个函数时,该函数就被视为构造函数,用于创建一个新的对象实例
  • 构造函数内部的 this 指向实例,普通函数内部的 this 指向调用函数的
    对象
  • 构造函数习惯上首字母大写

请简述原型/原型链/(原型)继承

  • 【先说明原型是什么】原型是对象的一个属性,指向一个父对象。每个对象都有这个属性,都继承父对象的属性和方法
  • 【再形象地解释一下】原型像一个说明书,假设你制造了很多相似的玩具,而它们都有一些共同的特征,比如都能发出声音。你可以把这个共同的特征写在一个“备用说明书”上,然后每个玩具都可以查阅这个说明书,知道怎么发声。原型是对象的属性和方法的集合
  • 【再关联到原型链】每个对象都有一个原型,而该原型又有自己的原型,依此类推,形成一条链。
  • 【补充说明一下作用】当你访问一个对象的属性或方法时,JavaScript 引擎会在原型链上查找,直到找到该属性或方法或者到达原型链的末尾
  • 【一个例外】在原型链的终点,Object.prototype 的原型是 null

操作方法

  • 实例对象本身是没有名为 prototype 的属性的,他们有的是__proto__
  • 只有函数有prototype属性,也可以通过Person.prototype=xxx来改变
  • 查看实例的原型:Object.getPrototypeOf(mydog)或者mydog.prototype
  • 设置实例的原型:Object.setPrototypeOf(myDog, newPrototype); 或者mydog.__proto__

请添加图片描述
构造函数的原型上的方法会被每一个实例方法继承!

function Person(){}
Person.prototype.name='gaga'
let p1=new Person()
let p2=new Person()
console.log(p1.name) //gaga
console.log(p2.name) //gaga

JS 的 new 操作符做了哪些事情?

  1. new 操作符新建了一个空对象,
  2. 这个对象原型(person.__proto__)指向构造函数的prototype(Person.prototype)
  3. 执行构造函数
  4. 返回这个对象

如何实现继承?/ 请写出一个简单的类与继承

暂时只记一种方法:ES6的class继承

class Animal{
	constructor(name){
		this.name=name;
	}
	sayName(){
		console.log('name is'+this.name)
	}
}
class Dog extends Animal {
	constructor(name.color){
		super(name);//在子类的构造函数中调用父类的构造函数,并传递 name 参数给父类的构造函数
		this.breed = breed;
	}
}

let mydog=new Dog('andy','black');
mydog.sayName();

Promise 的理解

  • Promise 是承诺的意思,承诺它过一段时间会给你一个结果
  • Promise 是一种解决异步编程的方案,相比回调函数和事件更合理和更强大
  • 从语法上讲,promise 是一个对象,从它可以获取异步操作的消息

三种状态

  1. pending等待
  2. fulfilled完成,调用Promise的then方法指定的回调函数,传递操作结果。
  3. rejected失败,调用Promise的catch方法指定的回调函数,传递失败原因。
    状态一旦改变,就不会再变

特点

  1. 外界无法直接影响Promise的状态,内部异步操作完成后通过resolve或reject改变状态
  2. 一旦Promise的状态从Pending转变为Fulfilled或Rejected,就不会再发生变化

缺点

无法取消: Promise一旦创建,就无法被取消。

用户输入关键词 “apple”。
开始搜索操作,但用户很快意识到输入错误,想要更改关键词为 “banana”。
由于无法取消前一个搜索操作,系统仍在尝试获取 “apple” 的搜索结果,而用户已经不再关心这个结果。

基本语法

const myPromise = new Promise((resolve,reject)=>{
	//一些操作
	if(成功){
		resolve(成功结果)
	}else{
		reject("失败原因")
	}
)

介绍一下async/await

  • async/await是JavaScript中用于处理异步代码的一种语法糖
  • 一个使用async关键字定义的函数会返回一个Promise对象。在函数体内,你可以使用await关键字等待一个Promise解决,并且在等待期间代码会暂停执行,直到Promise被解决(Fulfilled)或拒绝(Rejected)。
  • 使用try-catch块可以直接捕获异步操作中的错误,而不需要通过回调函数或Promise的catch方法。

我们用 Promise 来解决什么问题?

  1. 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  2. 多并发的异步操作: 使用Promise.all()可以并行执行多个异步操作,当所有操作完成时,Promise.all()返回一个包含所有结果的Promise。这种并发执行的方式可以提高异步操作的效率。
  • Promise.all() 接受一个包含多个Promise对象的数组,并返回一个新的Promise。这个新的Promise在传入的所有Promise对象都成功解决时才会成功解决
  • Promise.race() 同样接受一个包含多个Promise对象的可迭代对象,但与Promise.all()不同的是,它在传入的Promise对象中的任何一个解决或拒绝时就会立即解决或拒绝。
Promise.all([promise1, promise2, promise3])
  .then(values => {
    console.log(values); // [1, 2, 3]
  })
  .catch(error => {
    console.error(error);
  });

一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?

  1. dns请求。UDP,dns服务器从域名服务开始进行递归搜索,找到ip地址
  2. TCP三次握手
  3. 发送一个http请求,基于tcp
  4. 服务器监听80端口获取请求,发送响应数据。
  5. 浏览器解析
    深度遍历把HTML解析成DOM tree
    遇到script、link、style等会产生阻塞
    css解析渲染dom tree
    js解析

get 请求传参长度的误区

误区:我们经常说 get 请求参数的大小存在限制,而 post 请求的参数大小是无限制的。实际上 HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对 get 请求参数的限制是来源与浏览器或 web服务器,浏览器或 web 服务器限制了 url 的长度

说说前端中的事件流

  • 事件流表示 事件在页面中传播的顺序
  • DOM事件流有两个阶段:捕获阶段和冒泡阶段
捕获阶段
从根节点,沿着DOM树向目标元素传播
应用:在事件到达目标元素之前拦截事件,在用户点击某个区域之前执行一些验证操作
冒泡阶段
从目标元素开始,向根节点传播
应用:事件委托(又叫事件代理)

addEventListener() 方法的第三个参数来控制事件处理程序是在捕获阶段执行还是冒泡阶段执行。如果该参数为true,则事件在捕获阶段执行;如果为false或省略,则事件在冒泡阶段执行

clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop 的区别?

offsetHeight=内容高度+padding+border
偏移高度,即这个元素总共占了多少空间,但注意不包括margin,maigin不算是这个元素里面的,而是元素周围的空白
offset属性是相对于offsetParent元素偏移的数值,offsetParent是距离最近的一个被定位的父元素
在这里插入图片描述
在这里插入图片描述

clientHeight=内容高度+padding
请添加图片描述
scrollHeight=实际内容高度+padding


offsetTop=margin-top
clientTop=border-top
scrollTop提供内容已滚动的距离

鼠标的位置

  • offsetX,offsetY:相对于监听鼠标事件的元素而言的左/上距离
  • clientX/Y:相对于浏览器窗口
  • 原点都在左上角

JS 拖拽功能的实现

使用h5的drop API

<body>

<div class="draggable" draggable="true" id="draggableElement">拖我</div>
<div class="droppable" id="droppableElement">放在这里</div>

<script>
  var draggableElement = document.getElementById('draggableElement');
  var droppableElement = document.getElementById('droppableElement');

  // 开始拖动时触发
  draggableElement.addEventListener('dragstart', function(e) {
    e.dataTransfer.setData('text/plain', 'Hello, Drag!'); // 设置拖动数据
  });

  // 在拖动目标上移动时触发
  droppableElement.addEventListener('dragover', function(e) {
    e.preventDefault(); // 阻止默认行为,允许放置
  });

  // 放置目标上释放时触发
  droppableElement.addEventListener('drop', function(e) {
    e.preventDefault(); // 阻止默认行为,允许放置
    var data = e.dataTransfer.getData('text/plain'); // 获取拖动数据
    console.log('拖放的数据:', data);
  });
</script>

JS的垃圾回收机制

JavaScript 使用自动垃圾回收(Garbage Collection)机制来管理内存。这意味着开发者不需要手动释放不再使用的内存,而是由 JavaScript 引擎自动完成。这有助于避免内存泄漏和提高开发效率

垃圾回收的基本原理是找到不再被引用的对象,然后释放它们占用的内存。下面是 JavaScript 中垃圾回收的一些关键概念:

有哪些垃圾回收策略?

  • 引用计数
    该策略通过在对象上维护一个引用计数器来跟踪对象的引用次数。当引用计数变为零时,对象被认为是不再被引用的,即可被垃圾回收。
    缺点是无法解决循环引用的问题,因为循环引用的对象的引用计数永远不会变为零。
  • 标记清除(Mark and Sweep)
    从根对象(一般是全局对象)开始,标记所有可以从根对象访问到的对象。这通常涉及到遍历变量、函数作用域、闭包、以及全局作用域中的所有引用。
    在标记阶段完成后,所有未被标记的对象被认为是不再被引用的,即不可达的对象。这些对象将被清除(回收),释放其占用的内存。

DOM VS BOM

  • DOM 树是文档对象模型的表示,它反映了 HTML 或 XML 文档的层次结构,将文档的内容表示为一个树形结构。在 DOM
    树中,每个HTML 或 XML元素、属性、文本均被表示为一个节点(Node)。DOM树的根节点是文档节点(Document),它包含整个文档的内容。
  • BOM树表示浏览器窗口和浏览器提供的一些对象(非文档对象)。在 BOM 树中,常见的对象包括window、screen、location、history 等。window 对象是 BOM 树的根节点,它代表了整个浏览器窗口。

JS 监听对象属性的改变

方法1:使用Object.defineProperty修改对象属性的内部特性

// 创建一个对象
let myObj = {};

// 在对象上定义属性 'myProperty'
Object.defineProperty(myObj, 'myProperty', {
  get: function() {
    return this._myProperty;
  },
  set: function(value) {
    this._myProperty = value;
    console.log('myProperty 被修改了:', value);
  },
  enumerable: true,
  configurable: true
});

// 使用该属性
myObj.myProperty = 42; // 触发 set 方法,并输出 'myProperty 被修改了: 42'
console.log(myObj.myProperty); // 触发 get 方法,并输出 42

方法2:Proxy
//以后再学

JS 怎么控制一次加载一张图片,加载完后再加载下一张

使用promise

//图片的地址数组
const imgsArr = ['img1',....]
//下载图片的函数
function loadImg(src){
	return new Promise((resolve, reject)=>{
		const img = new Image();
		img.src = src;
		img.onload = ()=>resolve(img)//加载成功的回调函数
		img.onerror = reject; //加载失败的回调
	})
}

//按顺序下载图片
async function loadImgSeq(){
	for(const src of imgArr){
		const img = await loadImg(src);
		imageDIV.appendChild(img);
	}
}

深拷贝VS浅拷贝?

  • 浅拷贝只复制对象本身以及对象内的基本数据类型,而对于引用类型的元素仅复制引用而不复制实际数据 (直接newobj=oldObj即可)
  • 深拷贝会复制所有嵌套的引用类型比如对象和数组,新复制的对象上的任何更改都不会影响旧对象,两者完全独立。(需要通过一些库来实现)

实现 JS 中所有对象的深度克隆(包装对象,Date 对象,正则对象)

function deepClone(obj, visited = new WeakMap()) {
  // 检查是否为基本数据类型,如果是,则直接返回
  if (obj === null || typeof obj !== 'object' || obj instanceof Date || obj instanceof RegExp) {
    return obj;
  }

  // 检查是否已经访问过该对象,防止循环引用
  if (visited.has(obj)) {
    return visited.get(obj);
  }

  // 创建一个新的对象或数组
  let clone;
  if (obj instanceof Date) {
    clone = new Date(obj.getTime());
  } else if (obj instanceof RegExp) {
    clone = new RegExp(obj);
  } else if (Array.isArray(obj)) {
    clone = [];
  } else {
    clone = Object.create(Object.getPrototypeOf(obj));
  }

  // 记录已经访问过的对象,以便处理循环引用
  visited.set(obj, clone);

  // 递归地深度克隆对象的属性
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], visited);
    }
  }

  return clone;
}

代码解释:

  1. 基本数据类型: 基本数据类型(如字符串、数字、布尔值等)在 JavaScript 中是按值传递的,而不是按引用传递的。因此,对基本数据类型进行赋值或传递时,会直接复制其值而不涉及引用。所以,不需要对基本数据类型进行深拷贝。上述实现中的条件语句 if (obj === null || typeof obj !== 'object' || obj instanceof Date || obj instanceof RegExp) 就是用来检查是否为基本数据类型,如果是,则直接返回原始值。

  2. 循环引用: 循环引用指的是对象包含对自身的引用,形成一个循环链。在深度克隆时,如果不处理循环引用,可能导致无限递归,最终导致堆栈溢出或其他性能问题。使用 WeakMap 来记录已经访问过的对象,可以在发现循环引用时停止递归,避免陷入无限循环。

  3. obj.hasOwnProperty(key) 这是为了确保只复制对象自身的属性,而不包括从原型链继承的属性。在 JavaScript 中,对象的属性可以分为自身属性和继承属性。使用 hasOwnProperty 方法可以判断一个属性是否为对象自身的属性,而不是从原型链上继承的属性。这是为了避免复制不必要的属性,只复制对象自身的属性。

  4. WeakMap 是 JavaScript 中的一种集合类型,它允许将对象作为键存储在映射中。与 Map 不同,WeakMap 中的键只能是对象,而值可以是任意类型。一个 WeakMap 中的键是弱引用的,这意味着如果对象作为键不再被引用,它可以被垃圾回收器回收,从而释放相应的键值对。

能来讲讲 JS 的语言特性

JavaScript 是一门多范式的编程语言,主要用于在网页上实现交互性。它具有一些独特的语言特性,以下是其中一些主要的特点:

  1. 动态类型: JavaScript 是一门动态类型的语言,变量的数据类型可以在运行时动态改变。这意味着你可以在不事先声明变量类型的情况下直接赋值。
  2. 弱类型: JavaScript 是一门弱类型语言,也被称为动态弱类型语言。这意味着在进行一些操作时,JavaScript 会进行隐式类型转换,而不会引发错误。
  3. 原型继承: JavaScript 使用原型继承作为对象之间共享和复用代码的机制。每个对象都有一个原型对象,而对象可以继承原型对象的属性和方法。
  4. 跨平台性: JavaScript 不仅用于浏览器端,还可以通过 Node.js 在服务器端运行。这使得 JavaScript 成为一种具有广泛用途的通用编程语言。
  5. 脚本语言、解释性语言: 脚本语言的一个特点是可以逐行地执行,而不需要事先将整个程序编译成机器码。解释性语言,是因为它的代码在运行时由解释器逐行解释执行。

为什么会造成跨域/请简述同源策略?

同源策略(Same-Origin Policy)是浏览器的一种安全机制,用于防止恶意网站通过脚本等方式进行恶意请求。同源策略规定,两个页面只有在协议、主机和端口均相同的情况下,才属于同一个源(origin)。
举例一种如果没有同源策略,可能会发生的情况: 恶意网站读取了cookie后,可以使用用户的身份信息发起对银行网站的恶意请求

请输出三种减少页面加载时间的方式

  1. 压缩图片/视频音频质量
  2. 懒加载
  3. 异步加载,不阻塞:使用async或defer属性加载JavaScript文件,以确保它们不会阻止页面的渲染。
    <script defer src="example.js"></script>
  4. 使用 CDN 托管资源(使用 CDN Content Delivery Network,内容分发网络 将静态资源放在全球多个服务器上,CDN 会根据用户的地理位置自动选择距离用户最近的服务器提供资源)

什么是 jsonp 工作原理是什么?他为什么不是真正的 ajax

Jsonp 其实就是一个跨域解决方案。
Js 跨域请求数据是不可以的,但是 js 跨域请求 js 脚本是可以的。
jsonp 原理:(动态创建 script 标签,回调函数)
浏览器在 js 请求中,是允许通过 script 标签的 src 跨域请求,可以在请求的结果中添加回调方法名,在请求页面中定义方法,就可获取到跨域请求的数据。

// 客户端定义的回调函数
function handleResponse(data) {
  // 处理从服务器返回的数据
  console.log(data);
}

// 创建一个 <script> 元素
const script = document.createElement('script');

// 设置 <script> 元素的 src 属性,包含 JSONP 请求的 URL,并指定回调函数名
script.src = 'https://example.com/data?callback=handleResponse';

// 将 <script> 元素添加到页面中,触发跨域请求
document.body.appendChild(script);

他为什么不是真正的 ajax
ajax 的核心是通过XmlHttpRequest获取本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的 js 脚本。

请掌握 2 种以上数组去重的方式

  1. set
const uniqueArray = [...new Set(originalArray)];
  1. filter
const uniqueArray = originalArray.filter((value, index, self) => {
  return self.indexOf(value) === index;
});

箭头函数与普通函数的区别

箭头:没有原型属性,不能作为构造函数,不存在arguments,没有自己的this,this指向这个函数的上下文作用域;
普通函数:有arguments对象,可以作为构造函数,this指向调用这个函数的对象,没有被调用时,指向全局对象;

function example() {
  console.log(arguments.length); // 参数个数=0
}

常见的 HTTP状态码

  1. 信息性状态码(Informational Codes):

    • 100 Continue: 表示服务器已经接收到请求的部分,但仍然等待客户端发送剩余的请求。
  2. 成功状态码(Successful Codes):

    • 200 OK: 表示请求已成功。
    • 201 Created: 表示请求已经被成功处理,并且服务器创建了新的资源。
    • 204 No Content: 表示服务器成功处理了请求,但没有返回任何内容。
  3. 重定向状态码(Redirection Codes):

    • 301 Moved Permanently: 表示被请求的资源已经永久移动到新位置。
    • 302 Found (Moved Temporarily): 表示被请求的资源已经临时移动到新位置。
    • 304 Not Modified: 表示资源未被修改,可以使用缓存的版本。
  4. 客户端错误状态码(Client Error Codes):

    • 400 Bad Request: 表示客户端发送了一个服务器无法理解的请求。
    • 401 Unauthorized: 表示请求需要用户身份验证。
    • 403 Forbidden: 表示服务器理解请求,但拒绝执行。
    • 404 Not Found: 表示服务器无法找到请求的资源。
  5. 服务器错误状态码(Server Error Codes):

    • 500 Internal Server Error: 表示服务器遇到了一个未知的错误。
    • 501 Not Implemented: 表示服务器不支持当前请求所需要的某个功能。
    • 503 Service Unavailable: 表示服务器当前无法处理请求,通常是因为服务器过载或维护。

预加载和懒加载的使用场景

  • 预: 在用户打开商品详情页面前,提前加载商品的相关信息和评论,减少用户等待时间,提高用户体验。
  • 懒:在游戏开始时,只加载当前场景所需的资源,例如地图、角色和音效,以避免一开始就加载整个游戏的资源,提高游戏启动速度。

Js 的函数节流和函数防抖的区别

  • 用于控制函数调用频率的技术,用在一些高频触发的场景中
函数节流
函数节流是指在一定时间内,无论触发事件多少次,只执行一次函数。
场景:微博热搜,热搜词条可能以非常高的频率进行更新,可以使用防抖设置一分钟更新一次,而不是每次有新词条都要触发重新计算。
实现原理
function throttle(func, delay) {
    let lastTime = 0;
    return function() {
        const now = Date.now();
        if (now - lastTime >= delay) {
            func.apply(this, arguments);
            lastTime = now;
        }
    };
}
实际开发中使用的是lodash库中的throttle函数
import _ from 'lodash';
const newfunc = _.throttle(func, 500);
函数防抖
等待一定时间,如果在这个时间内没有新的触发事件,则执行函数;如果有新的触发事件,则重新计时。
场景:搜索,每次输入新的文字都会进行搜索内容,但是如果打字太快就会导致过多的搜索请求,就可以使用节流。
实现原理
function debounce(func, delay) {
    let timer;
    return function() {
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(this, arguments);
        }, delay);
    };
}
实际使用Lodash库中的debounce函数来实现函数防抖
const newfunc = _.debounce(func, 500);

介绍一下js中的计时器

// 在2秒后执行函数
const timeoutId = setTimeout(() => {
    console.log('Timeout executed!');
}, 2000);

// 取消定时执行
clearTimeout(timeoutId);
---------------------------------------------------------
// 每隔1秒执行一次函数
const intervalId = setInterval(() => {
    console.log('Interval executed!');
}, 1000);

// 取消重复执行
clearInterval(intervalId);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1472050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Arduino中安装ESP32网络抽风无法下载 暴力解决办法 python

不知道什么仙人设计的arduino连接网络部分&#xff0c;死活下不下来。&#xff08;真的沙口&#xff0c;第一次看到这么抽风的下载口&#xff09; 操作 给爷惹火了我踏马解析json选zip直接全部下下来 把这个大家的开发板管理地址下下来跟后面python放在同一目录下&#xff0c…

redis——客户端

Redis是一个典型一对多服务器程序&#xff0c;一个服务器可以与多个客户端进行网络连接&#xff0c;每隔客户端可以向服务器发送命令请求&#xff0c;而服务器则接收并处理客户端发送的命令请求&#xff0c;并向客户端返回命令请求。 通过是一个I/O多路复用技术实现的文件事件处…

09 Redis之分布式系统(数据分区算法 + 系统搭建与集群操作)

6 分布式系统 Redis 分布式系统&#xff0c;官方称为 Redis Cluster&#xff0c;Redis 集群&#xff0c;其是 Redis 3.0 开始推出的分布式解决方案。其可以很好地解决不同 Redis 节点存放不同数据&#xff0c;并将用户请求方便地路由到不同 Redis 的问题。 什么是分布式系统?…

blender bvh显示关节名称

导入bvh&#xff0c;菜单选择布局&#xff0c;右边出现属性窗口&#xff0c; 在下图红色框依次点击选中&#xff0c;就可以查看bvh关节名称了。

如何保证档案室符合建设标准要求

保证档案室符合建设标准要求需要考虑以下几个方面&#xff1a; 1. 总体规划&#xff1a;合理规划档案室的布局和大小&#xff0c;确保能够满足现有和未来的档案存储需求。考虑档案室的空间利用率、通风、照明、安全出口等因素。 2. 档案室环境&#xff1a;档案室的环境应具备稳…

SQL Server 开发环境配置教程(SSMS+SQL Prompt)

背景 记录一下 SQL Server 常用开发软件 体验了各种数据库IDE(DBeaver、Navicat、DataGrip)之后综合下来还是感觉 SSMSSQL Prompt 对于 SQL Server 最好用&#xff0c;所以在此记录一下配置过程 数据库可视化管理工具SSMS 官方下载地址&#xff1a; https://learn.microsoft…

【Java程序设计】【C00317】基于Springboot的智慧社区居家养老健康管理系统(有论文)

基于Springboot的智慧社区居家养老健康管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的智慧社区居家养老健康管理系统设计与实现&#xff0c;本系统有管理员、社区工作人员、医生以及家属四种角色权限 管…

01|Mysql底层存储引擎

1. 聚集索引&#xff08;聚簇&#xff09;与非聚集索引 1.1 聚集索引 索引和数据存储在一起。叶子节点存储了完整的数据记录&#xff1b; 1.2 非聚集索引 MyISAM存储引擎就是非聚集索引&#xff0c;索引和数据文件是分开存储的。索引在MYI文件中&#xff0c;数据在MYD文件中…

Zoho ToDo 满足您的需求:任务管理满足隐私和安全要求

任务管理工具已经成为我们日常生活中不可或缺的一部分&#xff0c;它们帮助我们处理各种事务&#xff0c;从杂项和愿望清单到管理截止日期和资源。这些工具不仅仅是简单的任务列表&#xff0c;它们掌握了项目的蓝图、雄心勃勃的目标和完成的最后期限。然而随着这些工具的使用越…

装配制造业的MES系统种的物料齐套技术

装配是制造企业涉及产品生产加工最为普遍的一种模式&#xff0c;包括汽车、电子、电器、电气等行业。经研究表明&#xff0c;装配在整个产品制造总成本中所占比例超过了50%&#xff0c;所占用的总生产时间比例在40%-60%&#xff0c;直接影响着产品质量和成本。装配制造非常强调…

Arduino单片机基础介绍

&#xff08;本文为简单介绍&#xff0c;内容源于网络和AI&#xff09; Arduino单片机&#xff0c;自2005年诞生以来&#xff0c;已经成为全球爱好者和专业工程师们快速实现创意原型的重要工具。Arduino的普及不仅因其强大的功能和简易的操作&#xff0c;还在于其背后强大的社…

websocket入门及应用

websocket When to use a HTTP call instead of a WebSocket (or HTTP 2.0) WebSocket 是基于TCP/IP协议&#xff0c;独立于HTTP协议的通信协议。WebSocket 是双向通讯&#xff0c;有状态&#xff0c;客户端一&#xff08;多&#xff09;个与服务端一&#xff08;多&#xff09…

7.(数据结构)堆

7.1 相关概念 堆&#xff08;Heap&#xff09;在计算机科学中是一种特殊的数据结构&#xff0c;它通常被实现为一个可以看作完全二叉树的数组对象。以下是一些关于堆的基本概念&#xff1a; 数据结构&#xff1a; 堆是一个优先队列的抽象数据类型实现&#xff0c;通过完全二叉树…

Oracle 基础表管理(Heap-Organized Table Management)

表是数据库中负责数据存储的对象&#xff0c;在RDBMS中&#xff0c;数据以行、列的形式存储在表中。Oracle中表有很多种类型&#xff0c;最基础且应用最常用的类型就是堆表&#xff08;Heap-Organized Table&#xff09;&#xff0c;本文列举了Oracle堆表的常用管理操作。 一、…

【PX4-AutoPilot教程-仿真环境架构】梳理PX4GazeboMAVLinkMAVROSROSROS2之间的关系

梳理PX4&Gazebo&MAVLink&MAVROS&ROS&ROS2之间的关系 PX4与仿真器PX4支持的仿真器PX4与除Gazebo之外的仿真器的连接PX4与Gazebo仿真器的连接 PX4默认的MAVLink UDP端口PX4 SITL软件在环仿真的架构Gazebo仿真PX4启动仿真的launch文件ROS与PX4的关系 PX4与仿真…

LeetCode 0938.二叉搜索树的范围和:深度优先搜索(可中序遍历)

【LetMeFly】938.二叉搜索树的范围和&#xff1a;深度优先搜索&#xff08;可中序遍历&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/range-sum-of-bst/ 给定二叉搜索树的根结点 root&#xff0c;返回值位于范围 [low, high] 之间的所有结点的值的和。…

《武汉市贯彻实施细则》解读房地产经纪活动时效性

时效性【当前有效】 为进一步规范我市房地产经纪活动&#xff0c;保护房地产交易和经纪活动当事人的合法权益&#xff0c;促进行业健康发展&#xff0c;制定《武汉市房地产经纪实施细则》&#xff08;以下简称《实施细则》&#xff09;。 &#xff08;以下简称《实施细则》&am…

《Docker 简易速速上手小册》第5章 Docker Compose 与服务编排(2024 最新版)

文章目录 5.1 理解 Docker Compose5.1.1 重点基础知识5.1.2 重点案例&#xff1a;部署 Flask 应用和 Redis5.1.3 拓展案例 1&#xff1a;多服务协作5.1.4 拓展案例 2&#xff1a;使用自定义网络 5.2 编排多容器应用5.2.1 重点基础知识5.2.2 重点案例&#xff1a;部署 Flask 应用…

OpenCV开发笔记(七十五):相机标定矫正中使用remap重映射进行畸变矫正

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/136293833 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 红胖子(红模仿…

用html编写的简易新闻页面

用html编写的简易新闻页面 相关代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document<…