当前位置: 首页 > news >正文

Node.js的debug模块源码分析及在harmonyOS平台移植

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 core's 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",这可以在终端中使用命令行来完成:

$ DEBUG=myapp node myscript.js

这会启动名为 "myapp" 的调试器对象,并在控制台中输出所有与该名称相关联的调试信息。如果我们要禁用调试信息,只需要将 DEBUG 环境变量的值设置为空即可:

$ DEBUG= node myscript.js

这将禁用所有调试信息,避免在生产环境中对性能产生影响。

此外,js-debug库还支持设置不同的命名空间来过滤特定的调试信息。例如:

const debug1 = require('debug')('app:debug1')const debug2 = require('debug')('app:debug2')

然后在终端输入DEBUG=app:* 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 we're in Chrome, we'll 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 isn't set in LS, and we're 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 let's 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 don't 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 it's inlined in the `format`args.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 接口中包括了coercedisableenableenabled 等方法,这些方法与 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 we're in Chrome, we'll 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 isn't set in LS, and we're in Electron, try to load $DEBUGif (!r && typeof process !== 'undefined' && 'env' in process) {r = process.env.DEBUG;}return r;
}import {setup} from './common'var 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 core's debugging technique. Works in Node.js and web browsers

相关文章:

Node.js的debug模块源码分析及在harmonyOS平台移植

Debug库 是一个小巧但功能强大的 JavaScript 调试工具库&#xff0c;可以帮助开发人员更轻松地进行调试&#xff0c;以便更快地发现和修复问题。它的主要特点是可以轻松地添加调试日志语句&#xff0c;同时在不需要调试时可以轻松地禁用它们&#xff0c;以避免在生产环境中对性…...

【Crypto | CTF】BUUCTF RSA2

天命&#xff1a;密码学越来越难了&#xff0c;看别人笔记都不知道写啥 天命&#xff1a;莫慌&#xff0c;虽然我不会推演法&#xff0c;但我可以用归纳法 虽然我不知道解题的推演&#xff0c;但我可以背公式啊哈哈哈 虽然我不会这题&#xff0c;但是我也能做出来 公式我不知…...

单片机学习笔记---红外遥控红外遥控电机调速(完结篇)

目录 低电平触发中断和下降沿触发中断的区别 红外遥控 Int0.c Int.h Timer0.c Timer0.h IR.c IR.h main.c 红外遥控电机调速 Timer1.c Timer.h Motor.c Motor.h main.c 上一节讲了红外发送和接收的工作原理&#xff0c;这一节开始代码演示&#xff01; 提前说…...

Linux第62步_备份移植好的所有的文件和文件夹

1、备份“my-tfa”目录下所有的文件和文件夹 1)、打开终端 输入“ls回车”&#xff0c;列出当前目录下所有的文件和文件夹 输入“cd linux回车”&#xff0c;切换“linux”目录下 输入“ls回车”&#xff0c;列出当前目录下所有的文件和文件夹 输入“cd atk-mp1/回车”&am…...

【xss跨站漏洞】xss漏洞前置知识点整理

xss漏洞成因 xss漏洞是一种前端javascript产生的漏洞。 我们网站基本都是会用到javascript编写一些东西&#xff0c;浏览器也能直接识别javascript。 如果有一个地方能够输入文字&#xff0c;但是他又没有过滤你的输入&#xff0c;那么自己或者他人看到你输入的javascript代…...

mac下mysql 常用命令

mysql启动命令 在Mac OS X启动和停止MySQL服务的命令&#xff0c; 启动MySQL服务 sudo /usr/local/mysql/support-files/mysql.server start 停止MySQL服务 sudo /usr/local/mysql/support-files/mysql.server stop 重启MySQL服务 sudo /usr/local/mysql/support-files/mys…...

2.21号qt

1.QMainWindow中常用的类 继承于QMainWindow类&#xff0c;原因该类提供了QWidget没有提供的成员函数。 菜单栏、工具栏、状态栏、浮动窗口&#xff08;铆接部件&#xff09;、核心部件 1.1 菜单栏 QMenuBar //创建菜单栏 QMenuBar 最多只能有一个 QMenuBar *mbar menu…...

什么是MVVM?MVC、MVP与MVVM模式的区别?

MVVM&#xff08;Model-View-ViewModel&#xff09;是一种软件架构模式&#xff0c;用于将用户界面&#xff08;View&#xff09;与业务逻辑&#xff08;Model&#xff09;分离&#xff0c;并通过ViewModel来连接两者。MVVM的目标是实现可测试性、可维护性和可复用性。 MVC&am…...

ElementUI组件的安装和使用

Element UI 是一款基于 Vue 2.0 的桌面端组件库&#xff0c;主要用于快速构建网站的前端部分。它提供了丰富的组件&#xff0c;如按钮、输入框、表格、标签页等&#xff0c;以及一些布局元素&#xff0c;如布局容器、分割线等。Element UI 的设计风格简洁&#xff0c;易于上手&…...

Laravel01 课程介绍以及Laravel环境搭建

