本文介绍了JavaScript中Array.prototype.sort方法的一些奇怪的特性。

我爱好网站前端设计,以下摘自的我的博客,原文链接: http://zhangwenli.com/blog/2013/11/02/sorting-in-javascript/

Method of sorting an array is defined in Array.prototype.sort. So in most cases, you don't have to think about how to implement a sort algorithm when writing in JavaScript. But you still need to be careful when you use it, since JavaScript is fragile.

What's The Problem?

JavaScript provides sorting method for Array type, so that you don't have to write it by yourself.

Don't celebrate too early! JavaScript may not behave in the way you think it should.

var colors = ['red', 'green', 'blue', 'yellow', 'white'];
console.log(colors.sort());    // ["blue", "green", "red", "white", "yellow"]
console.log(colors);           // ["blue", "green", "red", "white", "yellow"]

Two things we can know from the above example are: Firstly, String type is sorted in an alphabetic order. Secondly, sort() method changes the original array rather than generating a new array.

But it's not sorted in an alphabetic order in fact.

var colors = ['red', 'green', 'blue', 'Yellow', 'White'];
console.log(colors.sort());    // ["White", "Yellow", "blue", "green", "red"]

It compares by each character's ASCII code so that uppercase letters are always smaller than lowercase ones.

Another thing you need to be careful with is when you sort an array with numbers.

var money = [12, 3, 7.4, 200];
console.log(money.sort());    // [12, 200, 3, 7.4]

Surprise?

Why

By default, the sort() method converts everything into String and then compares letter by letter. You may wonder why JavaScript isn't smart enough to tell this is an array of numbers and then sort it by numbers' value.

But if you think twice, you may find that an array can contain different types in JavaScript, so what do you expect to get from the following sorted array?

var everything = ['Red', '$200', 'white', 7.4, 12, true, 0.3, false];
console.log(everything.sort());

Although I doubt it's a good idea to put all these different types together in an array, this is totally legal in JavaScript. If you think 12 should be thought as larger than 7.4, then, how to sort the whole array?

OK. Here's how JavaScript deals with it: Convert to String and compare letter by letter in ASCII code order. So true is converted to "true" and false is converted to "false" when comparing.

var everything = ['Red', '$200', 'white', 7.4, 12, true, 0.3, false];
console.log(everything.sort()); 
// ["$200", 0.3, 12, 7.4, "Red", false, true, "white"]

Comparing Function

Sorting arrays with numbers only is still a very common need. So how can I sort them by comparing numbers' value?

Pass a comparing function to it!

Comparing Numbers

var money = [12, 3, 7.4, 200];
var compare = function(a, b) {return a - b;};
console.log(money.sort(compare)); // [3, 7.4, 12, 200]

Now, you get what you wanted.

What happens here is that compare function takes two element and returns a negative number if a is smaller than b, 0 if a is equal to b, a positive number if a is larger than b.

Comparing Objects

Comparing function is also useful when we compare objects. Let's say that we want to sort people by their id rather than their name, we can define a function to compare people by id.

var people = [{
    name: 'Alice',
    id: 1234
}, {
    name: 'Bob',
    id: 567
}];
var compare = function(a, b) {return a.id - b.id;}
console.log(people.sort(compare)); // Bob is before Alice now

Compare Everything

What if we sort everything with compare?

var everything = [4, 'Red', '$200', 'white', 7.4, 12, true, 0.3, false];
var compare = function(a, b) {return a - b;};
console.log(everything.sort(compare)); 

Chrome and Opera: [4, "Red", "$200", "white", false, 0.3, true, 7.4, 12];

Firefox: [false, 0.3, true, 4, "Red", "$200", "white", 7.4, 12];

IE: ["Red", "$200", false, 0.3, true, 4, "white", 7.4, 12].

This is so weird!

When comparing, a - b is calculated in compare so that every element in everything is converted to Number when comparing and then do the minus operation. As Number('Red'), Number('$200') and Number('white') give NaN all, comparing function compare returns NaN when they compare to any other element. With the existance of this NaN, the behavior of Array.prototype.sort is not defined in the ECMA Specification, so it depends on each platform's implementation.

Calling comparefn(a,b) always returns the same value v when given a specific pair of values a and b as its two arguments. Furthermore, Type(v) is Number, and v is not NaN. Note that this implies that exactly one of a <CF b, a =CF b, and a >CF b will be true for a given pair of a and b.

ECMA 262 Specification

The lesson learned here is that the behavior of sorting with a comparing function that will return NaN is unpredictable and you should be careful when choosing a suitable comparing function. Again, it's not a good idea to put everything with different types all together in the same array.

关于作者

我的博客主要关注于网站前端设计和编程,有兴趣的话可以订阅RSS,谢谢支持!另外,我在图灵社区出版了电子书《Three.js入门指南》,感兴趣的程序猿可以看看~

我的邮箱是OviliaZhang # gmail.com,欢迎交流!