有关JavaScript的10个怪癖和秘密分享_javascript技巧

原文作者:Andy Croxall
原文链接:Ten Oddities And Secrets About JavaScript
翻译编辑:张鑫旭 

数据类型和定义

  1. Null是个对象
  JavaScript众多类型中有个Null类型,它有个唯一的值null, 即它的字面量,定义为完全没有任何意义的值。其表现得像个对象,如下检测代码:

复制代码 代码如下:

alert(typeof null); //弹出 'object'

如下截图:

  尽管typeof值显示是"object",但null并不认为是一个对象实例。要知道,JavaScript中的值都是对象实例,每个数值都是Number对象,每个对象都是Object对象。因为null是没有值的,所以,很明显,null不是任何东西的实例。因此,下面的值等于false。

复制代码 代码如下:

alert(null instanceof Object); //为 false

  译者注:null还有被理解为对象占位符一说

2. NaN是个数值
  NaN本意是表示某个值不是数值,但是其本身却又是数值,且不等于其自身,很奇怪吧,看下面的代码:

复制代码 代码如下:

alert(typeof NaN); //弹出 'Number'
alert(NaN === NaN); //为 false

结果如下截图:

实际上NaN不等于任何东西。要确认某玩意是不是NaN只能使用isNaN.
3. 无关键字的数组等同于false(关于Truthy和Falsy)
  下面是JavaScript另一个极品怪癖:

复制代码 代码如下:

alert(new Array() == false); //为 true

结果如下截图:

  想要知道这里发生了什么,你需要理解truthy和falsy这个概念。它们是一种true/flase字面量。在JavaScript中,所有的非Boolean型值都会内置一个boolean标志,当这个值被要求有boolean行为的时候,这个内置布尔值就会出现,例如当你要跟Boolean型值比对的时候。

  因为苹果不能和梨做比较,所以当JavaScript两个不同类型的值要求做比较的时候,它首先会将其弱化成相同的类型。false, undefined, null, 0, "", NaN都弱化成false。这种强制转化并不是一直存在的,只有当作为表达式使用的时候。看下面这个简单的例子:

复制代码 代码如下:

var someVar =0;
alert(someVar == false); //显示 true

结果如下截图:

  上面测试中,我们试图将数值0和boolean值false做比较,因两者的数据类型不兼容,JavaScript自动强制转换成统一的等同的truthy和falsy,其中0等同于false(正如上面所提及的)。

  你可能注意到了,上面一些等同false的值中并没有空数组。只因空数组是个怪胚子:其本身实际上属于truthy,但是当空数组与Boolean型做比较的时候,其行为表现又属于falsy。不解?这是由原因的。先举个例子验证下空数组的奇怪脾气:

复制代码 代码如下:

var someVar = []; //空数组
alert(someVar == false); //结果 true
if (someVar) alert('hello'); //alert语句执行, 所以someVar当作true

结果如下截图,连续弹出两个框框:

  译者注:之所以会有这种差异,根据作者的说法,数组内置toString()方法,例如直接alert的时候,会以join(“,”)的形式弹出字符串,空数组自然就是空字符串,于是等同false。具体可参见作者另外一篇文章,《Twisted logic: understanding truthy & falsy》。不过我个人奇怪的是,像空对象,空函数,弱等于true或者false的时候都显示false,为何?真的因为数组是个怪胎,需要特殊考虑吗?

  为避免强制转换在比较方面的问题,你可以使用强等于(===)代替弱等于(==)。

复制代码 代码如下:

var someVar = 0;
alert(someVar == false); //结果 true – 0属于falsy
alert(someVar === false); //结果 false – zero是个数值, 不是布尔值

结果如下截图(win7 FF4):

  如果你想深入探究JavaScript中类型强制转换等些特有的癖好,可以参见官方相关的文档规范:
section 11.9.3 of the ECMA-262
正则表达式
  4. replace()可以接受回调函数
  这是JavaScript最鲜为人知的秘密之一,v1.3中首次引入。大部分情况下,replace()的使用类似下面:

复制代码 代码如下:

