用JavaScript显示浏览器客户端信息的超相近教程
1. firefox
gecko是firefox的呈现引擎。当初的gecko是作为通用mozilla浏览器一部分开发的,而第一个采用gecko引擎的浏览器是netscape6;
我们可以使用用户代理检测下:如下js代码:
var ua = navigator.useragent; console.log(ua);
在windows下firefox下打印如下:
上述是为netscape6编写的代理字符串一些构成:如下:
- mozilla/mozilla版本号(平台;加密类型;操作系统或cpu;预先发行的版本;gecko/gecko版本号;应用程序或产品/应用程序或产品版本号);
2. safari
safari的呈现引擎是webkit;是linux平台中konqueror浏览器的呈现引擎khtml的一个分支;几年后,webkit独立出来成为一个开源项目,专注于呈现引擎开发;
如下代码:
var ua = navigator.useragent; console.log(ua);
在windows下safari下打印如下:
webkit的用户代理字符串具备如下格式:
- mozilla/5.0 (平台;加密类型;操作系统或cpu) applewebkit/applewebkit版本号(khtml,like gecko) safari/safari版本号;
3. chrome
谷歌公司的chrome浏览器以webkit作为呈现引擎,使用了不同的javascript引擎;
如下js代码:
var ua = navigator.useragent; console.log(ua);
在windows下chrome下打印如下:
用户代理字符串完全自取webkit,只添加了一段表示chrome版本号信息;格式如下:
- mozilla/5.0 (平台;加密类型;操作系统或cpu) applewebkit/applewebkit版本号(khtml,like gecko) chrome/chrome版本号 safari/safari版本号
4. opera
opera默认的用户代理字符串是所有现代浏览器中最合理的---正确地标识了自身极其版本号,在opera8.0之前;其用户代理字符串采用如下格式:
- opera/ 版本号 (操作系统或cpu;加密类型) [语言]
- opera8发布后,用户代理字符串的 “语言”部分被移到圆括号内,以便更好地与其他浏览器匹配;如下所示:
- opera/版本号 (操作系统或cpu;加密类型;语言)
在现在的最新的版本opera29,
如下js代码:
var ua = navigator.useragent; console.log(ua);
通过代理检测如下:
5. ie
自从ie3以来,微软已经将ie的用户代理字符串修改成兼容netscape的形式,结构如下:
mozilla/版本号(平台;加密类型;操作系统或cpu);
但是ie8+的用户代理字符串添加了呈现引擎的(trident)的版本号;
js代码如下:
var ua = navigator.useragent.tolowercase(); console.log(ua);
比如在window下 ie7的如下:
ie8的如下:
但是ie9+ 对字符串的格式做了一点调整;mozilla的版本号增加到5.0;
ie9的如下:
ie10的如下:
ie11的如下:
6. ios和android
移动操作系统ios和android默认的浏览器都是基于webkit;而且都像桌面版一样;共享相同的基本用户代理字符串格式;ios设备的基本格式如下:
- mozilla/5.0 (平台;加密类型;操作系统或cpu like mac os x;语言)
- applewebkit/ applewebkit版本号(khtml,like gecko) version/浏览器版本号
- mobile/移动版本号 safari/safari版本号
android浏览器中默认格式与ios格式相似,没有移动版本号(但有mobile记号):
- mozilla/5.0 (linux; u; android 2.2; en-us; nexus one build/frf91)
- applewebkit/533.1 (khtml, like gecko) version/4.0 mobile safari/533.1
7. konqueror
与kde linux 集成的konqueror,是一款基于khtml 开源呈现引擎的浏览器。尽管konqueror 只
能在linux 中使用,但它也有数量可观的用户。为确保最大限度的兼容性,konqueror 效仿ie 选择了如
下用户代理字符串格式:
- mozilla/5.0 (compatible; konqueror/ 版本号; 操作系统或cpu )
不过,为了与webkit 的用户代理字符串的变化保持一致,konqueror 3.2 又有了变化,以如下格式
将自己标识为khtml:
- mozilla/5.0 (compatible; konqueror/ 版本号; 操作系统或cpu) khtml/ khtml 版本号 (like gecko)
下面是一个例子:
用户代理字符串检测技术
识别呈现引擎;
下面我们主要检测五大呈现引擎:ie,gecko,webkit,khtml和opera
如下代码:
var client = function(){ var engine = { // 呈现引擎 ie: 0, gecko:0, webkit: 0, khtml:0, opera:0, //其他版本号 ver: null }; // 在此检测呈现引擎,平台和设备 return { engine: engine }; }();
在如上client对象字面量中,每个呈现引擎都对应着一个属性,属性值默认为0;如果检测到了那个呈现引擎,那么将以浮点数值形式将引擎的版本号写入相应的属性。而呈现引擎的完整版本被写入变量ver属性中;
要正确识别呈现引擎,关键是检测顺序要正确,用户代理字符串存在诸多不一致的地方,如果检测顺序不对,很可能会导致检测结果不正确,因此,第一步就是识别opera,因为它的用户代理字符串有可能模仿其他浏览器;目前最新的opera浏览器版本是29,所以如下:
var ua = navigator.useragent .tolowercase(); console.log(ua);
打印如下:
因此可以这样判断代码;如下:
var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/)) { var result = ua.match(/opr\/([\d\.]+)/); console.log(result) console.log(result[1]) }
执行如下:
console.log(result);打印如下:
console.log(result[1])打印如下:
由此;我们可以这样编写代码;如下:
var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/)) { var result = ua.match(/opr\/([\d\.]+)/); engine.ver = result[1]; engine.opera = parsefloat(engine.ver); }
现在我们来打印下console.log(engine);如下所示:
但是呢opera29之前的版本, opera5+也有window.opera对象,所以我们也必须检测window.opera对象,我们可以调用version()方法可以返回一个表示浏览器版本的字符串;如下代码:
if(window.opera) { engine.ver = window.opera.version(); engine.opera = parsefloat(engine.ver); }
现在我们为了兼容之前及之后的opera浏览器,我们可以综合一下;如下:
var engine = client; var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/) || window.opera) { var result = ua.match(/opr\/([\d\.]+)/); engine.ver = result[1]; engine.opera = parsefloat(engine.ver); if(window.opera) { engine.ver = window.opera.version(); engine.opera = parsefloat(engine.ver); } }
现在第二步需要检测的是引擎是webkit,因为webkit的用户代理字符串中包含”gecko”和”khtml”这两个字符串,如果检测这两个,可能会有误差,但是webkit的用户代理字符串中的”applewebkit”是独一无二的,因此可以根据这个来检测;
var engine = client; var ua = navigator.useragent.tolowercase(); if(/applewebkit\/(\s+)/.test(ua)) { engine.ver = regexp["$1"]; engine.webkit = parsefloat(engine.ver); }
因此综合以上的所有代码如下:
var engine = client; var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/) || window.opera) { var result = ua.match(/opr\/([\d\.]+)/); engine.ver = result[1]; engine.opera = parsefloat(engine.ver); if(window.opera) { engine.ver = window.opera.version(); engine.opera = parsefloat(engine.ver); } }else if(/applewebkit\/(\s+)/.test(ua)) { engine.ver = regexp["$1"]; engine.webkit = parsefloat(engine.ver); }
接下来要测试的呈现引擎是khtml,同样,khtml的用户代理字符串中也包含”gecko”,因此在排除khtml之前,我们无法准确检测基于gecko浏览器。khtml的版本号与webkit的版本号在用户代理字符串中的格式差不多,因此也可以使用正则表达式,此外,由于konqueror 3.1 及更早版本中不包含khtml 的版本,故而就要使用konqueror 的版本来代替。下面就是相应的检测代码。
if (/khtml\/(\s+)/.test(ua) || /konqueror\/([^;]+)/.test(ua)){ engine.ver = regexp["$1"]; engine.khtml = parsefloat(engine.ver); }
下面是所有的代码:
var engine = client; var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/) || window.opera) { var result = ua.match(/opr\/([\d\.]+)/); engine.ver = result[1]; engine.opera = parsefloat(engine.ver); if(window.opera) { engine.ver = window.opera.version(); engine.opera = parsefloat(engine.ver); } }else if(/applewebkit\/(\s+)/.test(ua)) { engine.ver = regexp["$1"]; engine.webkit = parsefloat(engine.ver); }else if (/khtml\/(\s+)/.test(ua) || /konqueror\/([^;]+)/.test(ua)){ engine.ver = regexp["$1"]; engine.khtml = parsefloat(engine.ver); }
在排除webkit和khtml后,就可以准确检测gecko了,但是在用户代理字符串中,gecko的版本号不会出现在字符串”gecko”的后面,而是会出现在字符串”rv:”的后面。因此必须使用一个更复杂的正则表达式;
比如 firefox下的用户代理如下:
如下js代码检测:
var engine = client; var ua = navigator.useragent.tolowercase(); if(/rv:([^\)]+)\) gecko\/\d{8}/.test(ua)){ engine.ver = regexp["$1"]; engine.gecko = parsefloat(engine.ver); }
所以所有的js代码如下:
var engine = client; var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/) || window.opera) { var result = ua.match(/opr\/([\d\.]+)/); engine.ver = result[1]; engine.opera = parsefloat(engine.ver); if(window.opera) { engine.ver = window.opera.version(); engine.opera = parsefloat(engine.ver); } }else if(/applewebkit\/(\s+)/.test(ua)) { engine.ver = regexp["$1"]; engine.webkit = parsefloat(engine.ver); }else if (/khtml\/(\s+)/.test(ua) || /konqueror\/([^;]+)/.test(ua)){ engine.ver = regexp["$1"]; engine.khtml = parsefloat(engine.ver); }else if(/rv:([^\)]+)\) gecko\/\d{8}/.test(ua)){ engine.ver = regexp["$1"]; engine.gecko = parsefloat(engine.ver); }
最后一个检测就是ie浏览器了,ie的版本号位于字符串”msie”的后面,一个分号的前面;如下:
如下js代码检测:
if (/msie ([^;]+)/.test(ua)){ engine.ver = regexp["$1"]; engine.ie = parsefloat(engine.ver); }
如上;所有的代码如下:
var client = function(){ var engine = { // 呈现引擎 ie: 0, gecko:0, webkit: 0, khtml:0, opera:0, //其他版本号 ver: null }; // 在此检测呈现引擎,平台和设备 return { engine: engine }; }(); var engine = client; var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/) || window.opera) { var result = ua.match(/opr\/([\d\.]+)/); engine.ver = result[1]; engine.opera = parsefloat(engine.ver); if(window.opera) { engine.ver = window.opera.version(); engine.opera = parsefloat(engine.ver); } }else if(/applewebkit\/(\s+)/.test(ua)) { engine.ver = regexp["$1"]; engine.webkit = parsefloat(engine.ver); }else if (/khtml\/(\s+)/.test(ua) || /konqueror\/([^;]+)/.test(ua)){ engine.ver = regexp["$1"]; engine.khtml = parsefloat(engine.ver); }else if(/rv:([^\)]+)\) gecko\/\d{8}/.test(ua)){ engine.ver = regexp["$1"]; engine.gecko = parsefloat(engine.ver); }else if (/msie ([^;]+)/.test(ua)){ engine.ver = regexp["$1"]; engine.ie = parsefloat(engine.ver); }
识别浏览器
大多数情况下,如上面识别了引擎还不能满足我们的需求,比如苹果公司的safari浏览器和谷歌的chrome浏览器都使用了webkit作为呈现引擎;但他们的javascript引擎且不一样,在这两个浏览器中,client.webkit都会返回非0值,无法区别,因此我们还需要识别下浏览器;
如下增加代码如下:
var client = function(){ var engine = { // 呈现引擎 ie: 0, gecko:0, webkit: 0, khtml:0, opera:0, //其他版本号 ver: null }; var browser = { // 浏览器 ie: 0, firefox:0, safari:0, konq:0, opera:0, chrome:0, // 其他的版本 ver: null }; // 在此检测呈现引擎,平台和设备 return { engine: engine, browser: browser }; }();
如上代码增加了私有变量browser,用于保存每个主要浏览器的属性,与engine变量一样,除了当前使用的浏览器,其他属性值将保持为0;如果是当前使用的浏览器,则这个属性中保存的是浮点数值形式的版本号,同样browser中的ver属性中在必要时将包含字符串形式的浏览器完整版本号;
因此封装后的所有js代码如下:
var client = function(){ var engine = { // 呈现引擎 ie: 0, gecko:0, webkit: 0, khtml:0, opera:0, //其他版本号 ver: null }; var browser = { // 浏览器 ie: 0, firefox:0, safari:0, konq:0, opera:0, chrome:0, // 其他的版本 ver: null }; // 在此检测呈现引擎,平台和设备 return { engine: engine, browser: browser }; }(); var engine = client; var browser = client; var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/) || window.opera) { var result = ua.match(/opr\/([\d\.]+)/); engine.ver = browser.ver = result[1]; engine.opera = browser.opera = parsefloat(engine.ver); if(window.opera) { engine.ver = browser.ver = window.opera.version(); engine.opera = browser.opera = parsefloat(engine.ver); } }else if(/applewebkit\/(\s+)/.test(ua)) { engine.ver = regexp["$1"]; engine.webkit = parsefloat(engine.ver); // 确定是chrome还是safari /* * chrome用户代理字符串 * mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) * chrome/42.0.2311.152 safari/537.36 */ if(/chrome\/(\s+)/.test(ua)) { browser.ver = regexp["$1"]; browser.chrome = parsefloat(browser.ver); }else if(/version\/(\s+)/.test(ua)) { /* * safari用户代理字符串 * mozilla/5.0 (windows nt 6.1; wow64) applewebkit/534.57.2 (khtml, like gecko) * version/5.1.7 safari/534.57.2 */ browser.ver = regexp["$1"]; browser.safari = parsefloat(browser.ver); }else { //近似地确定版本号 var safariversion = 1; if (engine.webkit < 100){ safariversion = 1; } else if (engine.webkit < 312){ safariversion = 1.2; } else if (engine.webkit < 412){ safariversion = 1.3; } else { safariversion = 2; } browser.safari = browser.ver = safariversion; } }else if (/khtml\/(\s+)/.test(ua) || /konqueror\/([^;]+)/.test(ua)){ engine.ver = browser.ver = regexp["$1"]; engine.khtml = browser.konq = parsefloat(engine.ver); }else if(/rv:([^\)]+)\) gecko\/\d{8}/.test(ua)){ engine.ver = regexp["$1"]; engine.gecko = parsefloat(engine.ver); /* * firefox的用户代理的字符串 * mozilla/5.0 (windows nt 6.1; wow64; rv:38.0) * gecko/20100101 firefox/38.0 */ // 确定是不是firefox if(/firefox\/(\s+)/.test(ua)) { browser.ver = regexp["$1"]; browser.firefox = parsefloat(browser.ver); } }else if (/msie ([^;]+)/.test(ua) || "activexobject" in window){ if("activexobject" in window) { if(/msie ([^;]+)/.test(ua)) { engine.ver = browser.ver = regexp["1"]; engine.ie = browser.ie = parsefloat(engine.ver); }else { // ie11+ if(/rv:([^\)]+)\)/.test(ua)) { engine.ver = browser.ver = regexp["1"]; engine.ie = browser.ie = parsefloat(engine.ver); }else { // ie11+ if(/rv:([^\)]+)\)/.test(ua)) { engine.ver = browser.ver = regexp["1"]; engine.ie = browser.ie = parsefloat(engine.ver); } } } } // 可以打印下 console.log(browser);
对opera和ie而言,browser对象中的值等于engine对象中值,对konqueror 而言,browser.
konq 和browser.ver 属性分别等于engine.khtml 和engine.ver 属性。为了检测chrome 和safari,我们在检测引擎的代码中添加了if 语句。提取chrome 的版本号时,需要查找字符串"chrome/"并取得该字符串后面的数值。而提取safari 的版本号时,则需要查找字符串"version/"并取得其后的数值。由于这种方式仅适用于safari 3 及更高版本,因此需要一些备用的代码,将webkit 的版本号近似地映射为safari 的版本号(至于else后面的就不多讲)。在检测firefox 的版本时,首先要找到字符串"firefox/",然后提取出该字符串后面的数值。
有了上面的代码,我们就可以编写下面的代码判断了,如下代码:
if (client.engine.webkit) { //if it's webkit if (client.browser.chrome){ //执行针对chrome 的代码 } else if (client.browser.safari){ //执行针对safari 的代码 } } else if (client.engine.gecko){ if (client.browser.firefox){ //执行针对firefox 的代码 } else { //执行针对其他gecko 浏览器的代码 } } 识别平台 目前有三大主流平台(windows,mac,unix(包括各种linux));因为那些浏览器(safari,opera,firefox)在不同的平台可能会有不同的问题;为了检测这些平台,还需要像下面这样再添加一个新对象; var client = function(){ var engine = { // 呈现引擎 ie: 0, gecko:0, webkit: 0, khtml:0, opera:0, //其他版本号 ver: null }; var browser = { // 浏览器 ie: 0, firefox:0, safari:0, konq:0, opera:0, chrome:0, // 其他的版本 ver: null }; var system = { win: false, mac: false, xll: false }; // 在此检测呈现引擎,平台和设备 return { engine: engine, browser: browser, system: system }; }();
如上的代码添加了一个包含3个属性的新变量system,其中,win属性表示是否为windows平台,mac代表mac,xll代表是unix,system的对象的属性默认都为false,在确定平台时,检测navigator.platform要比检测用户代理字符串更简单,检测用户代理字符串在不同的浏览器中会给出不同的平台信息,而navigator.platform属性可能的值包括”win32”,”win64”,”macppc”,”macintel”,”xll”和”linux i686”,这些值在不同的浏览器中都是一致的,检测代码非常直观;如下代码:
var system = client; var platform = navigator.platform; system.win = platform.indexof("win") == 0; system.mac = platform.indexof("mac") == 0; system.x11 = (platform.indexof("x11") == 0) || (platform.indexof("linux") == 0);
比如我现在在chrome浏览器下打印如下:
console.log(system)
截图如下:
如果我想知道是win32或者是win64的话,我们可以使用这句代码打印即可
console.log(navigator.platform);
识别移动设备
可以通过简单地检测字符串”iphone”,”ipod”,”ipad”,就可以分别设置响应属性的值了。
system.iphone = ua.indexof(“iphone”) > -1; system.ipod = ua.indexof(“ipod”) > -1; system.ipad = ua.indexof(“ipad”) > -1;
- 除了知道ios 设备,最好还能知道ios 的版本号。在ios 3 之前,用户代理字符串中只包含"cpu like
- mac os",后来iphone 中又改成"cpu iphone os 3_0 like mac os x",ipad 中又改成"cpu os 3_2
- like mac os x"。也就是说,检测ios 需要正则表达式反映这些变化。
//检测ios 版本 if (system.mac && ua.indexof("mobile") > -1){ if (/cpu (?:iphone )?os (\d+_\d+)/.test(ua)){ system.ios = parsefloat(regexp.$1.replace("_", ".")); } else { system.ios = 2; //不能真正检测出来,所以只能猜测 } }
- 检查系统是不是mac os、字符串中是否存在"mobile",可以保证无论是什么版本,system.ios
- 中都不会是0。然后,再使用正则表达式确定是否存在ios 的版本号。如果有,将system.ios 设置为
- 表示版本号的浮点值;否则,将版本设置为2。(因为没有办法确定到底是什么版本,所以设置为更早的
- 版本比较稳妥。)
- 检测android 操作系统也很简单,也就是搜索字符串"android"并取得紧随其后的版本号。
//检测android 版本 if (/android (\d+\.\d+)/.test(ua)){ system.android = parsefloat(regexp.$1); }
- 由于所有版本的android 都有版本值,因此这个正则表达式可以精确地检测所有版本,并将
- system.android 设置为正确的值。
- 在了解这些设备信息的基础上,就可以通过下列代码来确定用户使用的是什么设备中的webkit 来
访问网页:
if (client.engine.webkit){ if (client.system.ios){ //ios 手机的内容 } else if (client.system.android){ //android 手机的内容 } }
因此所有的js代码如下:
var client = function(){ var engine = { // 呈现引擎 ie: 0, gecko:0, webkit: 0, khtml:0, opera:0, //其他版本号 ver: null }; var browser = { // 浏览器 ie: 0, firefox:0, safari:0, konq:0, opera:0, chrome:0, // 其他的版本 ver: null }; var system = { win: false, mac: false, xll: false, // 移动设备 iphone: false, ipod: false, ipad: false, ios: false, android:false }; // 在此检测呈现引擎,平台和设备 return { engine: engine, browser: browser, system: system }; }(); var engine = client; var browser = client; var ua = navigator.useragent.tolowercase(); if(ua.match(/opr\/([\d\.]+)/) || window.opera) { var result = ua.match(/opr\/([\d\.]+)/); engine.ver = browser.ver = result[1]; engine.opera = browser.opera = parsefloat(engine.ver); if(window.opera) { engine.ver = browser.ver = window.opera.version(); engine.opera = browser.opera = parsefloat(engine.ver); } }else if(/applewebkit\/(\s+)/.test(ua)) { engine.ver = regexp["$1"]; engine.webkit = parsefloat(engine.ver); // 确定是chrome还是safari /* * chrome用户代理字符串 * mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) * chrome/42.0.2311.152 safari/537.36 */ if(/chrome\/(\s+)/.test(ua)) { browser.ver = regexp["$1"]; browser.chrome = parsefloat(browser.ver); }else if(/version\/(\s+)/.test(ua)) { /* * safari用户代理字符串 * mozilla/5.0 (windows nt 6.1; wow64) applewebkit/534.57.2 (khtml, like gecko) * version/5.1.7 safari/534.57.2 */ browser.ver = regexp["$1"]; browser.safari = parsefloat(browser.ver); }else { //近似地确定版本号 var safariversion = 1; if (engine.webkit < 100){ safariversion = 1; } else if (engine.webkit < 312){ safariversion = 1.2; } else if (engine.webkit < 412){ safariversion = 1.3; } else { safariversion = 2; } browser.safari = browser.ver = safariversion; } }else if (/khtml\/(\s+)/.test(ua) || /konqueror\/([^;]+)/.test(ua)){ engine.ver = browser.ver = regexp["$1"]; engine.khtml = browser.konq = parsefloat(engine.ver); }else if(/rv:([^\)]+)\) gecko\/\d{8}/.test(ua)){ engine.ver = regexp["$1"]; engine.gecko = parsefloat(engine.ver); /* * firefox的用户代理的字符串 * mozilla/5.0 (windows nt 6.1; wow64; rv:38.0) * gecko/20100101 firefox/38.0 */ // 确定是不是firefox if(/firefox\/(\s+)/.test(ua)) { browser.ver = regexp["$1"]; browser.firefox = parsefloat(browser.ver); } }else if (/msie ([^;]+)/.test(ua) || "activexobject" in window){ if("activexobject" in window) { if(/msie ([^;]+)/.test(ua)) { engine.ver = browser.ver = regexp["$1"]; engine.ie = browser.ie = parsefloat(engine.ver); }else { if(/rv:([^\)]+)\)/.test(ua)) { engine.ver = browser.ver = regexp["$1"]; engine.ie = browser.ie = parsefloat(engine.ver); } } } } // 检测平台 var system = client; var platform = navigator.platform; system.win = platform.indexof("win") == 0; system.mac = platform.indexof("mac") == 0; system.x11 = (platform.indexof("x11") == 0) || (platform.indexof("linux") == 0); // 移动设备 system.iphone = ua.indexof("iphone") > -1; system.ipod = ua.indexof("ipod") > -1; system.ipad = ua.indexof("ipad") > -1; //检测ios 版本 if (system.mac && ua.indexof("mobile") > -1){ if (/cpu (?:iphone )?os (\d+_\d+)/.test(ua)){ system.ios = parsefloat(regexp.$1.replace("_", ".")); } else { system.ios = 2; //不能真正检测出来,所以只能猜测 } } //检测android 版本 if (/android (\d+\.\d+)/.test(ua)){ system.android = parsefloat(regexp.$1); }