来吧,让你的 console.log() 输出更漂亮一点

3,160 阅读2分钟

原作者:Ben Nadel

翻译/加工:零和幺

www.bennadel.com/blog/3941-s…

如果你看过 MDN 上关于 Console 的文档 就会知道 Console 对象其实有很多有意思的功能,像字符串替换(string substitution)CSS 样式(CSS styling) 等等。这些功能工作的前提是:用一个特殊的标记(token)作为 console.log() 方法的第一个参数,然后用一系列紧随其后的参数来修饰第一个参数。

举个例子,我们可以使用 %o 来插入一个对象:

console.log( "My document: %o", document )

也可以用 %c 给同一个 log 声明修改样式:

console.log( "%cMy document: %o", "color: red ;", document )

注意:第二个参数和第三个参数分别用来修饰 %c%o。在这个例子中,color: red; 影响 %c 后面的所有东西。

说了这么多,我们来一个有趣的例子,看看它能干点什么:

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<title>
		Styling console.log() Output Formatting With CSS
	</title>
</head>
<body>

	<h1>
		Styling console.log() Output Formatting With CSS
	</h1>

	<script type="text/javascript">

		console.log(
			"%cAltering the text color",
			"color: fuchsia"
		);

		console.log(
			"%cAltering the text experience",
			"background-color: fuchsia ; color: white ; font-weight: bold ; " +
			"font-size: 20px ; font-style: italic ; text-decoration: underline ; " +
			"font-family: 'american typewriter' ; text-shadow: 1px 1px 3px black ;"
		);

		// NOTE: In this demo, inline-block isn't needed in Chrome; but, it is needed in
		// Firefox or the block properties, like padding, don't work. Trying to use
		// "block" will work in Chrome; but, will go full-width in Firefox.
		console.log(
			"%cAltering the box display",
			"display: inline-block ; border: 3px solid red ; border-radius: 7px ; " +
			"padding: 10px ; margin: 20px ;"
		);

		// NOTE: Background-images work in Chrome, but not in Firefox. Also, at least
		// locally, only fully qualified URLs seems to work (but that may have been
		// something I was messing up).
		// --
		// Also, it doesn't look like width/height work on box-model. As such, I am using
		// padding to push-out the box size.
		console.log(
			"%cBackground image",
			"display: inline-block ; background-image: url( 'https://bennadel.github.io/JavaScript-Demos/demos/console-log-css/rock.png' ) ; " +
			"background-size: cover ; padding: 10px 175px 158px 10px ; " +
			"border: 2px solid black ; font-size: 11px ; line-height: 11px ; " +
			"font-family: monospace ;"
		);

		// The same CSS styling can be used with many of the other console methods, like
		// the .group() method.
		console.group(
			"%cGrouped Output",
			"background-color: #e0005a ; color: #ffffff ; font-weight: bold ; padding: 4px ;"
		);
		console.log( "Groups are cool for grouped stuff." );
		console.log( "Totes magotes" );
		console.groupEnd();

	</script>

</body>
</html>

我们尝试将许多 CSS 样式放在 console.log() 中,当我们在 Chrome 浏览器中运行上面的代码后,可以在开发者工具的控制台中看到如下输出:

Firefox 也支持这些,但是好像不支持背景图。

是不是非常帅?但现在的问题是 console.log() 中的代码太冗余了。我们怎么才能将这些功能更好的封装起来 —— 隐藏掉复杂的 CSS 样式并且仍然灵活地提供 console.log() 的功能呢?幸好 JavaScript 是一个强大的语言,封装起来并不十分复杂。

为了实现这个想法,我创造了 echo 对象用来代替 console 对象。也就是说,会有一些类似 console 的方法:

  • echo.log()
  • echo.error()
  • echo.warn()
  • echo.trace()
  • echo.group()
  • echo.groupEnd()

就像 console 方法一样,这些 echo 方法将是可变的 —— 它接受动态数量的参数。在这里有一个巨大的不同是 echo 对象将会提供格式化方法,该方法可以用来格式化某个输入:

  • echo.asAlert()
  • echo.asWarning()
echo.log( echo.asAlert( "Oh no!" ), "Something went wrong!")

上面的代码会格式化 Oh no 部分,让其具有 “alert 样式”,同时让 "Something wen wrong!" 这句输入保持普通文本样式。

让我们看看这是如何实现的:

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<title>
		Styling console.log() Output Formatting With CSS
	</title>
