前端面试宝典总结4-手搓代码JavaScript(基础版)

前端面试宝典总结4之手写代码JavaScript(基础版)

本文章 对各大学习技术论坛知识点,进行总结、归纳自用学习,共勉🙏

上一篇👉: 前端面试宝典总结4-手搓代码JavaScript(数据处理)

文章目录

  • 前端面试宝典总结4之手写代码JavaScript(基础版)
  • 1.手写 instanceof 方法
  • 2.手写类型判断函数
  • 3.手写 call 函数
  • 4. 手写 apply 函数
  • 5.手写 bind 函数
  • 6. 手写防抖函数
  • 7. 手写节流函数
  • 8. 手写 Object.create
  • 9. 手写 new 操作符
  • 10. 手写 Promise
  • 11. 手写 Promise.then
  • 12. 手写 Promise.all
  • 13. 手写 Promise.race
  • 14.实现深拷贝
  • 15.实现斐波那契数列

1.手写 instanceof 方法

instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

  1. 首先获取类型的原型
  2. 然后获得对象的原型
  3. 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
function myInstanceof(left, right) {let proto = Object.getPrototypeOf(left), // 获取对象的原型prototype = right.prototype; // 获取构造函数的 prototype 对象// 判断构造函数的 prototype 对象是否在对象的原型链上while (true) {if (!proto) return false;if (proto === prototype) return true;proto = Object.getPrototypeOf(proto);}
}

2.手写类型判断函数

function getType(value) {// 判断数据是 null 的情况if (value === null) {return value + "";}// 判断数据是引用类型的情况if (typeof value === "object") {let valueClass = Object.prototype.toString.call(value),type = valueClass.split(" ")[1].split("");type.pop();return type.join("").toLowerCase();} else {// 判断数据是基本数据类型的情况和函数的情况return typeof value;}
}

3.手写 call 函数

call 函数的实现步骤:

  1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
  2. 判断传入上下文对象是否存在,如果不存在,则设置为 window ,(在Web浏览器中是window,Node.js中是globalglobalThis)。
  3. 处理传入的参数,截取第一个参数后的所有参数。
  4. 将函数作为上下文对象的一个属性。
  5. 使用上下文对象来调用这个方法,并保存返回结果。
  6. 删除刚才新增的属性。
  7. 返回结果。
// call函数实现
Function.prototype.myCall = function(context) {// 判断调用对象if (typeof this !== "function") {console.error("type error");}// 获取参数let args = [...arguments].slice(1),result = null;// 判断 context 是否传入,如果未传入则设置为 windowcontext = context || window;// 将调用函数设为对象的方法context.fn = this;// 调用函数result = context.fn(...args);// 将属性删除delete context.fn;return result;
};

4. 手写 apply 函数

apply 函数的实现步骤:

  1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
  2. 判断传入上下文对象是否存在,如果不存在,则设置为 window ,(在Web浏览器中是window,Node.js中是globalglobalThis)。
  3. 将函数作为上下文对象的一个属性。
  4. 判断参数值是否传入
  5. 使用上下文对象来调用这个方法,并保存返回结果。
  6. 删除刚才新增的属性
  7. 返回结果
// apply 函数实现
Function.prototype.myApply = function(context) {// 判断调用对象是否为函数if (typeof this !== "function") {throw new TypeError("Error");}let result = null;// 判断 context 是否存在,如果未传入则为 windowcontext = context || window;// 将函数设为对象的方法context.fn = this;// 调用方法if (arguments[1]) {result = context.fn(...arguments[1]);} else {result = context.fn();}// 将属性删除delete context.fn;return result;
};

5.手写 bind 函数

bind 函数的实现步骤:

  1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
  2. 保存当前函数的引用,获取其余传入参数值。
  3. 创建一个函数返回
  4. 函数内部使用 apply 来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的 thisapply 调用,其余情况都传入指定的上下文对象。
