跳到主要内容
30 道高频 JavaScript 手写实现汇总 | 极客日志
JavaScript 大前端 算法
30 道高频 JavaScript 手写实现汇总 整理 30 个高频 JavaScript 手写实现,涵盖函数防抖节流、深拷贝、原型链操作、Promise 规范、设计模式及数组处理等核心知识点。通过解析原理与代码示例,帮助开发者深入理解语言机制,适用于面试准备与日常开发优化。涉及 AJAX、JSONP、URL 解析及图片懒加载等实际应用场景。
字节跳动 发布于 2026/2/26 更新于 2026/5/27 24 浏览JavaScript 手写实现合集
1. 手写防抖函数
原理 :事件被触发 n 秒后再执行回调,n 秒内又被触发则重新计时。
function debounce (fn, wait ) {
let timer = null ;
return function ( ) {
let context = this , args = arguments ;
if (timer) {
clearTimeout (timer);
timer = null ;
}
timer = setTimeout (() => {
fn.apply (context, args);
}, wait);
};
}
适用场景 :
按钮提交场景:防止多次提交按钮,只执行最后提交的一次
窗口滚动条的监听:滚动条触发次数过于频繁,通常只需要获取最后一次的位置
2. 手写节流函数
原理 :规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
使用时间戳实现
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);
}
};
}
使用标记实现
const throttle = (fn, delay = 500 ) => {
let flag = true ;
return (...args ) => {
if (!flag) return ;
flag = false ;
setTimeout (() => {
fn.apply (this , args);
flag = true ;
}, delay);
};
};
拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
缩放场景:监控浏览器 resize
动画场景:避免短时间内多次触发动画引起性能问题
3. 手写深拷贝
使用 JSON 方法
const newObj = JSON .parse (JSON .stringify (obj));
函数对象会被转成 null,RegExp 会变成 {}
会抛弃对象的 constructor,所有的构造函数会指向 Object
对象有循环引用会报错
递归实现深拷贝
function deepCopy (obj, dep = 0 ) {
if (typeof obj !== "object" || dep < 1 ) {
return typeof obj === "object" ? Object .assign ({}, obj) : obj;
}
let clone = Array .isArray (obj) ? [] : {};
for (const key in obj) {
if (Object .prototype .hasOwnProperty .call (obj, key)) {
if (obj[key] && typeof obj[key] === "object" ) {
clone[key] = deepCopy (obj[key], dep - 1 );
} else {
clone[key] = obj[key];
}
}
}
return clone;
}
4. 手写 Object.create function create (obj ) {
function F ( ) {}
F.prototype = obj;
return new F ();
}
面试知识点 :Object.create(null) 可以创建一个不含原型链的纯净 Object 对象。
5. 手写 instanceof instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
首先获取类型的原型
然后获得对象的原型
循环判断对象的原型是否等于类型的原型,直到对象原型为 null
function myInstanceof (left, right ) {
let __proto__ = Object .getPrototypeOf (left),
prototype = right.prototype ;
while (__proto__) {
if (__proto__ === prototype) {
return true ;
}
__proto__ = Object .getPrototypeOf (__proto__);
}
return false ;
}
6. 手写 new 操作符 在调用 new 的过程中会发生四件事情(面试常问):
在堆中开辟一块内存创建了一个新的空对象
设置原型,将新对象的原型设置为函数的 prototype 对象
让函数的 this 指向这个对象,执行构造函数的代码
判断函数的返回值类型,如果是值类型,返回创建的对象;如果是引用类型,就返回这个引用类型的对象
function objectFactory (constructor, ...rest ) {
let newObject = null ,
result = null ;
if (typeof constructor !== "function" ) {
console .error ("type error" );
return ;
}
newObject = Object .create (constructor.prototype );
result = constructor.apply (newObject, ...rest);
let flag = result && (typeof result === "object" || typeof result === "function" );
return flag ? result : newObject;
}
7. 手写 Function.prototype.call 方法
判断调用对象是否为函数
判断传入上下文对象是否存在,如果不存在,则为 window
处理传入的参数,截取第一个参数后的所有参数
将函数作为上下文对象的一个属性
使用上下文对象来调用这个方法,并保存返回结果
删除刚才新增的属性
返回结果
Function .prototype .myCall = function (context ) {
if (typeof this !== "function" ) {
console .error ("type error" );
}
let args = [...arguments ].slice (1 ),
result = null ;
context = context || window ;
context.fn = this ;
result = context.fn (...args);
delete context.fn ;
return result;
};
8. 手写 Function.prototype.apply 方法
判断调用对象是否为函数
判断传入上下文对象是否存在,如果不存在,则设置为 window
将函数作为上下文对象的一个属性
判断参数值是否传入
使用上下文对象来调用这个方法,并保存返回结果
删除刚才新增的属性
返回结果
Function .prototype .myApply = function (context ) {
if (typeof this !== "function" ) {
throw new TypeError ("Error" );
}
let result = null ;
context = context || window ;
context.fn = this ;
if (arguments [1 ]) {
result = context.fn (...arguments [1 ]);
} else {
result = context.fn ();
}
delete context.fn ;
return result;
};
9. 手写 Function.prototype.bind 方法
判断调用对象是否为函数
保存当前函数的引用,获取其余传入参数值
创建一个函数返回
函数内部使用 apply 来绑定函数调用,需要判断函数作为构造函数的情况
Function .prototype .myBind = function (context ) {
if (typeof this !== "function" ) {
throw new TypeError ("Error" );
}
var args = [...arguments ].slice (1 ),
fn = this ;
return function Fn ( ) {
return fn.apply (this instanceof Fn ? this : context, args.concat (...arguments ));
};
};
10. 手写函数柯里化 柯里化就是把接受「多个参数」的函数变换成接受一个「单一参数」的函数,并且返回接受「余下参数」返回结果的一种应用。
判断传递的参数是否达到执行函数的 fn 个数
没有达到的话,继续返回新的函数,并且返回 curry 函数传递剩余参数
实现方式 1 function curry (fn, args ) {
let length = fn.length ;
args = args || [];
return function ( ) {
let subArgs = args.slice (0 );
for (let i = 0 ; i < arguments .length ; i++) {
subArgs.push (arguments [i]);
}
if (subArgs.length >= length) {
return fn.apply (this , subArgs);
} else {
return curry.call (this , fn, subArgs);
}
};
}
实现方式 2 function currying (exeFunc ) {
let args = [];
let currFunc = function (...rest ) {
args.push (...rest);
if (rest.length <= 0 ) {
return exeFunc (args);
} else {
return currFunc;
}
};
return currFunc;
}
ES6 实现 function curry (fn, ...args ) {
return fn.length <= args.length ? fn (...args) : curry.bind (null , fn, ...args);
}
11. 手写 AJAX 请求 AJAX 是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。
创建一个 XMLHttpRequest 对象
在这个对象上使用 open 方法创建一个 HTTP 请求
在发起请求前,可以为这个对象添加一些信息和监听函数
当对象的 readyState 变为 4 的时候,代表服务器返回的数据接收完成
最后调用 send 方法来向服务器发起请求
const SERVER_URL = "/server" ;
let xhr = new XMLHttpRequest ();
xhr.open ("GET" , SERVER_URL , true );
xhr.onreadystatechange = function ( ) {
if (this .readyState !== 4 ) return ;
if (this .status === 200 ) {
handle (this .response );
} else {
console .error (this .statusText );
}
};
xhr.onerror = function ( ) {
console .error (this .statusText );
};
xhr.responseType = "json" ;
xhr.setRequestHeader ("Accept" , "application/json" );
xhr.send (null );
12. 手写 A+ 规范的 Promise Promise 是 ES6 新增的一个对象,手写 Promise 能帮助更好地理解其原理。
var PromisePolyfill = (function ( ) {
function tryToResolve (value ) {
if (this === value) {
throw TypeError ("Chaining cycle detected for promise!" );
}
if (value !== null && (typeof value === "object" || typeof value === "function" )) {
try {
var then = value.then ;
var thenAlreadyCalledOrThrow = false ;
if (typeof then === "function" ) {
then.bind (value)(
function (value2 ) {
if (thenAlreadyCalledOrThrow) return ;
thenAlreadyCalledOrThrow = true ;
tryToResolve.bind (this , value2)();
}.bind (this ),
function (reason2 ) {
if (thenAlreadyCalledOrThrow) return ;
thenAlreadyCalledOrThrow = true ;
resolveOrReject.bind (this , "rejected" , reason2)();
}.bind (this )
);
} else {
resolveOrReject.bind (this , "resolved" , value)();
}
} catch (e) {
if (thenAlreadyCalledOrThrow) return ;
thenAlreadyCalledOrThrow = true ;
resolveOrReject.bind (this , "rejected" , e)();
}
} else {
resolveOrReject.bind (this , "resolved" , value)();
}
}
function resolveOrReject (status, data ) {
if (this .status !== "pending" ) return ;
this .status = status;
this .data = data;
if (status === "resolved" ) {
for (var i = 0 ; i < this .resolveList .length ; ++i) {
this .resolveList [i]();
}
} else {
for (i = 0 ; i < this .rejectList .length ; ++i) {
this .rejectList [i]();
}
}
}
function Promise (executor ) {
if (!(this instanceof Promise )) {
throw Error ("Promise can not be called without new !" );
}
if (typeof executor !== "function" ) {
throw TypeError ("Promise resolver " + executor + " is not a function" );
}
this .status = "pending" ;
this .resolveList = [];
this .rejectList = [];
try {
executor (tryToResolve.bind (this ), resolveOrReject.bind (this , "rejected" ));
} catch (e) {
resolveOrReject.bind (this , "rejected" , e)();
}
}
Promise .prototype .then = function (onFullfilled, onRejected ) {
if (typeof onFullfilled !== "function" ) {
onFullfilled = function (data ) {
return data;
};
}
if (typeof onRejected !== "function" ) {
onRejected = function (reason ) {
throw reason;
};
}
var executor = function (resolve, reject ) {
setTimeout (function ( ) {
try {
var value =
this .status === "resolved"
? onFullfilled (this .data )
: onRejected (this .data );
resolve (value);
} catch (e) {
reject (e);
}
}.bind (this ));
};
if (this .status !== "pending" ) {
return new Promise (executor.bind (this ));
} else {
return new Promise (function (resolve, reject ) {
this .resolveList .push (executor.bind (this , resolve, reject));
this .rejectList .push (executor.bind (this , resolve, reject));
}.bind (this ));
}
};
Promise .deferred = Promise .defer = function ( ) {
var dfd = {};
dfd.promise = new Promise (function (resolve, reject ) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
if (typeof module !== "undefined" ) {
module .exports = Promise ;
}
return Promise ;
})();
PromisePolyfill .all = function (promises ) {
return new Promise ((resolve, reject ) => {
const result = [];
let cnt = 0 ;
for (let i = 0 ; i < promises.length ; ++i) {
promises[i].then (
(value ) => {
cnt++;
result[i] = value;
if (cnt === promises.length ) resolve (result);
},
reject
);
}
});
};
PromisePolyfill .race = function (promises ) {
return new Promise ((resolve, reject ) => {
for (let i = 0 ; i < promises.length ; ++i) {
promises[i].then (resolve, reject);
}
});
};
13. 手写千位分隔符 function parseToMoney (num ) {
num = parseFloat (num.toFixed (3 ));
let [integer, decimal] = String .prototype .split .call (num, "." );
integer = integer.replace (/\d(?=(\d{3})+$)/g , "$&," );
return integer + "." + (decimal ? decimal : "" );
}
14. 手写观察者模式 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。Vue 双向绑定就是使用了观察者模式。
class Subject {
constructor ( ) {
this .ObserverList = [];
}
add (observer ) {
this .ObserverList .push (observer);
}
remove (observer ) {
this .ObserverList = this .ObserverList .filter ((item ) => item !== observer);
}
notify (...arg ) {
this .ObserverList .forEach ((cb ) => cb.update (...arg));
}
}
class Observer {
constructor (name ) {
this .name = name;
}
update ( ) {
console .log (this .name );
}
}
15. 发布/订阅模式 发布/订阅模式:基于一个主题/事件通道,希望接收通知的对象(称为 subscriber)通过自定义事件订阅主题,被激活事件的对象(称为 publisher)通过发布主题事件的方式被通知。Vue2 中的全局事件总线就是使用这种设计模式。
class Pubsub {
constructor ( ) {
this .topics = {};
this .subUid = -1 ;
}
publish (topic, args ) {
if (!this .topics [topic]) return false ;
let subscribers = this .topics [topic];
let len = subscribers ? subscribers.length : 0 ;
while (len--) {
subscribers[len].func (topic, args);
}
return this ;
}
subscribe (topic, func ) {
if (!this .topics [topic]) this .topics [topic] = [];
let token = (++this .subUid ).toString ();
this .topics [topic].push ({
token : token,
func : func,
});
return token;
}
unsubscribe (token ) {
for (let m in this .topics ) {
if (this .topics [m]) {
for (let i = 0 ; i < this .topics [m].length ; i++) {
if (this .topics [m][i].token == token) {
this .topics [m].splice (i, 1 );
return token;
}
}
}
}
return this ;
}
}
16. 使用 setTimeout 实现 setInterval setInterval 的作用是每隔一段指定时间执行一个函数,但是这个执行不是真的到了时间立即执行,它真正的作用是每隔一段时间将事件加入事件队列中去。只有当执行栈为空的时候,才能去从事件队列中取出事件执行。
针对 setInterval 的这个缺点,我们可以使用 setTimeout 递归调用来模拟 setInterval,这样我们就确保了只有一个事件结束了,我们才会触发下一个定时器事件。
function mySetInterval (fn, timeout ) {
setTimeout (() => {
fn ();
mySetInterval (fn, timeout);
}, timeout);
}
17. 将 JS 对象转化为树形结构 source = [
{ id : 1 , pid : 0 , name : "body" },
{ id : 2 , pid : 1 , name : "title" },
{ id : 3 , pid : 2 , name : "div" },
];
tree = [
{
id : 1 ,
pid : 0 ,
name : "body" ,
children : [
{
id : 2 ,
pid : 1 ,
name : "title" ,
children : [
{
id : 3 ,
pid : 2 ,
name : "div" ,
},
],
},
],
},
];
function jsonToTree (data ) {
let result = [];
if (!Array .isArray (data)) {
return result;
}
let map = {};
data.forEach ((item ) => {
map[item.id ] = item;
});
data.forEach ((item ) => {
let parent = map[item.pid ];
if (parent) {
(parent.children || (parent.children = [])).push (item);
} else {
result.push (item);
}
});
return result;
}
18. 实现 sleep 函数
使用 Promise 封装 setTimeout function timeout (delay ) {
return new Promise ((resolve ) => {
setTimeout (resolve, delay);
});
}
使用时间戳阻塞线程 function sleep (time, fn ) {
let curDate = Date .now ();
while (Date .now () - curDate < time);
fn ();
}
19. 手写 ES6 的 flat(数组扁平化) 数组扁平化就是将 [1, [2, [3]] 这种多层的数组拍平成一层 [1, 2, 3]。
let arr = [
[22 , [1 , 2 , [5 , 8 ], [9 , 2 ]], [2 , 7 , 1 , [9 , 0 , [1 , 6 ]]]],
[2 ],
];
方法 1: reduce + concat function flatten (arr, dep ) {
return dep > 1
? arr.reduce ((pre, cur ) => pre.concat (Array .isArray (cur) ? flatten (cur, dep - 1 ) : cur), [])
: Array .prototype .slice .call (arr);
}
方法 2: some + concat + 解构 function flatten2 (arr, dep = 0 ) {
if (dep < 1 ) {
return arr.slice ();
}
while (dep && arr.some ((item ) => Array .isArray (item))) {
arr = [].concat (...arr);
dep--;
}
return arr;
}
方法 3: push + apply function flatten3 (arr, dep = 0 ) {
if (dep < 1 ) {
return Array .prototype .slice .call (arr);
}
let result = [];
arr.forEach ((element ) =>
Array .isArray (element) ? result.push .apply (result, flatten3 (element, dep - 1 )) : result.push (element)
);
return result;
}
方法 4: toString (仅适用于数字) function flatten4 (arr ) {
return arr.toString ().split ("," ).map ((item ) => +item);
}
方法 5: 栈实现 function flatten5 (arr, dep = 0 ) {
let stack = [...arr];
let result = [];
while (stack.length ) {
let data = stack.pop ();
if (Array .isArray (data)) {
stack.push (...data);
} else {
result.push (data);
}
}
return result.reverse ();
}
20. 手写 Object.assign Object .myAssign = function (target, ...source ) {
if (target == null ) {
throw new TypeError ("Cannot convert undefined or null to object" );
}
let ret = Object (target);
source.forEach (function (obj ) {
if (obj != null ) {
for (let key in obj) {
if (obj.hasOwnProperty (key)) {
ret[key] = obj[key];
}
}
}
});
return ret;
};
21. 实现每隔一秒打印 1,2,3,4
使用闭包实现 for (var i = 0 ; i < 5 ; i++) {
(function (i ) {
setTimeout (function ( ) {
console .log (i);
}, i * 1000 );
})(i);
}
使用 let 块级作用域 for (let i = 0 ; i < 5 ; i++) {
setTimeout (function ( ) {
console .log (i);
}, i * 1000 );
}
22. JS 解析 URL Params 为对象 let url = "http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled" ;
parseParam (url);
function parseParam (url ) {
const paramsStr = /.+\?(.+)$/ .exec (url)[1 ];
const paramsArr = paramsStr.split ("&" );
let paramsObj = {};
paramsArr.forEach ((param ) => {
if (/=/ .test (param)) {
let [key, val] = param.split ("=" );
val = decodeURIComponent (val);
val = /^\d+$/ .test (val) ? parseFloat (val) : val;
if (paramsObj.hasOwnProperty (key)) {
paramsObj[key] = [].concat (paramsObj[key], val);
} else {
paramsObj[key] = val;
}
} else {
paramsObj[param] = true ;
}
});
return paramsObj;
}
23. 实现一个 JSON.stringify
Boolean | Number | String 类型会自动转换成对应的原始值
undefined、任意函数以及 symbol,会被忽略(非数组对象)或者被转换成 null(数组中)
不可枚举的属性会被忽略
循环引用属性会被忽略
Function 会变成 null,RegExp 对象会变成 {}
function jsonStringify (obj ) {
let type = typeof obj;
if (type !== "object" ) {
if (/string|undefined|function/ .test (type)) {
obj = '"' + obj + '"' ;
}
return String (obj);
} else {
let json = [];
let arr = Array .isArray (obj);
for (let k in obj) {
let v = obj[k];
let type = typeof v;
if (/string|undefined|function/ .test (type)) {
v = '"' + v + '"' ;
} else if (type === "object" ) {
v = jsonStringify (v);
}
json.push ((arr ? "" : '"' + k + '":' ) + String (v));
}
return (arr ? "[" : "{" ) + String (json) + (arr ? "]" : "}" );
}
}
24. 实现 JSON.parse let json = '{"name":"cxk", "age":25}' ;
let obj = eval ("(" + json + ")" );
使用 eval 实现简单但很容易被 XSS 攻击。
25. 手写 JS 的继承 继承有很多方式,这里只归纳最优的两种:寄生组合式和 ES6 的 class 继承。
寄生组合式继承 function inheritPrototype (subType, superType ) {
let prototype = Object .create (superType.prototype );
prototype.constructor = subType;
subType.prototype = prototype;
}
ES6 class 的 extends class Rectangle {
constructor (height, width ) {
this .height = height;
this .width = width;
}
get area () {
return this .calcArea ();
}
calcArea ( ) {
return this .height * this .width ;
}
}
const rectangle = new Rectangle (40 , 20 );
console .log (rectangle.area );
class Square extends Rectangle {
constructor (len ) {
super (len, len);
this .name = "SquareIng" ;
}
get area () {
return this .height * this .width ;
}
}
const square = new Square (20 );
console .log (square.area );
extends 继承的核心代码如下,其实和寄生组合式继承方式一样:
function _inherits (subType, superType ) {
subType.prototype = Object .create (superType && superType.prototype , {
constructor : {
value : subType,
enumerable : false ,
writable : true ,
configurable : true ,
},
});
if (superType) {
Object .setPrototypeOf
? Object .setPrototypeOf (subType, superType)
: (subType.__proto__ = superType);
}
}
26. 判断对象是否存在循环引用 循环引用对象本来没有什么问题,但是序列化的时候就会发生问题,比如调用 JSON.stringify() 对该类对象进行序列化,就会报错:Converting circular structure to JSON。
const isCycleObject = (obj, parent ) => {
const parentArr = parent || [obj];
for (let i in obj) {
if (typeof obj[i] === "object" ) {
let flag = false ;
parentArr.forEach ((pObj ) => {
if (pObj === obj[i]) {
flag = true ;
}
});
if (flag) return true ;
flag = isCycleObject (obj[i], [...parentArr, obj[i]]);
if (flag) return true ;
}
}
return false ;
};
27. 数组的去重
双重循环 function unique (array ) {
var res = [];
for (var i = 0 , arrayLen = array.length ; i < arrayLen; i++) {
for (var j = 0 , resLen = res.length ; j < resLen; j++) {
if (array[i] === res[j]) {
break ;
}
}
if (j === resLen) {
res.push (array[i]);
}
}
return res;
}
ES6 Set function unique (arr ) {
return [...new Set (arr)];
}
filter + indexOf function unique_ (arr ) {
return arr.filter ((item, index ) => arr.indexOf (item) === index);
}
reduce + includes function unique__ (arr ) {
return arr.reduce ((pre, cur ) => (pre.includes (cur) ? pre : pre.concat (cur)), []);
}
Map 实现 function unique___ (arr ) {
let mp = new Map ();
arr.forEach ((element ) => {
if (!mp.has (element)) {
mp.set (element, 1 );
}
});
return [...mp.keys ()];
}
排序后去重 function unique (array ) {
var res = [];
var sortedArray = array.slice ().sort ();
var seen;
for (var i = 0 , len = sortedArray.length ; i < len; i++) {
if (!i || seen !== sortedArray[i]) {
res.push (sortedArray[i]);
}
seen = sortedArray[i];
}
return res;
}
28. 图片懒加载 图片懒加载就是鼠标滑动到哪里,图片加载到哪里。总的来说,一般页面打开,会同时加载页面所有的图片,如果页面的图片请求太多会造成很卡很慢的现象,为了避免这一现象,利用懒加载图片的方法,提高性能。
let imgList = [...document .querySelectorAll ("img" )];
let length = imgList.length ;
const imgLazyLoad = (function ( ) {
let count = 0 ;
return function ( ) {
let deleteIndexList = [];
imgList.forEach ((img, index ) => {
let rect = img.getBoundingClientRect ();
if (rect.top < window .innerHeight ) {
img.src = img.dataset .src ;
deleteIndexList.push (index);
count++;
if (count === length) {
document .removeEventListener ("scroll" , imgLazyLoad);
}
}
});
imgList = imgList.filter ((img, index ) => !deleteIndexList.includes (index));
};
})();
document .addEventListener ("scroll" , debounce (imgLazyLoad, 200 ));
29. 洗牌算法 function shuffle (arr ) {
for (let i = 0 ; i < arr.length ; i++) {
let randomIndex = i + Math .floor (Math .random () * (arr.length - i));
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
}
return arr;
}
30. 手写 JSONP JSONP 的特殊之处在于前端会传递一个 callback 参数给后端,后端返回数据时会将这个 callback 参数的值作为函数名来包裹住 JSON 数据,最终返给前端的就是一段 JS 代码,这样就巧妙地解决了跨域问题。优点是兼容性好,但只能用于 GET 请求,而且需要服务端支持。
const jsonp = ({ url, params, callbackName } ) => {
const generateUrl = ( ) => {
let dataSrc = "" ;
for (let key in params) {
if (params.hasOwnProperty (key)) {
dataSrc += `${key} =${params[key]} &` ;
}
}
dataSrc += `callback=${callbackName} ` ;
return `${url} ?${dataSrc} ` ;
};
return new Promise ((resolve, reject ) => {
const scriptEle = document .createElement ("script" );
scriptEle.src = generateUrl ();
document .body .appendChild (scriptEle);
window [callbackName] = (data ) => {
resolve (data);
document .removeChild (scriptEle);
};
});
};
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online