引用自Wikipedia:

一个quine是一个计算机程序,它不接受任何输入,且唯一的输出就是自身的源代码.

@cowboy (Ben Alman) 给出了一个用JavaScript写的quine程序:

!function $(){console.log('!'+$+'()')}()

为什么这个quine能成功运行呢?

获得源代码

如果一个程序仅是由一个函数组成的,那么我们很容易获得这个函数的源代码:在大多数JavaScript引擎中,将一个函数转换成字符串就会返回它的源码.

> function foo() { return "abc" }

> String(foo)
'function foo() { return "abc" }'

输出自身源码的函数表达式

我们的quine最开始是这样的:

function $() { console.log(String($)) }

这是一个函数声明,函数名为$(合法的JavaScript标识符).我们需要把它改写成为一个函数表达式.像这样:

var prog = function $() { console.log(String($)) };

等号右边是一个命名的函数表达式.函数名$只在函数内部才能访问到(译者注:作者忽略了旧版IE).调用这个函数后会发现,它已经是一个很接近quine的程序:

> prog()
function $() { console.log(String($)) }
undefined

在调用函数之后,控制台会显示出函数的输出,以及函数的返回值undefined.

立即调用函数表达式(IIFE)

现在我们有了一个用单个函数写成的程序.但我们不想延迟它的执行,我们想让它立即执行.JavaScript允许你创建一个函数表达式的同时就立即调用这个函数.这种写法称之为立即调用函数表达式(Immmediately Invoked Function Expression)(IIFE, 发音为“iffy”,译者注:亦非). 顺便说一句,这个术语也是由Alman发明的.把我们的程序改写成IIFE之后是这样的:

> !function $() { console.log(String($)) }()
function $() { console.log(String($)) }
true

一元非运算符(!)让JavaScript把这个函数解析成为一个函数表达式.否则,我们不能立即执行它 [1]. true是这个函数返回值取反后的结果:!undefined.

添加缺少的字符

我们的输出还缺少一些字符:函数表达式前面的叹号!以及后面的括号.可以这样加上它们:

> !function $() { console.log('!'+$+'()') }()
!function $() { console.log('!'+$+'()') }()
true

IIFE的另一种写法

你还可以用小括号来括住一个IIFE:

(function $(){console.log('('+$+'())')}())

函数表达式之前的左括号和之后的右括号在源码中都出现了两次,所以使用感叹号可以给我们省出两个字符.

一个 quine 是一个计算机程序,它不接受任何输入,且唯一的输出就是自身的源代码。

感觉挺有意思的,于是打算自己用js写一个试试。

首先确定输出方式为控制台,即通过 console.log 输出。

为有(guang)趣(gao)起见,准备从一个“打印本站域名”的“额外功能”开始:

console.log("perichr.org")

这个命令可以在控制栏打印出一行 perichr.org 。接下来我准备一步一步改造这个命令,直至达成 quine的目标。

首先试着打印上面这个命令:

console.log('console.log("perichr.org")')

这样就在控制栏打印了一行 console.log("perichr.org") 。注意到源码里外用了不同的引号,这是为了规避转义符。

对了,别忘了需要执行自己,那么有:

console.log("perichr.org");console.log('console.log("perichr.org")')

很好,这样即执行了“额外功能”,又打印了自己的源代……不对!压根没有打印自己的源代码对吧! 
从简单的入手,不考虑打印完整源代码,先想想要怎样能在执行一个功能的同时打印功能代码呢?打印的是字符串,执行的是js源码……想到什么了?对了, eval !

p='console.log("perichr.org")';eval(p);console.log(p)

很好,这次完美地打印了 perichr.org 和 console.log("perichr.org") 。要注意的是,这里的变量 p 没有做声明,严格模式下是通不过的哟!

不过这还远远不够!来做一点小调整:

p='console.log("perichr.org");console.log(p)';eval(p)