alert('10 13 21 48 52'.replace(/\d+/g, '*')); //用 * 替换所有的数字

  这是一个简单的替换,一个字符串,一个星号。但是,如果我们希望在替换发生的时候有更多的控制,该怎么办呢?我们只希望替换30以下的数值,该怎么办呢?此时如果仅仅依靠正则表达式是鞭长莫及的。我们需要借助回调函数的东风对每个匹配进行处理。

复制代码 代码如下:

alert('10 13 21 48 52'.replace(/\d+/g, function(match) {
return parseInt(match) <30?'*' : match;
}));

  当每个匹配完成的时候,JavaScript应用回调函数,传递匹配内容给match参数。然后,根据回调函数里面的过滤规则,要么返回星号,要么返回匹配本身(无替换发生)。

如下截图:

5. 正则表达式:不只是match和replace
  不少javascript工程师都是只通过match和replace和正则表达式打交道。但JavaScript所定义的正则表达式相关方法远不止这两个。

  其中值得一提的是test(),其工作方式类似match(),但是返回值却不一样:test()返回的是布尔型,用来验证是否匹配,执行速度高于match()。

复制代码 代码如下:

alert(/\w{3,}/.test('Hello')); //弹出 'true'

  上面行代码用来验证字符串是否有三个以上普通字符,显然"hello"是符合要求的,所以弹出true。
结果如下截图:

  我们还应注意RegExp对象,你可以用此创建动态正则表达式对象,例如:

复制代码 代码如下:

function findWord(word, string) {
var instancesOfWord = string.match(new RegExp('\\b'+word+'\\b', 'ig'));
alert(instancesOfWord);
}
findWord('car', 'Carl went to buy a car but had forgotten his credit card.');

这儿,我们基于参数word动态创建了匹配验证。这段测试代码作用是不区分大小选的情况下选择car这个单词。眼睛一扫而过,测试英文句子中只有一个单词是car,因此这里的演出仅一个单词。\b是用来表示单词边界的。

  结果如下截图:

函数和作用域
  6. 你可以冒充作用域
  作用域这玩意是用来决定什么变量是可用的,独立的JavaScript(如JavaScript不是运行中函数中)在window对象的全局作用域下操作,window对象在任何情况下都可以访问。然而函数中声明的局部变量只能在该函数中使用。

复制代码 代码如下:

var animal ='dog';
function getAnimal(adjective) { alert(adjective+''+this.animal); }
getAnimal('lovely'); //弹出 'lovely dog'

  这儿我们的变量和函数都声明在全局作用域中。因为this指向当前作用域,在这个例子中就是window。因此,该函数寻找window.animal,也就是'dog'了。到目前为止,一切正常。然而,实际上,我们可以让函数运行在不同的作用域下,而忽视其本身的作用域。我们可以用一个内置的称为call()的方法来实现作用域的冒充。

复制代码 代码如下:

var animal ='dog';
function getAnimal(adjective) { alert(adjective+''+this.animal); };
var myObj = {animal: 'camel'};
getAnimal.call(myObj, 'lovely'); //弹出 'lovely camel'

  call()方法中的第一个参数可以冒充函数中的this,因此,这里的this.animal实际上就是myObj.animal,也就是'camel'了。后面的参数就作为普通参数传给函数体。

  另外一个与之相关的是apply()方法,其作用于call()一样,不同之处在于,传递给函数的参数是以数组形式表示的,而不是独立的变量们。所以,上面的测试代码如果用apply()表示就是:

复制代码 代码如下:

getAnimal.apply(myObj, ['lovely']); //函数参数以数组形式发送

demo页面中,点击第一个按钮的结果如下截图:

  点击第二个和第三个按钮的结果如下:
7. 函数可以执行其本身
  下面这个是很OK的:

复制代码 代码如下:

(function() { alert('hello'); })(); //弹出 'hello'

  这里的解析足够简单:声明一个函数,然后因为()解析立即执行它。你可能会奇怪为何要这么做(指直接屁股后面()调用),这看上去是有点自相矛盾的:函数包含的通常是我们想稍后执行的代码,而不是当下解析即执行的,否则,我们就没有必要把代码放在函数中。

  另外一个执行函数自身(self-executing functions (SEFs))的不错使用是为在延迟代码中使用绑定变量值,例如事件的回调(callback),超时执行(timeouts)和间隔执行(intervals)。如下例子:

复制代码 代码如下:

