JS相关知识

张开发
2026/4/7 16:23:19 15 分钟阅读

分享文章

JS相关知识
前言这里开始陆续介绍JS的相关学习笔记一、基础部分1.字符串基本方法字符串方法的核心特点不会修改原字符串所有操作都会返回一个新字符串这是因为字符串在 JS 中是「不可变值」。基础操作/查找类方法作用示例length属性获取字符串长度hello.length // 5charAt(index)获取指定索引的字符hello.charAt(1) // eindexOf(str, start)查找子串首次出现的索引找不到返回 -1hello world.indexOf(o) // 4includes(str)判断是否包含指定子串返回布尔值hello.includes(ll) // truestartsWith(str)/endsWith(str)判断是否以指定子串开头 / 结尾hello.startsWith(he) // true转换/截取类方法作用示例toUpperCase()/toLowerCase()转大写 / 小写Hello.toUpperCase() // HELLOslice(start, end)截取字符串start 开始end 结束但不包含hello.slice(1,3) // elsubstring(start, end)类似 slice但 start/end 为负时会自动转为 0hello.substring(1,3) // elsplit(分隔符)把字符串拆成数组a,b,c.split(,) // [a,b,c]trim()去除首尾空格 / 换行符 hello .trim() // hello拼接/替换类方法作用示例concat(str1, str2...)拼接字符串不如常用a.concat(b,c) // abcreplace(原字符串, 新字符串)替换首个匹配的子串hello.replace(l, x) // hexloreplaceAll(原字符串, 新字符串)替换所有匹配的子串hello.replaceAll(l, x) // hexxo2.数组Array内置方法数组方法分为两类会修改原数组、不会修改原数组这是核心区别一定要记清。修改原数组的方法方法作用示例push(元素)末尾添加元素返回新长度let arr [1,2]; arr.push(3); // arr 变为 [1,2,3]pop()末尾删除一个元素返回删除的元素let arr [1,2,3]; arr.pop(); // arr 变为 [1,2]unshift(元素)开头添加元素返回新长度let arr [1,2]; arr.unshift(0); // arr 变为 [0,1,2]shift()开头删除一个元素返回删除的元素let arr [0,1,2]; arr.shift(); // arr 变为 [1,2]splice(start, 删除数量, 新增元素...)增 / 删 / 改数组let arr [1,2,3]; arr.splice(1,1,4); // arr 变为 [1,4,3]reverse()反转数组let arr [1,2,3]; arr.reverse(); // [3,2,1]sort()排序默认按字符串排序需传回调let arr [3,1,2]; arr.sort((a,b)a-b); // [1,2,3]不修改原数组的方法方法作用示例length属性获取数组长度[1,2,3].length // 3slice(start, end)截取数组同字符串 slice[1,2,3,4].slice(1,3) // [2,3]concat(数组/元素)拼接数组返回新数组[1,2].concat([3,4]) // [1,2,3,4]join(分隔符)把数组转成字符串split 的逆操作[1,2,3].join(,) // 1,2,3indexOf(元素)查找元素首次出现的索引找不到返回 -1[1,2,3].indexOf(2) // 1includes(元素)判断是否包含指定元素返回布尔值[1,2,3].includes(4) // false迭代类方法这类方法接收「回调函数」作为参数遍历数组并处理是前端开发的高频用法方法作用示例forEach((item, index){})遍历数组无返回值[1,2].forEach(item console.log(item)) // 打印 1、2map((item){})遍历数组并返回新数组映射[1,2].map(item item*2) // [2,4]filter((item){})过滤数组返回符合条件的新数组[1,2,3].filter(item item1) // [2,3]find((item){})查找第一个符合条件的元素找不到返回 undefined[1,2,3].find(item item1) // 2reduce((sum, item)sumitem, 初始值)累加 / 聚合数组返回最终结果[1,2,3].reduce((s,i)si, 0) // 63.JS实现继承的方法下面介绍下面试中可能遇到的常见的6种继承的方式实现的思路很简单理解后很容易掌握。原型链继承子类构造函数无法向父类传参所有的子类实例共享着一个原型对象一旦原型对象的属性发生变化所有的子类实例都会受到影响function Father(){ this.name father } Father.prototype.sayHello function(){ console.log(Hello from father); } function Child(age){ this.age age } Child.prototype new Father() Child.prototype.constructor Child Child.prototype.showAge function(){ console.log(this.age) } const child new Child() console.log(child.name); child.sayHello()构造函数继承用 .call() 和 .apply()方法在子类构造函数中调用父类构造函数只继承了父类构造函数的属性没有继承父类构造函数原型的属性function Father(name){ this.name name } Father.prototype.sayName function(){ console.log(this.name); } function Son(name,age){ Father.call(this,name) this.age age } const son new Son(xiaohua,20) console.log(son.name); //xiaohua console.log(son.sayName); //undefined组合继承原型链继承构造函数继承调用两次父类构造函数造成资源的浪费function Father(name){ this.name name } Father.prototype.sayHello function(){ console.log(Hello from father); } function Son(name,age){ Father.call(this,name) this.age age } Son.prototype new Father() Son.prototype.constructor Son const son new Son(xiaohua,20) console.log(son.name,son.age); son.sayHello()原型式继承直接用Object.create()创建一个新的对象继承原有对象const father { name:father, sayHello(){ console.log(Hello); } } const child Object.create(father) child.age 18 console.log(child.name,child.age); child.sayHello()寄生组合式继承继承属性和方法只调用一次Fatherfunction Father(name){ this.name name } Father.prototype.sayHello function(){ console.log(Hello from father); } function Son(name,age){ Father.call(this,name) this.age age } Son.prototype Object.create(Father.prototype) Son.prototype.constructor Son const son new Son(xiaohua,20) console.log(son.name,son.age); son.sayHello()ES6的Class继承class Father{ constructor(name){ this.name name } sayHello(){ console.log(Hello from father); } } class Son extends Father{ constructor(name,age){ super(name) this.age age } } const son new Son(xiaohua,23) console.log(son.name,son.age); son.sayHello()4.扩展运算符和剩余参数剩余参数...语法的核心作用是将函数接收到的多个独立参数打包成一个数组。注意区分剩余参数...接收参数≠ 扩展运算符...展开数组 / 对象前者是 “打包”后者是 “解包”。// 定义函数时用 ...参数名 接收剩余参数必须是最后一个参数 function sum(...numbers) { // numbers 是一个数组包含所有传入的参数 return numbers.reduce((total, num) total num, 0); } // 调用时可传入任意数量的参数 console.log(sum(1, 2)); // 3numbers [1,2] console.log(sum(1, 2, 3, 4)); // 10numbers [1,2,3,4] console.log(sum()); // 0numbers []扩展运算符...的核心作用是将可迭代对象数组、字符串、Set、Map或对象展开为独立的元素 / 键值对解决了传统拼接、传参繁琐的问题让代码更简洁。适用范围数组、对象ES2018、字符串、类数组对象如arguments、可迭代对象Set/Map。关键区分...出现在函数参数接收端是剩余参数打包出现在值的使用端是扩展运算符解包const original [1, 2, [3, 4]]; const copy [...original]; // 浅拷贝 copy[0] 100; // 不影响原数组 copy[2][0] 300; // 浅拷贝嵌套数组仍共用引用 console.log(original); // [1,2,[300,4]] console.log(copy); // [100,2,[300,4]]5.this对象的理解this 是执行上下文中的一个属性它指向最后一次调用这个方法的对象。在实际开发中this 的指向可以通过四种调用模式来判断。第一种是函数调用模式当一个函数不是一个对象的属性时直接作为函数来调用时this 指向全局对象。第二种是方法调用模式如果一个函数作为一个对象的方法来调用时this 指向这个对象。第三种是构造器调用模式如果一个函数用 new 调用时函数执行前会新创建一个对象this 指向这个新创建的对象。第四种是apply 、 call 和 bind 调用模式这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数一个是 this 绑定的对象一个是参数数组。call 方法接收的参数第一个是 this 绑定的对象后面的其余参数是传入函数执行的参数。也就是说在使用 call() 方法时传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变其他情况下都不会改变。这四种方式使用构造器调用模式的优先级最高然后是 apply、call 和 bind 调用模式然后是方法调用模式然后是函数调用模式6.Promise介绍Promise 对象是异步编程的一种解决方案。Promise 是一个构造函数接收一个函数作为参数返回一个 Promise 实例。一个 Promise 实例有三种状态分别是pending、fulfilled 和 rejected。实例的状态只能由 pending 转变 fulfilled 或者 rejected 状态并且状态一经改变无法再被改变了。状态的改变是通过传入的 resolve() 和 reject() 函数来实现的当我们调用resolve回调函数时会执行Promise对象的then方法传入的第一个回调函数当我们调用reject回调函数时会执行Promise对象的then方法传入的第二个回调函数或者catch方法传入的回调函数。7.事件循环事件循环又叫做事件队列两者是一个概念。事件循环指的是js代码所在运行环境浏览器、nodejs编译器的一种解析执行规则。事件循环不属于js代码本身的范畴而是属于js编译器的范畴在js中讨论事件循环是没有意义的。换句话说js代码可以理解为是一个人在公司中具体做的事情 而 事件循环 相当于是公司的一种规章制度。 两者不是一个层面的概念。下面介绍下事件循环的执行机制进入到script标签,就进入到了第一次事件循环.2.遇到同步代码立即执行3.遇到宏任务,放入到宏任务队列里.4.遇到微任务,放入到微任务队列里.5.执行完所有同步代码6.执行微任务代码7.微任务代码执行完毕本次队列清空8.寻找下一个宏任务重复步骤18.宏任务和微任务微任务一个需要异步执行的函数执行时机在主函数执行结束之后当前宏任务结束之前。常见的微任务Promise.thenMutationObserverprocess.nextTick(Node.js中)常见的宏任务script(可以理解为外层同步代码setTimeout/setIntervalUI事件postMessagesetImmediate/,I/O(Node.js中)这里再补充一道题与事件循环机制相结合便于理解宏任务和微任务的执行顺序console.log(1); setTimeout(() { console.log(2); }, 1000); console.log(3); new Promise((resolve) { resolve(); }).then(() { console.log(4); }).then(() { console.log(5); }); console.log(6); // 执行结果 // 1 // 3 // 6 // 4 // 5 // 29.什么是JS的异步编程js的异步编程是指在不阻塞主线程的情况下执行耗时操作比如网络请求、文件读写或定时任务等。js本质上是单线程的这意味着它一次只能执行一个任务。为了确保用户界面的响应性需要采用异步编程的模式来处理那些需要较长时间才能完成的任务。回调函数回调函数是最原始的异步编程方式之一通常在一个异步操作完成后调用。 例如在一个异步函数中注册一个回调函数来处理数据加载完成后的逻辑。 缺点是当多个异步操作嵌套时会导致代码难以阅读和维护通常称为“回调地狱”。事件监听器事件监听器用于处理用户交互或其他事件。 当某个事件发生时指定的回调函数会被调用。 例如监听按钮点击事件并在点击时执行某些操作。PromisesPromises 是 JavaScript 中处理异步操作的一种更现代的方式。 Promise 对象代表一个尚未完成但最终会产生结果的操作。 它有三种状态pending待定、fulfilled已成功和rejected已失败。 使用 .then() 和 .catch() 方法来处理成功和失败的情况。Async/AwaitAsync/Await 是基于 Promises 的语法糖使异步代码看起来更像同步代码。 async 关键字用于定义一个异步函数await 关键字用于等待一个 Promise 结果。 异步函数总是返回一个 Promise。 使用 async/await 可以让异步代码更加简洁易读。二、进阶部分三、常见常考手写题1.new操作符原理创建一个新对象将该对象指向构造函数的原型对象执行构造函数this绑定到这个新对象的上面如果构造函数返回一个对象类型则返回它否则返回新创建的对象function myNew(fn,...args){ if(Object.prototype.toString.call(fn)! [object Function]){ return Error in params } let obj {} obj Object.create(fn.prototype) let ret fn.call(obj,...args) return ret instanceof Object ? ret : obj }2.instanceof检查对象的原型链中是否存在构造函数的原型对象function myInstanceof(left,right){ if(typeof right ! function){ throw new TypeError(right must be a function) } if(left null){ return false } const leftType typeof left if(leftType ! object leftType ! function){ return false } // 当前检查的实例对象 let proto Object.getPrototypeOf(left) // 构造函数的原型对象 let protptype right.prototype while(true) { if(proto protptype) return true proto Object.getPrototypeOf(proto) } return false }3.数组转树递归的方式const list [ { id: 01, name: 张大大, pid: , job: 项目经理 }, { id: 02, name: 小亮, pid: 01, job: 产品leader }, { id: 03, name: 小美, pid: 01, job: UIleader }, { id: 04, name: 老马, pid: 01, job: 技术leader }, { id: 05, name: 老王, pid: 01, job: 测试leader }, { id: 06, name: 老李, pid: 01, job: 运维leader }, { id: 07, name: 小丽, pid: 02, job: 产品经理 }, { id: 08, name: 大光, pid: 02, job: 产品经理 }, { id: 09, name: 小高, pid: 03, job: UI设计师 }, { id: 10, name: 小刘, pid: 04, job: 前端工程师 }, { id: 11, name: 小华, pid: 04, job: 后端工程师 }, { id: 12, name: 小李, pid: 04, job: 后端工程师 }, { id: 13, name: 小赵, pid: 05, job: 测试工程师 }, { id: 14, name: 小强, pid: 05, job: 测试工程师 }, { id: 15, name: 小涛, pid: 06, job: 运维工程师 }, ]; //递归 function toTree(list,parId){ let len list.length function loop(parId){ let res [] for(let i 0; i len; i){ let item list[i] if(item.pid parId){ item.children loop(item.id) res.push(item) } } return res } return loop(parId) }哈希的方式//hash function toTree(list) { const res [] const map {} for (const item of list) { map[item.id] { ...item, children: [] } } for (const item of list) { const node map[item.id] if (item.pid) { if (!map[item.pid]) continue map[item.pid].children.push(node) } else { res.push(node) } } return res } let result toTree(list,) console.log(JSON.stringify(result,null,2));4.防抖防抖和节流都是控制高频事件触发的优化手段。防抖是指在事件被触发N秒后执行回调函数。如果在该时间段内又重发该函数则会重新计时。比如在输入框搜索时用户连续输入字符我们不希望每次输入都触发搜索请求而是在用户停止输入一段时间后再触发搜索请求这就可以使用防抖技术function debounce(fn,delay){ let timer null const _debounce function(...args){ if(timer) clearTimeout(timer) timer setTimeout((){ fn.apply(this,args) },delay) } return _debounce } const debounced debounce((name,age){ console.log(name,age); },3000) debounced(Tom,24)5.节流节流是指在每隔n秒后执行一次回调函数。比如当用户滚动页面时我们不希望每次滚动都触发相应的操作而是每隔一段时间才执行一次操作这就可以使用节流技术。function throttle(fn,delay){ let last 0 const _throttle function(...args){ const now new Date().getTime() if(now - last delay){ fn.apply(this,args) last now } } return _throttle }6.数组扁平化ES6 中的 flat 方法来实现数组扁平化。flat 方法的语法arr.flat([depth])其中 depth 是 flat 的参数depth 是可以传递数组的展开深度默认不填、数值是 1即展开一层数组。如果层数不确定参数可以传进 Infinity代表不论多少层都要展开function flatten(arr){ let res [] arr.forEach(item { if(Array.isArray(item)){ res.push(...flatten(item)) }else{ res.push(item) } }) return res }7.数组的去重给定某无序数组要求去除数组中的重复数字并且返回新的无重复数组。方法一使用ES6中的Set方法Set对象是值的合集集合中的元素只出现一次方法二可以使用哈希表// set去重的方法 let arr [1,2,3,4,5,6,1,2,7] let result Array.from(new Set(arr)) console.log(result); // 哈希表去重的方法 let arr [1,2,3,4,5,6,1,2,7] // 哈希表去重的方法 function uniqueArray(arr){ let result [] let map Object.create(null) for(let i 0; i arr.length; i){ const key arr[i] if(!Object.prototype.hasOwnProperty.call(map,key)){ map[key] true result.push(arr[i]) } } return result } console.log(uniqueArray(arr));总结先写这么多后面有时间继续更新哈。如果喜欢哈希望点赞收藏~~

更多文章