发现了吗?这次输出了 perichr.org 和 console.log("perichr.org");console.log(p) !进化了!接下来先讨个巧,利用 赋值表达式返回右值 的小技巧来简化一下代码,毕竟代码越精简心理压力越小嘛……

eval(p='console.log("perichr.org");console.log(p)')

接下来怎么办呢……来对比下上面的源码和输出的代码: console.log("perichr.org");console.log(p) ,其实已经很接近了有木有!输出码就是缺少了头 eval(p=' 和尾 ') !来,补上!

eval(p='console.log("perichr.org");console.log("eval(p=\'"+p+"\')")')

在补的时候就应该已经注意到了一个问题:转义符!这里已经无法避免转义符了!来看看输出的代码: eval(p='console.log("perichr.org");console.log("eval(p='"+p+"')")')

真好,已经十分接近了对吧!但还是有一点小问题:没错,转义符!输出时源码中的 \' 被转义成了 ' !怎么办?那就要把文本中的 ' 反转义为 \' !怎么转?可以使用 "'".replace(/'/g,"\\'") 这种方式。考虑到文本要在 p 的赋值时转义,那么就需要手动反转义为: '.replace(/\'/g,"\\\\\'")' 。接下来把它插入源码中:

eval(p='console.log("perichr.org");console.log("eval(p=\'"+p.replace(/\'/g,"\\\\\'")+"\')")')

等等……好像有什么不对?我刚才是不是把一大波 有用的反斜杠 加入源代码了?要知道,这货也是会被转义的啊!!怎么办?好嘛,来改造下反转义代码,这回单引号和反斜杠都要反转义一下: .replace(/[\\']/g,"\\$&") ,这里的 $& 是指返回匹配字符串。手动反转义: .replace(/[\\\\\']/g,"\\\\$&") ,再来一次:

eval(p='console.log("perichr.org");console.log("eval(p=\'"+p.replace(/[\\\\\']/g,"\\\\$&")+"\')")')

在控制台试一试……成功!完美!强力!分毫不差地返回了自身!任务圆满完成!

怎么样,看起来很高大上的东西,其实入门很简单吧?

有人问,费劲心机写这么个东西,有啥意义?嘛,这个世界不是为了“某些人所谓的意义”而存在的。世界存在的意义在于“世界存在”这个现象本身;我折腾这个东西的意义也就在于“折腾代码”这件事本身。我觉得我获得了一些有意义的东西。你说呢?

xw引:

http://www.cnblogs.com/ziyunfei/archive/2012/10/05/2709428.html

http://www.350351.com/plus/view.php?aid=285910&pageno=1#

javascript版的quine程序-返回自身源码的更多相关文章

  1. 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计

    使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...

  2. 一步步实现windows版ijkplayer系列文章之六——SDL2源码分析之OpenGL ES在windows上的渲染过程

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  3. 从微信小程序开发者工具源码看实现原理(四)- - 自适应布局

    从前面从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计可以知道,小程序大部分是通过web技术进行渲染的,也就是最终通过浏览器的dom tree + cssom来生成渲染树:既然最终是通 ...

  4. android版高仿淘宝客户端源码V2.3

    android版高仿淘宝客户端源码V2.3,这个版本我已经更新到2.3了,源码也上传到源码天堂那里了,大家可以看一下吧,该应用实现了我们常用的购物功能了,也就是在手机上进行网购的流程的,如查看产品(浏 ...

  5. 《pigcms v6.2最新完美至尊版无任何限制,小猪微信源码多用户微信营销服务平台系统》

    <pigcms v6.2最新完美至尊版无任何限制,小猪微信源码多用户微信营销服务平台系统> 前两天分享了套小猪CMS(PigCms)多用户微信营销服务平台系统V6.1完美破解至尊版带微用户 ...

  6. HTML与CSS入门经典(第9版)试读 附随书源码 pdf扫描版​

    HTML与CSS入门经典(第9版)是经典畅销图书<HTML与CSS入门经典>的最新版本,与过去的版本相同,本书采用直观.循序渐进的方法,为读者讲解使用HTML5与CSS3设计.创建并维护世 ...

  7. 微信小程序一键生成源码 在线制作定制功能强大的微信小程序

    微信小程序发展到现在,短短的一年不到的时间(很快就要迎来微信小程序周年庆),在快迎来周年庆之际,百牛信息技术bainiu.ltd特记录一下这个发展的历程,用于将来见证小程序发展的辉煌时刻,我们还能知道 ...

  8. 同程联盟景点门票动态程序 beta1.0源码

    经过一段时间的开发,以及内部测试,同程网联盟景区新版程序正式发布推出,感谢广大联盟会员一直以来的支持与关注! 同程网联盟景区新版程序新功能介绍: 1.统一的页面风格.页面风格将与随后推出的度假线路.酒 ...

  9. WPF如何实现一款类似360安全卫士界面的程序?(共享源码!)

    以前学习Windows Form编程的时候,总感觉自己做的界面很丑,看到360安全卫士.迅雷等软件的UI设计都非常美观,心里总是憧憬着要是自己能实现这样的UI效果该多好!!!另一个困扰我的问题是,这个 ...

随机推荐

  1. SparkSQL UDF使用方法与原理详解

    UDF是SQL中很常见的功能,但在Spark-1.6及之前的版本,只能创建临时UDF,不支持创建持久化的UDF,除非修改Spark源码.从Spark-2.0开始,SparkSQL终于支持持久化的UDF ...

  2. 【Cocos2dx 3.3 Lua】SpriteBatchNode和SpriteFrameCache使用

    精灵帧缓存类 一.SpriteFrameCache     精灵帧缓冲类SpriteFrameCache用于存储精灵帧,SpriteFrameCache是一个单例模式,不属于某一个精灵,是所有精灵共享 ...

  3. iOS 自定义日志输出

    在做iOS开发过程中,我们经常需要输出日志来查看某些数据是否打印出来,或者查看查个类是否被调用了. 系统默认的是NSLog(@"xxxx %d",1) ,但如果该APP要发布到商店 ...

  4. 7.MySQL必知必会之用通配符进行过滤-like

    用通配符进行过滤-like 1. like操作符 先说两个概念:

  5. Servlet—基础

    什么是Servlet? 1 . jsp经编译后就变成了Servlet.(JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代 码,Web容器将JSP的代码编译成JVM能够识别 ...

  6. yii2--windows下composer安装

    //文件及存放目录 D:\php\php.exe D:\php\composer.phar //设置path(追加) ; D:\php //创建composer.bat文件 D:\php>ech ...

  7. MSF渗透测试-CVE-2017-11882(MSOffice漏洞)

    1.测试环境 2.测试前准备 3.测试过程 -3.1虚拟机环境测试 -3.2局域网靶机测试 4.测试感想 1.测试环境 攻击机: OS:kail IP:192.168.15.132/192.168.1 ...

  8. 修練營ASP.NET]淺談多層式架構 (Multi Tiers)

    從需求談起 我們舉個小例子來理解一般的方式與多層的方式有何不同 假設:我需要顯示最近三個月內,所有營業員的銷售金額成績排名 一般的做法: 在一個畫面中,拉個GridView,一個SqlDataSouc ...

  9. linux 安装软件的几种方法

    一. 解析Linux应用软件安装包: 通常Linux应用软件的安装包有三种: 1) tar包,如software-1.2.3-1.tar.gz.它是使用UNIX系统的打包工具tar打包的. 2) rp ...

  10. JS重要的内置对象

    Array对象: 属性: .length      获得数组的长度: 方法: .concat() 连接内容或者数组,组成新的数组: .join(n)  用n连接数组的每一项组成字符串,可以是空字符串: ...