var someVar ='hello';
setTimeout(function() { alert(someVar); }, 1000);
var someVar ='goodbye';

  Newbies在论坛里总问这里timeout的弹出为什么是goodbye而不是hello?答案就timeout中的回调函数直到其运行的时候才去赋值someVar变量的值。而那个时候,someVar已经被goodbye重写了好长时间了。

  SEFs提供了一个解决此问题的方法。不是像上面一样含蓄地指定timeout回调,而是直接将someVar值以参数的形式传进去。效果显著,这意味着我们传入并孤立了someVar值,保护其无论后面是地震海啸还是女朋友发飙咆哮都不会改变。

复制代码 代码如下:

var someVar = 'hello';
setTimeout((function(someVar) {
returnfunction() { alert(someVar); }
})(someVar), 1000);
var someVar ='goodbye';

  风水轮流转,这次,这里的弹出就是hello了。这就是函数参数和外部变量的点差别了哈。
例如,最后一个按钮点击后的弹出如下:
浏览器
  8. FireFox以RGB格式读与返回颜色而非Hex
  直到现在我都没有真正理解为何Mozilla会这样子。为了有个清晰的认识,看下面这个例子:

复制代码 代码如下:

<!--
#somePara { color: #f90; }
-->
<p id="somePara">Hello, world!</p>
<script>
var ie = navigator.appVersion.indexOf('MSIE') !=-1;
var p = document.getElementById('somePara');
alert(ie ? p.currentStyle.color : getComputedStyle(p, null).color);
</script>

  大部分浏览器弹出的结果是ff9900,而FireFox的结果却是rgb(255, 153, 0),RGB的形式。经常,处理颜色的时候,我们需要花费不少代码将RGB颜色转为Hex。
下面是上面代码在不同浏览器下的结果:
其它杂七杂八
  9. 0.1 + 0.2 !== 0.3
  这个古怪的问题不只会出现在JavaScript中,这是计算机科学中一个普遍存在的问题,影响了很多的语言。标题等式输出的结果是0.30000000000000004。

  这是个被称为机器精度的问题。当JavaScript尝试执行(0.1 + 0.2)这行代码的时候,会把值转换成它们喜欢的二进制口味。这就是问题的起源,0.1实际上并不是0.1,而是其二进制形式。从本质上将,当你写下这些值的时候,它们注定要失去精度。你可能只是希望得到个简单的两位小数,但你得到的(根据Chris Pine的注解)是二进制浮点计算。好比你想把一段应该翻译成中文简体,结果出来的却是繁体,其中还是有差异是不一样的。

一般处理与此相关的问题有两个做法:

转换成整数再计算,计算完毕再转换成希望的小数内容
调整你的逻辑,设定允许范围为不是指定结果。
  例如,我们不应该下面这样:

复制代码 代码如下:

var num1=0.1, num2=0.2, shouldEqual=0.3;
alert(num1 + num2 == shouldEqual); //false

  而可以试试这样:

复制代码 代码如下:

alert(num1 + num2 > shouldEqual - 0.001&& num1 + num2 < shouldEqual +0.001); //true

  10. 未定义(undefined)可以被定义(defined)
  我们以一个和风细雨的小古怪结束。听起来可能有点奇怪,undefined并不是JavaScript中的保留字,尽管它有特殊的意义,并且是唯一的方法确定变量是否未定义。因此:

复制代码 代码如下:

var someVar;
alert(someVar == undefined); //显示 true

  目前为止,一切看上去风平浪静,正常无比,但剧情总是很狗血:

复制代码 代码如下:

undefined ="I'm not undefined!";
var someVar;
alert(someVar == undefined); //显示 false!

  这就是为什么jQuery源码中最外部的闭包函数要有个并没有传入的undefined参数,目的就是保护undefined不要被外部的些不良乘虚而入。

时间: 2024-09-19 10:13:21

有关JavaScript的10个怪癖和秘密分享_javascript技巧的相关文章

JavaScript头像上传插件源码分享_javascript技巧

本文实例为大家分享了JavaScript头像上传插件源码,供大家参考,具体内容如下 效果图: 源码: cxc.js       /* cxc.js 频繁操作公共接口 */ var $ = function (id) { return document.getElementById(id); }; //通过id获取dom对象 var A = function (msg) { alert(msg); }; //alert的简写 var EmptyFun = function () { }; // 空