Function.prototype.customBind = function(context, ...argsBefore) {// 步骤1: 判断调用对象是否为函数if (typeof this !== 'function') {throw new TypeError('The customBind method can only be called on a function');}// 步骤2: 保存当前函数的引用,获取其余传入参数值const func = this;// 步骤3: 创建一个函数返回return function(...argsAfter) {// 函数内部使用 apply 来绑定函数调用// 判断是否为构造函数调用,通过 new.target 判断const finalContext = new.target ? this : context;return func.apply(finalContext, argsBefore.concat(argsAfter));};
};// 示例使用
function greet(greeting) {return greeting + ', ' + this.name;
}const user = {name: 'Alice'};
const boundGreet = greet.customBind(user, 'Hello');console.log(boundGreet()); // 输出: Hello, Alice

6. 手写防抖函数

函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

/*** 函数防抖 (Debounce) 实现* * @param {Function} fn 要防抖的函数* @param {Number} wait 防抖时间,单位毫秒* @returns {Function} 返回防抖后的函数*/
function debounce(fn, wait) {let timer = null; // 用来保存定时器的标识// 返回的防抖函数会在每次调用时执行return function debouncedFunction() {// 保存当前的上下文和参数,因为在setTimeout中需要正确地应用它们let context = this;let args = arguments;// 如果定时器存在,说明前一次的调用还在延迟中,需要取消前一次的调用if (timer) {clearTimeout(timer);timer = null;}// 重新设置定时器,延迟wait毫秒后执行fntimer = setTimeout(() => {// 使用apply来确保fn在正确的上下文中执行,并且传入参数fn.apply(context, args);}, wait);};
}// 示例使用
document.getElementById('someButton').addEventListener('click', debounce(function(event) {console.log('点击事件触发,但是我只会在你停止点击1秒后响应!');
}, 1000)

7. 手写节流函数

函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

// 函数节流的实现;
function throttle(fn, delay) {let curTime = Date.now();return function() {let context = this,args = arguments,nowTime = Date.now();// 如果两次时间间隔超过了指定时间,则执行函数。if (nowTime - curTime >= delay) {curTime = Date.now();return fn.apply(context, args);}};
}

8. 手写 Object.create

思路:将传入的对象作为原型

function create(obj) {// 创建一个临时构造函数Ffunction F() {}// 将F的prototype设置为传入的对象obj,这样任何通过F构造出来的实例都会将obj作为它们的原型F.prototype = obj;// 使用new操作符调用F构造函数,创建一个新的对象实例,该实例的原型自动指向F.prototype,即objreturn new F();
}let prototypeObj = {hello: function() {console.log('Hello, world!');}
};let newObj = create(prototypeObj);// 现在 newObj 可以访问原型上的 hello 方法
newObj.hello(); // 输出: Hello, world!// 验证 newObj 的原型是否为 prototypeObj
console.log(Object.getPrototypeOf(newObj) === prototypeObj); // 输出: true

9. 手写 new 操作符

在调用 new 的过程中会发生以上四件事情:

  1. 首先创建了一个新的空对象。
  2. 设置原型,将对象的原型设置为函数的 prototype 对象。
  3. 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)。
  4. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
function objectFactory() {// 从arguments中移除构造函数并保存,同时修改arguments数组内容const constructor = Array.prototype.shift.call(arguments);// 确保传入的是一个函数,否则抛出错误if (typeof constructor !== 'function') {throw new TypeError('Argument must be a function');}// 创建一个新对象,其原型指向构造函数的prototypeconst newObject = Object.create(constructor.prototype);// 调用构造函数,将其this绑定到新对象上,并传入剩余的arguments作为构造函数的参数const result = constructor.apply(newObject, arguments);// 检查构造函数的返回值是否为一个对象(或函数,因为函数也是对象),如果不是则忽略返回值,直接返回新创建的对象// 注意:此处逻辑应确保result不为null或undefined,而不仅仅是对象或函数,因此修正条件检查return result instanceof Object ? result : newObject;
}// 使用方法示例
function Person(name) {this.name = name;
}Person.prototype.sayHello = function() {console.log(`Hello, my name is ${this.name}`);
};const person = objectFactory(Person, 'Alice');
person.sayHello(); // 输出: Hello, my name is Alice

10. 手写 Promise

const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";function MyPromise(fn) {// 保存初始化状态var self = this;// 初始化状态this.state = PENDING;// 用于保存 resolve 或者 rejected 传入的值this.value = null;// 用于保存 resolve 的回调函数this.resolvedCallbacks = [];// 用于保存 reject 的回调函数this.rejectedCallbacks = [];// 状态转变为 resolved 方法function resolve(value) {// 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变if (value instanceof MyPromise) {return value.then(resolve, reject);}// 保证代码的执行顺序为本轮事件循环的末尾setTimeout(() => {// 只有状态为 pending 时才能转变,if (self.state === PENDING) {// 修改状态self.state = RESOLVED;// 设置传入的值self.value = value;// 执行回调函数self.resolvedCallbacks.forEach(callback => {callback(value);});}}, 0);}// 状态转变为 rejected 方法function reject(value) {// 保证代码的执行顺序为本轮事件循环的末尾setTimeout(() => {// 只有状态为 pending 时才能转变if (self.state === PENDING) {// 修改状态self.state = REJECTED;// 设置传入的值self.value = value;// 执行回调函数self.rejectedCallbacks.forEach(callback => {callback(value);});}}, 0);}// 将两个方法传入函数执行try {fn(resolve, reject);} catch (e) {// 遇到错误时,捕获错误,执行 reject 函数reject(e);}
}MyPromise.prototype.then = function(onResolved, onRejected) {// 首先判断两个参数是否为函数类型,因为这两个参数是可选参数onResolved =typeof onResolved === "function"? onResolved: function(value) {return value;};onRejected =typeof onRejected === "function"? onRejected: function(error) {throw error;};// 如果是等待状态,则将函数加入对应列表中if (this.state === PENDING) {this.resolvedCallbacks.push(onResolved);this.rejectedCallbacks.push(onRejected);}// 如果状态已经凝固,则直接执行对应状态的函数if (this.state === RESOLVED) {onResolved(this.value);}if (this.state === REJECTED) {onRejected(this.value);}
};

