JavaScript的隐藏特性?

312

1
你是不是想说:“看到了那个问题吸引的声望和观点,我想问几乎完全相同的问题来提高自己的声望”?;-) - Bobby Jack
1
当然,悲观主义者。 :) 我考虑过将这个问题变成社区问题。此外,当你获得一定数量的积分后,所有的回报都会递减。 - Allain Lalonde
1
好的,看起来你并不“需要”声望!我猜我只是对C#这个问题有些困惑——它似乎并不是这个网站旨在解决的问题类型。 - Bobby Jack
3
是的,也许不是这样,但我发现答案中的知识非常棒。如果没有 Stack Overflow(SO),要让一般的 C# 程序员在一个地方接触到所有这些知识将会很难。玩耍多年才能获得同样的宝贵经验清单。 - Allain Lalonde
我喜欢这一系列的问题;我认为答案的“digg”式系统比论坛上的“+1”更好。更容易看出社区认为最重要的是什么。我相信这对于谷歌来说也是良好的链接诱饵! - Martin Clarke
7
我已经专业编写JavaScript 10年了,从这个帖子中我学到了一些东西。谢谢Alan! - Andrew Hedges
99个回答

21

JavaScript中的时间戳:

// Usual Way
var d = new Date();
timestamp = d.getTime();

// Shorter Way
timestamp = (new Date()).getTime();

// Shortest Way
timestamp = +new Date();

14
最短的方式很巧妙但难以理解,因为有些人可能会认为你想写 += 但错误地写成了 =+。 - Rene Saarsoo
2
当然,通过适当的格式化/间距,可以避免这种歧义。为什么使用正确的间距如此困难?不要写“timestamp=+new Date();”,那显然很令人困惑。相反,请写成“timestamp = +new Date();”。 - ken
@ken:谁定义“适当”?有些人喜欢少一些空格,有些人喜欢多一些。对于一个组织或项目来说可能有一种适当的方式,但不一定是全球通用的。 - icktoofay
@ken:当然,“timestamp=+new Date();”并不是很清晰,可能也不会被任何编码指南支持。但是,“something=123;”并不罕见,对于习惯这种风格的人来说,这样的写法也完全可以算“正确”。 - icktoofay
Date.now() - bennedich
显示剩余4条评论

20

你可以使用左侧的 [] 分配本地变量。如果你想从一个函数中返回多个值而不创建一个不必要的数组,这将非常方便。

function fn(){
    var cat = "meow";
    var dog = "woof";
    return [cat,dog];
};

var [cat,dog] = fn();  // Handy!

alert(cat);
alert(dog);

这是JS核心的一部分,但不知何故,直到今年我才意识到它。


除了这个小问题,这是一个有趣的功能。IE不支持它。 - Kamarey
12
这是"解构赋值"; 我相信它仅在运行JavaScript 1.7及更高版本的Firefox中受支持。但它在Opera 10、Chrome 3以及IE中肯定会引发错误。请参阅https://developer.mozilla.org/en/New_in_JavaScript_1.7#Destructuring_assignment。 - NickFitz
我希望在更多的编程语言中能看到这样的语法,比如C#。 - Greg
3
同时,这段代码已经非常优秀了:function fn(){ return {cat:"喵喵",dog:"汪汪"}; // 很好用! }; var snd = fn(); alert(snd.cat); alert(snd.dog); - Plynx
在Chrome >15 w >JS 1.6中无法工作,否则这将是最大的惊喜。我甚至尝试过一次,试图模拟PHP的list(...)。 - Lorenz Lo Sauer

19

Javascript中的所有对象都是实现为哈希表,因此它们的属性可以通过索引器进行访问,反之亦然。此外,您可以使用 for/in 运算符枚举所有属性:

var x = {a: 0};
x["a"]; //returns 0

x["b"] = 1;
x.b; //returns 1

for (p in x) document.write(p+";"); //writes "a;b;"

2
此外,属性名称是字符串,如果该字符串包含某些字符,使其无法通过点符号访问,则可以通过索引符号进行访问。例如,对象属性x ['funky prop']无法作为x.funky prop访问;x ['funky.prop']也无法作为x.funky.prop访问。 - BarelyFitz
4
在使用for-in循环访问属性之前,请使用"object.hasOwnProperty(propertyName)"检查属性名,否则您可能会遇到一些意外的问题。请注意不要忘记这一步哦;) - BYK
@Beska:我认为详细说明如下:for (p in x) if (x.hasOwnProperty(p)) document.write(p+";"); 这样可以避免在x的原型中添加新属性时,in 也会枚举它们的问题,这可能不是期望的行为。 - shuckster