javascript suggest效果 自动完成实现代码分享_javascript技巧

首先,用到的框架当然是我的框架mass Framework,当然你用其他框架也可以,如jQuery,没有什么复杂的东西.只要弄懂原理,一下子就能搞出来.想必,以后你们工作也遇到做搜索框的活儿. 由于本人没有后端,因此取用一个对象作为本地数据库.而我现在要做的,其实远远比suggest高级,类似IDE的语法提示的东西.当前成品已放到github上. 好了,我们动手吧.首先是结构层,装了FF的同学可以在百度首页查看源码,当输入几个字母时,会动态生成了那些HTML.不过怎么也好,其成就是一个DIV放到

IE与Firefox在JavaScript上的7个不同句法分享_javascript技巧

在这篇文章中,作者介绍了7个在IE和Firefox中不同的JavaScript句法. 1. CSS "float" 值 访问一个给定CSS 值的最基本句法是:object.style.property,使用驼峰写法来替换有连接符的值,例如,访问某个ID为"header"的<div>的 background-color值,我们使用如下句法: document.getElementById("header").style.backgrou

javascript中自定义对象的属性方法分享_javascript技巧

首先介绍下关联数组: 复制代码 代码如下: <script> var test=new Object(); test["a"]=1; test["b"]="string"; test["c"]=false; alert(test["a"]); </script> 执行上面的代码,显示1.在javascript中,方法和属性一样当做值来看待. 复制代码 代码如下: <script

JavaScript实现的双向跨域插件分享_javascript技巧

由于浏览器(同源策略)限制,JavaScript 跨域的问题,一直是一个颇为棘手的问题.HTML5 提供了跨文档消息传输的功能,在网页文档之间互相接收与发送信息.使用这个功能,不仅同源(域 + 端口号)的 Web 网页之间可以互相通信,还可以在两个不同域名之间实现跨域通信. 跨文档消息传输Cross Document Messaging提供了postMessage方法在不同网页文档之间互相传递数据,支持实时消息传递.现在很多浏览器都将支持这个功能,比如Google Chrome 2.0+.Int

JavaScript实现的石头剪刀布游戏源码分享_javascript技巧

这个游戏主要设计到两点: 首先是胜负运算 由于石头剪刀布是循环性的 石头 杀 剪子 剪子 杀 布 布   杀  石头 石头  杀  剪子 ... 根据以上特点找出规律,写出算法即可. 让电脑随机 这点比较容易,前面我有写过文章介绍,不明白的童鞋可以去看看. 随机刷屏 其实这个效果不是游戏的关键性,但为了看起来更加互动,好玩,我就给加上了.这里用到了一个取模算法,根据余数去循环显示即可达到效果. 界面截图 最后上代码 <!DOCTYPE html> <html> <head&g

JavaScript中实现依赖注入的思路分享_javascript技巧

如今各个框架都在模块化,连前端的javascript也不例外.每个模块负责一定的功能,模块与模块之间又有相互依赖,那么问题来了:javascript的依赖注入如何实现?(javascript的依赖注入,各大框架都有相应的实现,这里只学习实现思路) 如下需求: 假设已经有定义好的服务模块Key-Value集合,func为添加的新服务,参数列表为服务依赖项. 复制代码 代码如下: var services = { abc : 123, def : 456, ghi : 789 }; // 假设已定义

对JavaScript中this指针的新理解分享_javascript技巧

一直以来对this的理解只在可以用,会用,却没有去深究其本质.这次,借着<JavaScript The Good Parts>,作了一次深刻的理解.(所有调试都可以在控制台中看到,浏览器F12键) 下面我们一起来看看这个this吧. 在我们声明一个函数时,每个函数除了有定义时的parameters(形参),自身还会有额外的两个参数,一个是this,一个是arguments(实参).arguments就是函数实际接受到的参数,是一个类数组.arguments我只做个简略的介绍,重点我们放在thi

JavaScript 封装一个tab效果源码分享_javascript技巧

效果图如下: 查看演示  源码下载 html代码 <!DOCTYPE html> <html> <head> <title>tab测试</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-" /> <link href="css/tab.css" rel="styles