</head>
<body>

	<h1>
		Styling console.log() Output Formatting With CSS
	</h1>

	<script type="text/javascript">

		// I provide a logging API that has some special sauce for formatting.
		var echo = (function() {

			var queue = [];
			var ECHO_TOKEN = {};
			var RESET_INPUT = "%c ";
			var RESET_CSS = "";

			// Attach formatting utility method.
			function alertFormatting( value ) {

				queue.push({
					value: value,
					css: "display: inline-block ; background-color: #e0005a ; color: #ffffff ; font-weight: bold ; padding: 3px 7px 3px 7px ; border-radius: 3px 3px 3px 3px ;"
				});

				return( ECHO_TOKEN );

			}

			// Attach formatting utility method.
			function warningFormatting( value ) {

				queue.push({
					value: value,
					css: "display: inline-block ; background-color: gold ; color: black ; font-weight: bold ; padding: 3px 7px 3px 7px ; border-radius: 3px 3px 3px 3px ;"
				});

				return( ECHO_TOKEN );

			}

			// I provide an echo-based proxy to the given Console Function. This uses an
			// internal queue to aggregate values before calling the given Console
			// Function with the desired formatting.
			function using( consoleFunction ) {

				function consoleFunctionProxy() {

					// As we loop over the arguments, we're going to aggregate a set of
					// inputs and modifiers. The Inputs will ultimately be collapsed down
					// into a single string that acts as the first console.log parameter
					// while the modifiers are then SPREAD into console.log as 2...N.
					// --
					// NOTE: After each input/modifier pair, I'm adding a RESET pairing.
					// This implicitly resets the CSS after every formatted pairing.
					var inputs = [];
					var modifiers = [];

					for ( var i = 0 ; i < arguments.length ; i++ ) {

						// When the formatting utility methods are called, they return
						// a special token. This indicates that we should pull the
						// corresponding value out of the QUEUE instead of trying to
						// output the given argument directly.
						if ( arguments[ i ] === ECHO_TOKEN ) {

							var item = queue.shift();

							inputs.push( ( "%c" + item.value ), RESET_INPUT );
							modifiers.push( item.css, RESET_CSS );

						// For every other argument type, output the value directly.
						} else {

							var arg = arguments[ i ];

							if (
								( typeof( arg ) === "object" ) ||
								( typeof( arg ) === "function" )
								) {

								inputs.push( "%o", RESET_INPUT );
								modifiers.push( arg, RESET_CSS );

							} else {

								inputs.push( ( "%c" + arg ), RESET_INPUT );
								modifiers.push( RESET_CSS, RESET_CSS );

							}

						}

					}

					consoleFunction( inputs.join( "" ), ...modifiers );

					// Once we output the aggregated value, reset the queue. This should have
					// already been emptied by the .shift() calls; but the explicit reset
					// here acts as both a marker of intention as well as a fail-safe.
					queue = [];

				}

				return( consoleFunctionProxy );

			}

			return({
				// Console(ish) functions.
				log: using( console.log ),
				warn: using( console.warn ),
				error: using( console.error ),
				trace: using( console.trace ),
				group: using( console.group ),
				groupEnd: using( console.groupEnd ),

				// Formatting functions.
				asAlert: alertFormatting,
				asWarning: warningFormatting
			});

		})();

		// --------------------------------------------------------------------------- //
		// --------------------------------------------------------------------------- //

		// Let's try mixing a bunch of values together.
		echo.log(
			echo.asAlert( "This is great!" ),
			"Right!",
			{ "i am": "an object" },
			null,
			[ "an array" ],
			function amAFunction() {},
			echo.asWarning( "I mean, right?!?!?!" )
		);

		echo.log();

		// Let's try a more sensible example.
		echo.group( echo.asWarning( "Arnold Schwarzenegger Movies" ) );
		echo.log( "The Running Man" );
		echo.log( "Terminator 2", echo.asAlert( "Amazing!" ), echo.asWarning( "TOP 100" ) );
		echo.log( "Predator" );
		echo.log( "Twins", echo.asWarning( "TOP 50" ) );
		echo.groupEnd();

		echo.log();

		echo.log( echo.asAlert( "Winner Winner" ), "Chicken dinner!" );

	</script>

</body>
</html>

上面的代码要想立刻理解可能有点困难,但格式化方法是关键,如:asAlert()asWarning()。这些方法将值(values)和 CSS 样式推入内部的队列。然后,这些 console 的代理方法将内部队列合并到单个的 console 调用中。

如果我们在 Chrome 中运行这个 demo,我们可以看到下面的控制台输出:

通过 echo 对象的抽象,让某个输入在控制台输出添加样式的效果或者不添加样式的效果变得异常简单。我们不用担心哪个参数对应哪个标记(token),你只需要将输入添加到对应的格式化方法中即可。

(完)

你的点赞会给我一天好心情,如果能顺手 来个 star 就更完美了。