11. 手写 Promise.then

then 方法返回一个新的 promise 实例,为了在 promise 状态发生变化时(resolve / reject 被调用时)再执行 then 里的函数,我们使用一个 callbacks 数组先把传给then的函数暂存起来,等状态改变时再调用。
那么,保证后一个 then 里的方法在前一个 then(可能是异步)结束之后再执行?
可以将传给 then 的函数和新 promiseresolve 一起 push 到前一个 promisecallbacks 数组中,达到承前启后的效果:

  • 承前:当前一个 promise 完成后,调用其 resolve 变更状态,在这个 resolve 里会依次调用 callbacks 里的回调,这样就执行了 then 里的方法了
  • 启后:上一步中,当 then 里的方法执行完成后,返回一个结果,如果这个结果是个简单的值,就直接调用新 promise 的 resolve,让其状态变更,这又会依次调用新 promise 的 callbacks 数组里的方法,循环往复。如果返回的结果是个 promise,则需要等它完成之后再触发新 promiseresolve,所以可以在其结果的 then 里调用新 promiseresolve
then(onFulfilled, onReject){// 保存前一个promise的thisconst self = this; return new MyPromise((resolve, reject) => {// 封装前一个promise成功时执行的函数let fulfilled = () => {try{const result = onFulfilled(self.value); // 承前return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //启后}catch(err){reject(err)}}// 封装前一个promise失败时执行的函数let rejected = () => {try{const result = onReject(self.reason);return result instanceof MyPromise? result.then(resolve, reject) : reject(result);}catch(err){reject(err)}}switch(self.status){case PENDING: self.onFulfilledCallbacks.push(fulfilled);self.onRejectedCallbacks.push(rejected);break;case FULFILLED:fulfilled();break;case REJECT:rejected();break;}})}

12. 手写 Promise.all

思路:

  1. 接收一个 Promise 实例的数组或具有 Iterator 接口的对象作为参数
  2. 这个方法返回一个新的 promise 对象,
  3. 遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象
  4. 参数所有回调成功才是成功,返回值数组与参数顺序一致
  5. 参数数组其中一个失败,则触发失败状态,第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息。

一般来说,Promise.all 用来处理多个并发请求,也是为了页面数据构造的方便,将一个页面所用到的在不同接口的数据一起请求过来,不过,一旦其中一个接口请求失败,多个请求也就失败了 整个就给噶了,页面可能无法展示内容,页面能否展现出一定的健壮性,部分取决于其对各个数据组件相互依赖的紧密程度耦合性

function promiseAll(promises) {return new Promise((resolve, reject) => {if (!Array.isArray(promises)) {throw new TypeError('argument must be an array');}const promiseNum = promises.length;const resolvedResult = new Array(promiseNum);let resolvedCounter = 0;let hasRejected = false;function handleResolve(index, value) {resolvedResult[index] = value;resolvedCounter++;if (resolvedCounter === promiseNum && !hasRejected) {resolve(resolvedResult);}}function handleReject(reason) {if (!hasRejected) {hasRejected = true;reject(reason);}}promises.forEach((p, index) => {Promise.resolve(p).then(value => handleResolve(index, value), reject => handleReject(reject));});});
}// Test
let p1 = new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000));
let p2 = new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000));
let p3 = new Promise((resolve, reject) => setTimeout(() => reject('Error'), 500));promiseAll([p3, p1, p2]).then(res => console.log(res)).catch(err => console.error(err)); // Will catch the first rejection with 'Error'

