厦门学网站设计,安徽城乡与建设部网站,免费的推广平台有哪些,pythone网站开发Debug库 是一个小巧但功能强大的 JavaScript 调试工具库#xff0c;可以帮助开发人员更轻松地进行调试#xff0c;以便更快地发现和修复问题。它的主要特点是可以轻松地添加调试日志语句#xff0c;同时在不需要调试时可以轻松地禁用它们#xff0c;以避免在生产环境中对性… Debug库 是一个小巧但功能强大的 JavaScript 调试工具库可以帮助开发人员更轻松地进行调试以便更快地发现和修复问题。它的主要特点是可以轻松地添加调试日志语句同时在不需要调试时可以轻松地禁用它们以避免在生产环境中对性能产生影响。我们在一些有名的三方库如socket.io就能看到debug库的身影说明它确实很常用。 Debug 库介绍
一个模仿Node.js核心调试技术的小型JavaScript调试实用程序。适用于Node.js和web浏览器。Debug库 是一个小巧但功能强大的 JavaScript 调试工具库可以帮助开发人员更轻松地进行调试以便更快地发现和修复问题。它的主要特点是可以轻松地添加调试日志语句同时在不需要调试时可以轻松地禁用它们以避免在生产环境中对性能产生影响。
debug库的github:GitHub - debug-js/debug: A tiny JavaScript debugging utility modelled after Node.js cores debugging technique. Works in Node.js and web browsers
它提供了一种比console.log()更方便的打印调试信息的方式。Debug 库包含了一个全局函数 debug()通过调用这个函数可以创建一个具有指定名称的调试器对象。调试器对象提供了一些方法可以用于在控制台中输出调试信息。同时可以使用环境变量来控制调试器对象是否启用。
我移植发布的鸿蒙的debug库:
参见鸿蒙三方中心库OpenHarmony三方库中心仓
简单使用
下面是一个 Debug 库的代码示例
let debug require(debug)(myapp);function myFunction() {debug(This is a debug message);debug(This is a debug message%d,2);
}myFunction();
首先使用 require() 函数将 Debug 库引入到我们的代码中并调用 debug() 函数创建一个名为 myapp 的调试器对象。然后我们定义一个名为 myFunction() 的函数在这个函数中调用调试器对象的 debug() 方法输出一条调试信息。
如果我们想要在控制台中看到这些调试信息我们需要设置环境变量 DEBUG 的值为 myapp这可以在终端中使用命令行来完成
$ DEBUGmyapp node myscript.js
这会启动名为 myapp 的调试器对象并在控制台中输出所有与该名称相关联的调试信息。如果我们要禁用调试信息只需要将 DEBUG 环境变量的值设置为空即可
$ DEBUG node myscript.js
这将禁用所有调试信息避免在生产环境中对性能产生影响。
此外js-debug库还支持设置不同的命名空间来过滤特定的调试信息。例如
const debug1 require(debug)(app:debug1)const debug2 require(debug)(app:debug2)
然后在终端输入DEBUGapp:* node app.js则会输出形如app:debug1 This is debug1 message!的信息。
上述示例是基于nodejs的命令行环境下的。
鸿蒙下的使用举例
import debugModule from yyz116/debug // debug()const debug debugModule(MyApp:client); debug.enable() //为空则是禁用日志输出
debug.enable(MyApp:client); //开启日志。注意默认日志输出是关闭的开启调试日志需要代码里调用enbale接口。不同于原库的环境变量方式debug(booting %s, Hello MyTest);
debug(this is a %d test, 5);debug(hello);
源码简析
源码很简单代码量也不多很容易看懂。首先看 index.js
if (typeof process ! undefined process.type renderer) {
module.exports require(./browser.js);
} else {
module.exports require(./node.js);
}说明它同时支持浏览器环境和nodejs环境通过if判断process对象是否存在来决定是使用哪个文件的导出。
由于我们是要移植到鸿蒙系统中的于是node的那版就不看了直接看browser.js源码
/* eslint-env browser *//*** This is the web browser implementation of debug().*/exports.formatArgs formatArgs;
exports.save save;
exports.load load;
exports.useColors useColors;
exports.storage localstorage();
exports.destroy (() {let warned false;return () {if (!warned) {warned true;console.warn(Instance method debug.destroy() is deprecated and no longer does anything. It will be removed in the next major version of debug.);}};
})();/*** Colors.*/exports.colors [#0000CC,#0000FF,......#FFCC33
];/*** Currently only WebKit-based Web Inspectors, Firefox v31,* and the Firebug extension (any Firefox version) are known* to support %c CSS customizations.** TODO: add a localStorage variable to explicitly enable/disable colors*/// eslint-disable-next-line complexity
function useColors() {// NB: In an Electron preload script, document will be defined but not fully// initialized. Since we know were in Chrome, well just detect this case// explicitlyif (typeof window ! undefined window.process (window.process.type renderer || window.process.__nwjs)) {return true;}// Internet Explorer and Edge do not support colors.if (typeof navigator ! undefined navigator.userAgent navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d)/)) {return false;}// Is webkit? http://stackoverflow.com/a/16459606/376773// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632return (typeof document ! undefined document.documentElement document.documentElement.style document.documentElement.style.WebkitAppearance) ||// Is firebug? http://stackoverflow.com/a/398120/376773(typeof window ! undefined window.console (window.console.firebug || (window.console.exception window.console.table))) ||// Is firefox v31?// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages(typeof navigator ! undefined navigator.userAgent navigator.userAgent.toLowerCase().match(/firefox\/(\d)/) parseInt(RegExp.$1, 10) 31) ||// Double check webkit in userAgent just in case we are in a worker(typeof navigator ! undefined navigator.userAgent navigator.userAgent.toLowerCase().match(/applewebkit\/(\d)/));
}/*** Colorize log arguments if enabled.** api public*/function formatArgs(args) {args[0] (this.useColors ? %c : ) this.namespace (this.useColors ? %c : ) args[0] (this.useColors ? %c : ) module.exports.humanize(this.diff);if (!this.useColors) {return;}const c color: this.color;args.splice(1, 0, c, color: inherit);// The final %c is somewhat tricky, because there could be other// arguments passed either before or after the %c, so we need to// figure out the correct index to insert the CSS intolet index 0;let lastC 0;args[0].replace(/%[a-zA-Z%]/g, match {if (match %%) {return;}index;if (match %c) {// We only are interested in the *last* %c// (the user may have provided their own)lastC index;}});args.splice(lastC, 0, c);
}/*** Invokes console.debug() when available.* No-op when console.debug is not a function.* If console.debug is not available, falls back* to console.log.** api public*/
exports.log console.debug || console.log || (() {});/*** Save namespaces.** param {String} namespaces* api private*/
function save(namespaces) {try {if (namespaces) {exports.storage.setItem(debug, namespaces);} else {exports.storage.removeItem(debug);}} catch (error) {// Swallow// XXX (Qix-) should we be logging these?}
}/*** Load namespaces.** return {String} returns the previously persisted debug modes* api private*/
function load() {let r;try {r exports.storage.getItem(debug);} catch (error) {// Swallow// XXX (Qix-) should we be logging these?}// If debug isnt set in LS, and were in Electron, try to load $DEBUGif (!r typeof process ! undefined env in process) {r process.env.DEBUG;}return r;
}/*** Localstorage attempts to return the localstorage.** This is necessary because safari throws* when a user disables cookies/localstorage* and you attempt to access it.** return {LocalStorage}* api private*/function localstorage() {try {// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context// The Browser also has localStorage in the global context.return localStorage;} catch (error) {// Swallow// XXX (Qix-) should we be logging these?}
}module.exports require(./common)(exports);const {formatters} module.exports;/*** Map %j to JSON.stringify(), since no Web Inspectors do that by default.*/formatters.j function (v) {try {return JSON.stringify(v);} catch (error) {return [UnexpectedJSONParseError]: error.message;}
}; 代码量很少注定移植是很简单的事情。这个文件中就定义了一系列的函数挂载到了node的exports对象上面。至于功能是干什么的简单的不用再说了。直接看这里
module.exports require(./common)(exports);const {formatters} module.exports;
这里才是关键核心的类和对象的实现是在common.js文件里的。
//common.js
/*** This is the common logic for both the Node.js and web browser* implementations of debug().*/function setup(env) {createDebug.debug createDebug;createDebug.default createDebug;createDebug.coerce coerce;createDebug.disable disable;createDebug.enable enable;createDebug.enabled enabled;createDebug.humanize require(ms);createDebug.destroy destroy;Object.keys(env).forEach(key {createDebug[key] env[key];});/*** The currently active debug mode names, and names to skip.*/createDebug.names [];createDebug.skips [];/*** Map of special %n handling functions, for the debug format argument.** Valid key names are a single, lower or upper-case letter, i.e. n and N.*/createDebug.formatters {};/*** Selects a color for a debug namespace* param {String} namespace The namespace string for the debug instance to be colored* return {Number|String} An ANSI color code for the given namespace* api private*/function selectColor(namespace) {let hash 0;for (let i 0; i namespace.length; i) {hash ((hash 5) - hash) namespace.charCodeAt(i);hash | 0; // Convert to 32bit integer}return createDebug.colors[Math.abs(hash) % createDebug.colors.length];}createDebug.selectColor selectColor;/*** Create a debugger with the given namespace.** param {String} namespace* return {Function}* api public*/function createDebug(namespace) {let prevTime;let enableOverride null;let namespacesCache;let enabledCache;function debug(...args) {// Disabled?if (!debug.enabled) {return;}const self debug;// Set diff timestampconst curr Number(new Date());const ms curr - (prevTime || curr);self.diff ms;self.prev prevTime;self.curr curr;prevTime curr;args[0] createDebug.coerce(args[0]);if (typeof args[0] ! string) {// Anything else lets inspect with %Oargs.unshift(%O);}// Apply any formatters transformationslet index 0;args[0] args[0].replace(/%([a-zA-Z%])/g, (match, format) {// If we encounter an escaped % then dont increase the array indexif (match %%) {return %;}index;const formatter createDebug.formatters[format];if (typeof formatter function) {const val args[index];match formatter.call(self, val);// Now we need to remove args[index] since its inlined in the formatargs.splice(index, 1);index--;}return match;});// Apply env-specific formatting (colors, etc.)createDebug.formatArgs.call(self, args);const logFn self.log || createDebug.log;logFn.apply(self, args);}debug.namespace namespace;debug.useColors createDebug.useColors();debug.color createDebug.selectColor(namespace);debug.extend extend;debug.destroy createDebug.destroy; // XXX Temporary. Will be removed in the next major release.Object.defineProperty(debug, enabled, {enumerable: true,configurable: false,get: () {if (enableOverride ! null) {return enableOverride;}if (namespacesCache ! createDebug.namespaces) {namespacesCache createDebug.namespaces;enabledCache createDebug.enabled(namespace);}return enabledCache;},set: v {enableOverride v;}});// Env-specific initialization logic for debug instancesif (typeof createDebug.init function) {createDebug.init(debug);}return debug;}function extend(namespace, delimiter) {const newDebug createDebug(this.namespace (typeof delimiter undefined ? : : delimiter) namespace);newDebug.log this.log;return newDebug;}/*** Enables a debug mode by namespaces. This can include modes* separated by a colon and wildcards.** param {String} namespaces* api public*/function enable(namespaces) {createDebug.save(namespaces);createDebug.namespaces namespaces;createDebug.names [];createDebug.skips [];let i;const split (typeof namespaces string ? namespaces : ).split(/[\s,]/);const len split.length;for (i 0; i len; i) {if (!split[i]) {// ignore empty stringscontinue;}namespaces split[i].replace(/\*/g, .*?);if (namespaces[0] -) {createDebug.skips.push(new RegExp(^ namespaces.slice(1) $));} else {createDebug.names.push(new RegExp(^ namespaces $));}}}/*** Disable debug output.** return {String} namespaces* api public*/function disable() {const namespaces [...createDebug.names.map(toNamespace),...createDebug.skips.map(toNamespace).map(namespace - namespace)].join(,);createDebug.enable();return namespaces;}/*** Returns true if the given mode name is enabled, false otherwise.** param {String} name* return {Boolean}* api public*/function enabled(name) {if (name[name.length - 1] *) {return true;}let i;let len;for (i 0, len createDebug.skips.length; i len; i) {if (createDebug.skips[i].test(name)) {return false;}}for (i 0, len createDebug.names.length; i len; i) {if (createDebug.names[i].test(name)) {return true;}}return false;}/*** Convert regexp to namespace** param {RegExp} regxep* return {String} namespace* api private*/function toNamespace(regexp) {return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, *);}/*** Coerce val.** param {Mixed} val* return {Mixed}* api private*/function coerce(val) {if (val instanceof Error) {return val.stack || val.message;}return val;}/*** XXX DO NOT USE. This is a temporary stub function.* XXX It WILL be removed in the next major release.*/function destroy() {console.warn(Instance method debug.destroy() is deprecated and no longer does anything. It will be removed in the next major version of debug.);}createDebug.enable(createDebug.load());return createDebug;
}module.exports setup;
代码也比较简单。主要看三个函数setup, createDebug函数和其内部的debug函数。
function setup(env) {createDebug.debug createDebug;createDebug.default createDebug;createDebug.coerce coerce;createDebug.disable disable;createDebug.enable enable;createDebug.enabled enabled;createDebug.humanize require(ms);createDebug.destroy destroy;Object.keys(env).forEach(key {createDebug[key] env[key];});/*** The currently active debug mode names, and names to skip.*/createDebug.names [];createDebug.skips [];
.....
}
JavaScript中的函数是一种特殊的对象因此可以像普通对象一样被扩展属性和方法。这种行为是由JavaScript中的函数对象的特性所决定的。
在JavaScript中函数也是一种对象也可以拥有属性和方法。当我们定义一个函数时实际上是在内存中创建了一个函数对象并且该函数对象可以拥有自己的属性和方法。这意味着我们可以在函数外部对其进行扩展添加新的属性和方法或者修改已有的属性和方法。上述代码是给createDebug的函数扩展了一些属性和方法。这些属性会成为函数对象的一部分并且可以被访问和使用。
JavaScript中函数对象的这种特性为我们提供了一定的灵活性使得可以在函数对象上添加额外的信息或者功能这在某些场景下是非常有用。
需要注意的是尽管函数对象可以被扩展属性和方法但这并不意味着推荐在实际开发中频繁地对函数对象进行扩展。通常情况下函数应当专注于其原本的用途和功能并遵循单一职责原则。
另外需要注意的是createDebug是一个函数而不是一个类。在JavaScript中函数和类都可以用来创建对象但它们的语法和用法有所不同。createDebug函数被用作一个工厂函数用于创建调试器对象。它返回一个包含内部状态和方法的对象而不是一个类的实例。通过调用createDebug函数我们可以得到一个具有特定功能和状态的调试器对象。在这种情况下并没有使用JavaScript中的类class关键字来定义createDebug而是直接定义了一个函数来实现相同的功能。
js在es6规范之前没有class的定义但可以通过函数来实现类对象的行为这种方式通常称为构造函数和原型继承。通过构造函数和原型继承的方式模拟类和实现类对象的行为。如下面这一小段代码
// 通过构造函数定义类
function Person(name, age) {this.name name;this.age age;
}// 在原型上定义方法
Person.prototype.sayHello function() {console.log(Hello, my name is this.name);
};// 创建类的实例
let person1 new Person(Alice, 25);
let person2 new Person(Bob, 30);// 调用实例方法
person1.sayHello(); // 输出Hello, my name is Alice
person2.sayHello(); // 输出Hello, my name is Bob
setup函数分析
在给定环境env下通过调用setup(env)来设置和返回一个名为createDebug的函数。createDebug函数内部定义了一个名为debug的内部函数以及一系列与debug相关的属性和方法。这种将函数作为返回值的写法是一种高阶函数的应用通过内部函数的闭包特性我们可以在外部函数中定义私有的属性和方法并返回一个可供外部使用的函数。
接下来让我们解释一下debug函数。debug是在createDebug函数内部定义的一个函数用于创建一个带有给定namespace的调试器。它还包含一些内部状态和逻辑用于控制调试器的输出行为。在这个代码中通过闭包的方式实现了一些私有状态的存储和访问。
这种嵌套定义的写法充分利用了JavaScript中闭包的特性可以有效地封装内部状态并提供一个对外的接口。这种写法为代码的模块化和可维护性提供了一定的帮助。
简单来说createDebug函数是用于设置调试功能的工厂函数而debug函数是用于创建特定命名空间的调试器的函数。整个代码的结构是基于函数式编程和闭包的思想通过高阶函数的方式来组织代码和管理状态使得逻辑清晰灵活性较高。
d.ts声明文件
为了便于在TypeScript 中能够使用js的代码d.ts声明文件必不可少。以便在开发过程中进行类型检查、自动补全、跳转到定义等操作。
.d.ts声明文件Declaration Files是用来描述一个 JavaScript 模块、库或者框架的类型信息以及代码结构的文件。它的作用是为了在开发阶段能够让 TypeScript 或者其他支持类型检查的工具能够了解 JavaScript 代码中的类型信息和结构。
为什么需要.d.ts声明文件主要是因为 JavaScript 是一种动态类型语言而 TypeScript 是一种静态类型的超集。为了在 TypeScript 中能够对 JavaScript 模块进行类型检查和更好的代码提示需要声明文件来提供类型信息。很多优秀的 JavaScript 模块和库都提供了官方或者社区维护的声明文件以便让 TypeScript 或其他支持类型检查的工具能够更好地理解和协助开发者使用这些模块。
//index.d.ts
declare var debug: debug.Debug { debug: debug.Debug; default: debug.Debug };
export debug;declare namespace debug {interface Debug {(namespace: string): Debugger;coerce: (val: any) any;disable: () string;enable: (namespaces: string) void;enabled: (namespaces: string) boolean;formatArgs: (this: Debugger, args: any[]) void;log: (...args: any[]) any;selectColor: (namespace: string) string | number;names: RegExp[];skips: RegExp[];formatters: Formatters;inspectOpts?: {hideDate?: boolean | number | null;colors?: boolean | number | null;depth?: boolean | number | null;showHidden?: boolean | number | null;};}type IDebug Debug;interface Formatters {[formatter: string]: (v: any) string;}type IDebugger Debugger;interface Debugger {(formatter: any, ...args: any[]): void;color: string;diff: number;enabled: boolean;enable: (namespaces: string) void;log: (...args: any[]) any;namespace: string;destroy: () boolean;extend: (namespace: string, delimiter?: string) Debugger;}
}interface Debug是对应 createDebug函数的接口interface Debugger是对应内部的debug函数的接口声明。因为interface Debug 接口中包括了coerce、disable、enable、enabled 等方法这些方法与 createDebug 函数的行为和特性相符。
harmonyOS平台移植
搞懂了源码移植就简单了。下面分享下在harmonyOS平台下的移植。
新建一harmony.js的文件仿照实现以下内容
/* eslint-env harmonyos */
/*** This is the harmonyos implementation of debug().* author:yangyongzhen* blog: blog.scdn.net/qq8864*/let exp {}exp.useColors useColors;
exp.formatArgs formatArgs;
exp.save save;
exp.load load;exp.storage (function () {var data {};return {setItem: function (key, item) { data[key] item; },getItem: function (key) { return data[key]; },removeItem: function (key) { delete data[key]; },};
})();exp.destroy (() {let warned false;return () {if (!warned) {warned true;console.warn(Instance method debug.destroy() is deprecated and no longer does anything. It will be removed in the next major version of debug.);}};
})();/*** Invokes console.debug() when available.* No-op when console.debug is not a function.* If console.debug is not available, falls back* to console.log.** api public*/
exp.log console.log || (() {});/*** Colors.*/exp.colors [#0000CC,#0000FF,......#FFCC33
];/*** Currently only WebKit-based Web Inspectors, Firefox v31,* and the Firebug extension (any Firefox version) are known* to support %c CSS customizations.** TODO: add a localStorage variable to explicitly enable/disable colors*/// eslint-disable-next-line complexity
function useColors() {// NB: In an Electron preload script, document will be defined but not fully// initialized. Since we know were in Chrome, well just detect this case// explicitlyreturn false;
}/*** Colorize log arguments if enabled.** api public*/function formatArgs(args) {args[0] (this.useColors ? %c : ) this.namespace (this.useColors ? %c : ) args[0] (this.useColors ? %c : );if (!this.useColors) {return;}const c color: this.color;args.splice(1, 0, c, color: inherit);// The final %c is somewhat tricky, because there could be other// arguments passed either before or after the %c, so we need to// figure out the correct index to insert the CSS intolet index 0;let lastC 0;args[0].replace(/%[a-zA-Z%]/g, match {if (match %%) {return;}index;if (match %c) {// We only are interested in the *last* %c// (the user may have provided their own)lastC index;}});args.splice(lastC, 0, c);
}/*** Save namespaces.** param {String} namespaces* api private*/
function save(namespaces) {try {if (namespaces) {exp.storage.setItem(debug, namespaces);} else {exp.storage.removeItem(debug);}} catch (error) {// Swallow// XXX (Qix-) should we be logging these?}
}/*** Load namespaces.** return {String} returns the previously persisted debug modes* api private*/
function load() {let r;try {r exp.storage.getItem(debug);} catch (error) {// Swallow// XXX (Qix-) should we be logging these?}// If debug isnt set in LS, and were in Electron, try to load $DEBUGif (!r typeof process ! undefined env in process) {r process.env.DEBUG;}return r;
}import {setup} from ./commonvar outs setup(exp);const {formatters} outs;/*** Map %j to JSON.stringify(), since no Web Inspectors do that by default.*/formatters.j function (v) {try {return JSON.stringify(v);} catch (error) {return [UnexpectedJSONParseError]: error.message;}
};export {outs};
发布到中心仓
写完单元测试用例测试没问题发布的过程很简单。参见HarmonyOS 鸿蒙应用开发(九、还是蓝海如何贡献第三方库)_鸿蒙开发第三方库社区-CSDN博客
写在最后
如果你觉得这篇内容对你还蛮有帮助我想邀请你帮我三个小忙点赞转发有你们的 『点赞和评论』才是我创造的动力。关注博主同时可以期待后续文章ing不定期分享原创知识。想要获取更多完整鸿蒙最新VIP学习资料请关注猫哥公众号【猫青年】回复“鸿蒙”获取 其他资源
分享7个实用又高效的 Node.js 工具库
npm--debug模块_npm debug-CSDN博客
GitHub - debug-js/debug: A tiny JavaScript debugging utility modelled after Node.js cores debugging technique. Works in Node.js and web browsers