17

当你想从一个数组中删除一个元素时,可以使用delete运算符,如下所示:

var numbers = [1,2,3,4,5];
delete numbers[3];
//numbers is now [1,2,3,undefined,5]

正如您所看到的,该元素已被移除,但由于该元素被替换为undefined值,因此数组中留下了一个空洞。

因此,为了解决这个问题,不要使用delete,而是使用splice数组方法...如下:

var numbers = [1,2,3,4,5];
numbers.splice(3,1);
//numbers is now [1,2,3,5]
splice的第一个参数是数组中的序数[index],第二个参数是要删除的元素数量。

17

这个主题中有几篇答案展示了如何通过其原型扩展Array对象。这是一个不好的想法,因为它会破坏for (i in a)语句。

那么如果你在代码中没有使用for (i in a)呢?这样做只有在你自己编写的代码是你运行的唯一代码时才可以,但在浏览器中这种情况并不太可能发生。如果人们开始像这样扩展他们的Array对象,Stack Overflow将开始充斥着一堆神秘的JavaScript错误。

详细信息请参见此处


18
不应该使用for..in循环遍历数组!用标准的for()循环或新的forEach()方法来遍历数组,而且for..in仅适用于遍历对象属性。请注意不改变原意。 - Zilk
6
尝试说服现有代码遵循这个建议 ;) - Chris Noe
3
对我来说,for( x in y) 在数组上从未正常工作过。我很快就学会了使用完整形式的for循环。我不会让任何使用 for(in) 循环在数组上的代码接近我的工作。相比之下,有很多真正好的、写得好的代码可以代替它。 - Breton
5
你只是不够了解JavaScript。它并不会破坏for-in循环,而是由于你构建的不当。在使用for-in迭代时,你必须用“yourObject.hasOwnProperty(propertyName)”检查所有属性。 - BYK
3
@statictype.org - 是的,它不是这个意思。这就是重点。只需使用一个索引变量进行迭代即可。 - harto
显示剩余2条评论

16

在一个函数中,你可以返回函数本身:

function showSomething(a){
   alert(a);
   return arguments.callee;
}

// Alerts: 'a', 'b', 'c'
showSomething('a')('b')('c');

// Or what about this:
(function (a){
   alert(a);
   return arguments.callee;
}​)('a')('b')('c');​​​​

我不知道什么时候它会有用,但无论如何,这很奇怪而有趣:

var count = function(counter){
   alert(counter);
   if(counter < 10){
      return arguments.callee(counter+1);
   }
   return arguments.callee;
};

count(5)(9); // Will alert 5, 6, 7, 8, 9, 10 and 9, 10

实际上,Node.js的FAB框架似乎已经实现了这个功能;例如,请参见此主题


15

JavaScript 中 Date() 的工作方式让我非常兴奋!

function isLeapYear(year) {
    return (new Date(year, 1, 29, 0, 0).getMonth() != 2);
}

这是一个真正的“隐藏功能”。

编辑:根据评论建议,删除了“?”条件以符合政治正确性。之前是:... new Date(year, 1, 29, 0, 0).getMonth() != 2 ? true : false ... 请查看评论了解详细信息。