13. 手写 Promise.race

该方法的参数是 Promise 实例数组, 然后其 then 注册的回调方法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行. 因为 Promise 的状态只能改变一次, 那么我们只需要把 Promise.race 中产生的 Promise 对象的 resolve 方法, 注入到数组中的每一个 Promise 实例中的回调函数中即可。

// 假设这是自定义Promise构造函数或者正在扩展原生Promise
// 这里直接使用原生Promise进行演示
if (!Promise.race) {Promise.race = function (promises) {// 确保传入的是一个可迭代对象if (!Array.isArray(promises)) {return Promise.reject(new TypeError('Promise.race requires an array of promises'));}return new Promise((resolve, reject) => {// 遍历传入的Promise数组for (let i = 0; i < promises.length; i++) {// 对每个Promise实例注册回调promises[i].then(resolve, reject);}});};
}
}// 示例使用
const p1 = new Promise((resolve, reject) => setTimeout(() => resolve('Promise 1 resolved'), 1000));
const p2 = new Promise((resolve, reject) => setTimeout(() => reject('Promise 2 rejected'), 500));
const p3 = new Promise((resolve, reject) => setTimeout(() => resolve('Promise 3 resolved'), 3000));Promise.race([p1, p2, p3]).then(value => console.log('Resolved:', value)).catch(reason => console.error('Rejected:', reason));

这段代码首先检查了传入Promise.race的参数是否为一个数组,确保了类型安全。然后,对于数组中的每个Promise实例,分别注册了resolvereject回调,这样无论哪个Promise先改变状态(无论是fulfilled还是rejected),Promise.race返回的Promise都会立即采用这个状态,反映出“赛跑”中的“胜利者”。

14.实现深拷贝

深拷贝是指创建一个新对象,其内容是原对象的完全拷贝,包括子对象也进行同样的拷贝,而不是引用。

function deepClone(obj) {if (obj === null || typeof obj !== 'object') return obj;let cloneObj = Array.isArray(obj) ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {cloneObj[key] = deepClone(obj[key]);}}}return cloneObj;
}

15.实现斐波那契数列

斐波那契数列是这样一个数列:每个数字是前两个数字的和,前两个数字是0和1。

function fibonacci(n) {if (n <= 0) return 0;if (n === 1) return 1;return fibonacci(n - 1) + fibonacci(n - 2);
}
// 或者使用动态规划减少重复计算
function fibonacciDP(n) {let fib = [0, 1];for (let i = 2; i <= n; i++) {fib[i] = fib[i - 1] + fib[i - 2];}return fib[n];
}

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

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