Laravel01 课程介绍 1. Laravel2. mac开发环境搭建(通过Homebrew)3. 创建一个项目 1. Laravel 公司中面临着PHP项目与Java项目并行&#xff0c;所以需要我写PHP的项目&#xff0c;公司用的框架就是Laravel&#xff0c;所以在B站上找了一门课学习。 Laravel中文文档地址 https…...

面试redis篇-03缓存击穿

原理 缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮 解决方案一:互斥锁 解决方案二:逻辑过期 提问与回答 面试官 :什么是缓存击穿 ? 怎么解决 ? 回答: 缓存击穿的意思…...

k8s容器以及基础设施优化

1.硬件系统选型&#xff1a;宿主机通用配置16c/32GB/4网卡队列 2.os优化&#xff1a;单机支持百万tcp并发&#xff0c;/etc/sysctl.conf,/etc/security/limits.conf 3.k8s&容器层优化&#xff1a;性能优化initContainer 4.kube-dns优化&#xff1a;增大--cache-size,设置…...

蓝桥杯备赛系列——倒计时50天!

蓝桥杯备赛系列 倒计时50天&#xff01; 前缀和和差分 知识点 **前缀和数组&#xff1a;**假设原数组用a[i]表示&#xff0c;前缀和数组用sum[i]表示&#xff0c;那么sum[i]表示的是原数组前i项之和&#xff0c;注意一般用前缀和数组时&#xff0c;原数组a[i]的有效下标是从…...

jenkins配置ssh的时候测试连接出现Algorithm negotiation fail

背景&#xff1a;当jenkins升级后&#xff0c;同时ssh插件也升级&#xff0c;测试ssh连接的时候 出现的问题&#xff1a; com.jcraft.jsch.JSchAlgoNegoFailException: Algorithm negotiation fail: algorithmName"server_host_key" jschProposal"ecdsa-sha2-n…...

思维模型整合

思维模型整合 4P--- 4C思考模型能力圈模型 4P— 4C思考模型 在竞争激烈的今天&#xff0c;每个赛道都有众多可以为客户提供相同价值的对手&#xff0c;而赛道中的佼佼者之所以能打败大部分人&#xff0c;可能并不是他们能比别人更能讨好大众&#xff0c;而是因为在这个赛道它有…...

代理模式笔记

代理模式 代理模式代理模式的应用场景先理解什么是代理&#xff0c;再理解动静态举例举例所用代码 动静态的区别静态代理动态代理 动态代理的优点代理模式与装饰者模式的区别 代理模式 代理模式在设计模式中是7种结构型模式中的一种&#xff0c;而代理模式有分动态代理&#x…...

手机中有哪些逆向进化的功能

手机中有哪些逆向进化的功能&#xff1f;逆向进化是指明明很优秀的很方便的功能&#xff0c;却因为成本或者其他工业原因莫名其妙地给取消了。 逆向进化1&#xff1a;可拆卸电池-变为不可拆卸电池。 智能手机为了追求轻薄等原因&#xff0c;所以移除了可拆卸电池功能。将电池…...

LeetCode24.两两交换链表中的节点

参考链接&#xff1a;代码随想录&#xff1a;LeetCode24.两两交换链表中的节点 我这里使用了3个变量进行暴力交换&#xff0c;简单快捷&#xff01;但是有一点想不明白&#xff0c;return这里只能写dh->next,写返回head就结果不对了&#xff01;但是后面又想明白了&#xff…...

Eureka注册中心(黑马学习笔记)

Eureka注册中心 假如我们的服务提供者user-service部署了多个实例&#xff0c;如图&#xff1a; 大家思考几个问题&#xff1a; order-service在发起远程调用的时候&#xff0c;该如何得知user-service实例的ip地址和端口&#xff1f; 有多个user-service实例地址&#xff0c…...

unity-firebase-Analytics分析库对接后数据不显示原因,及最终解决方法

自己记录一下unity对接了 FirebaseAnalytics.unitypackage&#xff08;基于 firebase_unity_sdk_10.3.0 版本&#xff09; 库后&#xff0c;数据不显示的原因及最终显示解决方法&#xff1a; 1. 代码问题&#xff08;有可能是代码写的问题&#xff0c;正确的代码如下&#xff…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准

城市路内停车管理常因行道树遮挡、高位设备盲区等问题&#xff0c;导致车牌识别率低、逃费率高&#xff0c;传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法&#xff0c;正成为破局关键。该设备安装于车位侧方0.5-0.7米高度&#xff0c;直接规避树枝遮…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

Appium下载安装配置保姆教程(图文详解)

目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...

大数据驱动企业决策智能化的路径与实践

&#x1f4dd;个人主页&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、引言&#xff1a;数据驱动的企业竞争力重构 在这个瞬息万变的商业时代&#xff0c;“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...

Vue3学习(接口,泛型,自定义类型,v-for,props)

一&#xff0c;前言 继续学习 二&#xff0c;TS接口泛型自定义类型 1.接口 TypeScript 接口&#xff08;Interface&#xff09;是一种定义对象形状的强大工具&#xff0c;它可以描述对象必须包含的属性、方法和它们的类型。接口不会被编译成 JavaScript 代码&#xff0c;仅…...