这也太复杂了。我不明白为什么有人会写cond ? true : false(或反之)而没有注意到那是多么愚蠢的事情。 - Konrad Rudolph
为了清晰起见,该行应为return !(new Date(year, 1, 29, 0, 0).getMonth() == 2);或者,确切地说,它应该是return new Date(year, 1, 29, 0, 0).getMonth() != 2; - Jesse Millikan
实际上,这种表示法并不像它看起来那么愚蠢,当涉及到未定义的结果可能会影响进一步的数据时。我从一个真正工作的系统中拿了这个例子,将其更改为“政治正确”的形式会完全破坏该系统 :) - Thevs
@Thevs:我不明白在这种情况下改变表单会如何破坏任何东西。既然旧代码和新代码似乎都返回真正的布尔值(true/false),而不仅仅是返回可能是真实值的对象。 - jsight
这段代码应该保持隐藏。至少是它的使用示例,因为它效率低下。只在测试套件中使用它来检查更快的实现,例如 !(year % 4 != 0 || (year % 100 == 0 && year % 400 != 0)) - dolmen
显示剩余2条评论

13

我最喜欢的技巧是使用apply来执行一个对象的方法并保持正确的"this"变量。

function MakeCallback(obj, method) {
    return function() {
        method.apply(obj, arguments);
    };
}

var SomeClass = function() { 
     this.a = 1;
};
SomeClass.prototype.addXToA = function(x) {
     this.a = this.a + x;
};

var myObj = new SomeClass();

brokenCallback = myObj.addXToA;
brokenCallback(1); // Won't work, wrong "this" variable
alert(myObj.a); // 1


var myCallback = MakeCallback(myObj, myObj.addXToA);
myCallback(1);  // Works as expected because of apply
alert(myObj.a); // 2

13

闭包的禅意

其他人已经提到了闭包。但是令人惊讶的是,有很多人知道有关闭包的知识、使用闭包编写代码,但仍然对闭包的真正含义存在误解。有些人将一级函数与闭包混淆。而另一些人则将其视为静态变量的一种形式。

对我来说,闭包就是一种类似于“私有”全局变量的东西。也就是说,某些函数将其视为全局变量,而其他函数则无法访问它。虽然这样描述底层机制可能有些不严谨,但它的行为和感觉就是如此。下面是一个例子:

// Say you want three functions to share a single variable:

// Use a self-calling function to create scope:
(function(){

    var counter = 0; // this is the variable we want to share;

    // Declare global functions using function expressions:
    increment = function(){
        return ++counter;
    }
    decrement = function(){
        return --counter;
    }
    value = function(){
        return counter;
    }
})()

现在,三个函数incrementdecrementvalue共享变量counter,而不需要counter成为实际的全局变量。这就是闭包的真正本质:

increment();
increment();
decrement();
alert(value()); // will output 1

上面的示例并不是一个真正有用的闭包用法。事实上,我会说以这种方式使用它是一种反模式。但它在理解闭包的本质方面是有用的。例如,当大多数人尝试执行以下操作时,他们会陷入困境:

for (var i=1;i<=10;i++) {
    document.getElementById('span'+i).onclick = function () {
        alert('this is span number '+i);
    }
}
// ALL spans will generate alert: this span is span number 10

这是因为他们不理解闭包的本质。他们认为他们将i的值传递给函数,而实际上函数共享一个单一的变量i。就像我之前说的,这是一种特殊的全局变量。

要解决这个问题,您需要“分离”*闭包:

*原文中使用了斜体,此处翻译为中文的“分离”。

function makeClickHandler (j) {
    return function () {alert('this is span number '+j)};
}

for (var i=1;i<=10;i++) {
    document.getElementById('span'+i).onclick = makeClickHandler(i);
}
// this works because i is passed by reference 
// (or value in this case, since it is a number)
// instead of being captured by a closure

*注意:我不知道这里的正确术语。


这是一篇非常有用的文章,谢谢。只有一个小问题。在for循环中,应该是i=1而不是i=i。另外,我无法让解决方法起作用。也许你的意思是这个:function makeClickHandler(j) { return function() { alert('这是第' + j + '个span'); }; } - Steve
@Steve:感谢你发现了这些漏洞。 - slebetman

13

以下是一些快捷方式:

var a = []; // equivalent to new Array()
var o = {}; // equivalent to new Object()

使用“var a = [];”无法创建指定大小的数组。您需要使用“var a = new Array(arraySize);”。 - Rajat
1
在 JavaScript 中声明一个指定大小的数组是否具有可衡量的性能优势? - travis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接