相关文章

R语言学习 - 柱状图

柱状图绘制 柱状图也是较为常见的一种数据展示方式&#xff0c;可以展示基因的表达量&#xff0c;也可以展示GO富集分析结果&#xff0c;基因注释数据等。这篇转录组工具比较 转录组分析工具哪家强&#xff1f;中就使用到比较多堆积柱状图。 常规矩阵柱状图绘制 有如下4个基…

Audio PsyChat:web端语音心理咨询系统

这是一个在服务器本地运行的web语音心理咨询系统&#xff0c;咨询系统内核使用PsyChat&#xff0c;我们为其制作了Web前端&#xff0c;并拼接了ASR和TTS组件&#xff0c;使局域网内用户可以通过单纯的语音进行交互。其中ASR和TTS组件使用PaddleSpeech API。 使用 使用单卡3090…

信息学奥赛初赛天天练-19-挑战程序阅读-探索因数、所有因数平和、质数的奥秘

PDF文档公众号回复关键字:20240604 1 2023 CSP-J 阅读程序3 阅读程序&#xff08;程序输入不超过数组成字符串定义的范围&#xff1a;判断题正确填√&#xff0c;错误填&#xff1b;除特殊说明外&#xff0c;判断题1.5分&#xff0c;选择题3分&#xff0c;共计40分&#xff…

C++中的静态变量与普通变量

在C中&#xff0c;变量的存储和生命周期可以根据其定义的位置和方式而有所不同。特别是&#xff0c;静态变量&#xff08;无论是静态局部变量还是静态全局变量&#xff09;与普通的全局变量和局部变量在行为和生命周期上有显著的区别。 局部变量 局部变量是在函数内部定义的变…

【微信支付】获取微信开发信息(全网最详细!!!)

前言 1、申请商户号 申请流程与资料 详细申请步骤 申请开通接入微信支付步骤 2、申请微信小程序 申请小程序步骤 查看小程序AppID 3、微信支付普通商户与AppID账号关联 4、获取开发中需要的密钥和证书 4.1、申请证书 4.2、下载证书工具 4.3、证书工具—填写商户信息…

如何进行团队协作

团队协作是项目管理中不可或缺的一部分&#xff0c;它涉及多个团队成员共同工作以达成共同的目标。以下是一些关于如何进行团队协作的建议&#xff1a; 1. 明确目标和角色 设定清晰的目标&#xff1a;确保所有团队成员都清楚了解项目的总体目标以及他们各自在其中的角色和职责…

关于微积分的几个问题回顾

1.定积分求解举例 定积分是微积分中的一个重要概念&#xff0c;用于求解连续函数在某一区间上的面积或体积等问题。下面我将给出一个定积分求解的举例。 假设我们要求解函数 f(x)x2 在区间 [0,1] 上的定积分&#xff0c;即求解 ∫01​x2dx 求解步骤 1. 找出被积函数 f(x) …

3D分割之SAGA训练流程解读

训练之前,会先提取2种特征, 一种是每张图片的image encoding, 它的size是(64,64),代表每个像素处的特征向量。这个向量用于特征匹配(选中的目标和每个像素的相似度)。 一种是SAM提取的所有mask(用于计算mask所在目标的特征向量)。 extract_features.py提取的是SAM模型…

计算机基础(1)——计算机的发展史

&#x1f497;计算机基础系列文章&#x1f497; &#x1f449;&#x1f340;计算机基础&#xff08;1&#xff09;——计算机的发展史&#x1f340;&#x1f449;&#x1f340;计算机基础&#xff08;2&#xff09;——冯诺依曼体系结构&#x1f340;&#x1f449;&#x1f34…

【计算机毕设】基于SpringBoot的个人理财系统设计与实现 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 个人理财管理对于现代人来说越来越重要&#xff0c;随着金融产品和消费方式的多样化&#xff0c;人们需要一个方便、高效、安全的工具来管理和规划自…

