Quine 程序是指一个能够输出自身代码的计算机程序。Quine 程序的实现是计算机科学中一个有趣的问题,不同语言中有不同的实现方式,常见的技巧包括代码注入、字符串插值或反射等。在 JavaScript 中我们有多种方法来实现 Quine 程序,本文将讲解其中的两种常见方法。
1. 函数转字符串
第一种思路是利用函数转化为字符串时会输出自身源代码的特性,我们可以通过打印出函数转化为字符串的结果来获取函数的源代码。
function f() {
console.log(f.toString()) }
f()
// 结果:function f() { console.log(f.toString()) }
这段代码中我们声明了一个函数f
,它所做的事情就是把自己转化为字符串,然后打印出来。调用这个函数我们就可以在控制台看到它的源代码内容。但是我们的目的是打印整个程序的源代码,这里第二行的函数调用语句没有被打印出来。我们只要在函数里面把这个函数调用语句也打印出来就好。
function f() {
console.log(f.toString(), '\nf()') }
f()
// 运行结果:
// function f() { console.log(f.toString(), '\nf()') }
// f()
这里我们利用了console.log
会把所有的参数按顺序打印的特性。但这种写法还不够简洁,因为这个函数我们只调用了一次,所以完全可以写成立即执行函数(IIFE)的形式。同时我们还使用了模版字符串来简化字符串的拼接,注意这里不再需要显式地调用toString
,因为模版字符串在解析的时候会自动帮我们调用。同时要记得在打印的时候把包裹函数的括号和调用函数的括号也打印出来。
(function f() {
console.log(`(${
f})()`) })()
// 运行结果:
// (function f() { console.log(`(${f})()`) })()
除了使用常规的命名函数声明的方式,我们也可以用箭头函数来实现程序自打印。
(f = () => console.log(`(f = ${
f})()`))()
这段代码做的事情和刚才的程序类似,我们声明了一个变量f
(因为没有添加变量声明关键字,所以相当于声明了一个全局变量),这个变量指向一个箭头函数,它所做的事情就是打印自己的源代码,并在周围把 IIFE(立即执行函数)和变量声明的代码也打印出来。这样就可以实现整个程序源代码的打印了。
如果把这个例子的变量名和格式稍微调整一下,可以得到这样一行代码
({
mathJaxContainer[0]}={
mathJaxContainer[1]}})()`))()
这行代码看起来没有任何意义,更像是一个颜文字或者一堆乱码,但它实际上做的事情跟刚刚的代码一样。只是把变量名从f
换成了$
,并且把箭头函数的参数列表从()
换成了_
(两者都是表示没有任何参数的意思)。
2. 格式化字符串
格式化字符串是 Quine 程序最常见的实现方式,因为大部分的语言都支持格式化字符串。JS同样可以在console.log
中使用格式化字符串,其规则和C语言基本一致。这里我们先简单讲解一下其使用方法:
console.log('%s-%d', 'hello', 1); // 输出:hello-1
这段代码中的第一个参数'%s %d'
是一个格式化字符串,其中 %s
和 %d
是占位符,分别表示字符串类型和数字类型。后面的参数 'hello' 和 1.0 是用于填充格式化字符串的值。执行这段代码会将字符串中的%s
替换为hello
,%d
替换为1,最终输出hello-1
。需要注意的是,占位符的数量和类型必须与后面的参数数量和类型相匹配,否则会导致运行时错误。
利用格式化字符串我们可以轻松地实现 Quine 程序。其根本思想和第一种方法类似,都是利用变量可以打印自身的特性。具体的实现方法如下所示:
const s = "const s = '%s'; console.log(s, s);"; console.log(s, s);
// 输出:const s = 'const s = '%s'; console.log(s, s);'; console.log(s, s);
在这段代码中我们把s
设置为一个格式化字符串,留一个字符串的占位符,在console.log
语句中用s
自身的内容替换占位符。在占位符的周围把变量声明语句和console.log
语句的代码写上就OK了。
总结
有两种方法可以实现JS的 Quine 程序:
- 第一种方法具有鲜明的 JavaScript 特色,利用函数转化为字符串是自身源代码的特性来打印整个程序的源代码。
- 第二种方法更为常见,在绝大多数的语言中都可以用这种方法实现程序自打印,即通过利用格式化字符串来让变量打印自身。
本文作者:wzkMaster,如果有帮助的话欢迎点赞收藏~