C语言基础学习之链表与共同体

数组: 数据结构---操作时候的特点&#xff1a; 优势&#xff1a;随机访问(存取)方便 不足&#xff1a;插入数据删除数据不方便 链式数据结构--链表 struct stu sl; // s1struct stu s2; // s2struct stu s3; //s3 s1-->s2-->s3 特点: 优势:增力和删除数据方便劣势…

【光谱特征选择】竞争性自适应重加权算法CARS(含python代码)

目录 一、背景 二、代码实现 三、项目代码 一、背景 竞争性自适应重加权算法&#xff08;Competitive Adaptive Reweighted Sampling&#xff0c;CARS&#xff09;是一种用于选择高光谱数据中最具代表性波段的方法。CARS通过模拟“生物进化”过程&#xff0c;自适应地对光谱…

IP路由基础ospf

文章目录 IP路由基础路由表(RIB)和转发表(FIB) 动态路由协议动态路由协议&#xff1a; OSPFOSPF基础工作原理&#xff1a;OSPF网络类型简介DR和BDR的选举选举规则&#xff1a; OSPF单区域路由计算LSA报文信息LSA处理原则单区域OSPF只会产生两类LSA&#xff1a;Router LSA 报文详…

Swift 协议

协议 一、协议语法二、属性要求三、方法要求四、异变方法要求五、构造器要求1、协议构造器要求的类实现2.可失败构造器要求 六、协议作为类型七、委托八、在扩展里添加协议遵循九、有条件地遵循协议十、在扩展里声明采纳协议十一、使用合成实现来采纳协议十二、协议类型的集合十…

Docker桥接网络分析

前言 《虚拟局域网(VLAN)》一文中描述了虚拟网卡、虚拟网桥的作用&#xff0c;以及通过iptables实现了vlan联网&#xff0c;其实学习到这里自然就会联想到目前主流的容器技术&#xff1a;Docker&#xff0c;因此接下来打算研究一下Docker的桥接网络与此有何异同。 猜测 众所周知…

第十一届蓝桥杯C++青少年组中/高级组选拔赛2019年真题解析

一、单选题 第1题 一个C语言的源程序中&#xff0c;有关主函数的说法正确的是&#xff08; &#xff09;. A:可以有多个主函数 B:必须有一个主函数 C:必须有主函数和其他函数 D:可以没有主函数 答案&#xff1a; 第2题 在下面的条件语句中&#xff08;其中s1和s2代表C语言…

计算机基础(8)——音频数字化(模电与数电)

&#x1f497;计算机基础系列文章&#x1f497; &#x1f449;&#x1f340;计算机基础&#xff08;1&#xff09;——计算机的发展史&#x1f340;&#x1f449;&#x1f340;计算机基础&#xff08;2&#xff09;——冯诺依曼体系结构&#x1f340;&#x1f449;&#x1f34…

如何在GlobalMapper中加载高清卫星影像?

GlobalMapper在GIS行业几乎无人不知&#xff0c;无人不晓&#xff0c;但它可以直接加载卫星影像也许就不是每个人都知道的了。 这里就来分享一下如何在GlobalMapper中加载高清卫星影像&#xff0c;并可以在文末查看领取软件安装包和图源的方法。 如何加载高清图源 首先&…

45-1 waf绕过 - 文件上传绕过WAF方法

环境准备: 43-5 waf绕过 - 安全狗简介及安装-CSDN博客然后安装dvwa靶场:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客打开dvwa靶场,先将靶场的安全等级调低,然后切换到文件上传 一、符号变异 在PHP中,由于其弱类型特性,有时候仅有一…

4月份新出!外网爆火的大模型黑书!内行人都在学~

今天给大家推荐一本4月份才新出的大型语言模型&#xff08;LLM&#xff09;的权威教程《基于GPT-3、ChatGPT、GPT-4等Transformer架构的自然语言处理》&#xff01;Google工程总监Antonio Gulli作序&#xff0c;一堆大佬推荐&#xff01;这含金量不用多说&#xff0c;在这里给大…