9 个 Javascript 你可能不知道的知识点 (9 JavaScript Tips You May Not Know)

2017-07-12 02:15:25 +08:00
 taojing10

用 markdown 转换了下,貌似还是有点问题,大家凑合凑合(字数限制原文加不上去了),如果想体验舒适的阅读,可以点击原文: https://blog.jing.do/4212

前言:本篇文章是我在查询时偶然间发现的,虽然年代久远但是依旧非常适合入门学习,特此翻译下分享给大家,顺便给大家加了一些备注方便阅读(特意加粗刷存在感)。原文全文和链接在最后

———————-以下是正文———————-

备注:本篇文章是 34 岁的程序员 Ayman Hourieh 在 2006 年发布的。我找到了他网站的链接,但是网站已经不存在了。我用 Wayback Machine 找到了文章的镜像。为了让更多人看到把他摘录分享出来。我只做了一些链接的修改。

JavaScript 是一个功能强大的面向对象语言。表面上看他他和 JAVA 和 C 非常相似,但是他却截然不同,其核心在于 JavaScript 更像一个功能性语言。本篇文章是一些 JavaScript 的小提示,一部分提供一些类 C 语言的功能模拟,另一部分是想要提高性能并探讨下脚本语言中一些比较难懂的东西。索引如下:

多用途的 Array

尽管 JavaScript 在数据结构方面看起来很奇特,他的 Array 比其他编程语言(如 C ++或 Java )用途更加广泛。 它通常用作数组或关联数组,后面将演示如何将其用作堆栈,队列和二叉树。 复用 Array 而不是编写其他的数据结构有两个好处:首先,不用浪费时间去重写已经存在的功能,其次,内置浏览器对 JavaScript 的运行将更高效。

大家都知道栈是后进先出:最后插入的会被最先移除。array 有两个方法来实现栈的功能。他们是push() 和 pop()。 push()用于插入 item 到 array 的尾部,而pop() 则用于移除并返回最后一个 item。以下是代码的实例:

var stack = [];
stack.push(2);       // 当前栈是 [2]
stack.push(5);       // 当前栈是 [2, 5]
var i = stack.pop(); // 当前栈是 [2]
alert(i);            // 显示 5

队列

队列是先进先出的:现行插入的将会被最先移除。array 可以用 push() 和 shift() 来实现队列。 push() 用于插入 item 到尾部, shift()用于移除第一个 item。案例如下:

var queue = [];
queue.push(2);         // 现在的队列 [2]
queue.push(5);         // 现在的队列 [2, 5]
var i = queue.shift(); // 现在的队列 [5]
alert(i);              // 显示 2

值得注意的是,array 还有一个unshift()的函数。这个函数用于将 item 放到 array 的头部。所以栈也可以使用 unshift()/shift(),而队列可以用unshift()/pop()

如果这些函数名称让你迷茫了(译者注:因为unshift()是处理头部,所以相对应的栈需要从头部出去,队列需要换到尾部),你可以给他们取个别名,比如,创建队列的方法名为add 和 remove

var queue = [];
queue.add = queue.push;
queue.remove = queue.shift;

queue.add(1);
var i = queue.remove();
alert(i);

二叉树

二叉树是在树的节点表示数据。每个节点有一个数据并且有两个子节点(左叉树和右叉树)。在 C 语言里,这种数据结构通常使用指针来实现。这种实现方法也可以在 JavaScript 中使用对象和引用来实现。然而,对于一些小的二叉树,有一种更简单便捷的方法,array 的第一个 item 作为树的头。 如果可以使用以下公式计算节点 i,则索引左右节点:

leftChild(i) = 2i + 1
rightChild(i) = 2i + 2

这张图揭示了这个算法: (来自于 Wikipedia):![9 个 Javascript 的知识点 (9 JavaScript Tips You May Not Know)]( https://blog.jing.do/wp-content/uploads/2017/07/9 个 javascript 的知识点-9-javascript-tips-you-may-not-know.png "9 个 Javascript 的知识点 (9 JavaScript Tips You May Not Know)")

正如你所看到的,这种方法并不是 JavaScript 的独有之处,但是在处理小的二叉树时非常有用。 例如,您可以编写自己的函数来获取和设置节点的值或子节点,并遍历二叉树,这些方法与做计算或 for 循环一样简单。但是,这种方法的缺点是随着树的深度增加,浪费的存储空间越来越多。

String Concatenation 对比 Array.join

大家都知道,如果做太多的字符串链接会让性能下降(译者注:不知道的去补课),并且这个非常容易避免。如果你想要用各个字符来组成一个字符串,最差的方法是使用+把所有的字符结合到一起:

str = '';
for (/* each piece */) {
  str += piece; // bad for performance!
}
return str;

这种方法将使用太多的字符串链接,会让性能枯竭。

在 JavaScript 中有个更好的办法,就是 Array.join(),他可以让所有 array 内的元素连接成一个字符串:

var tmp = [];
for (/* each piece */) {
  tmp.push(piece);
}
str = tmp.join(' '); // 用空格作为分隔符
return str;

该方法不会受到额外的字符串对象的影响,通常执行的非常高效

给对象绑定方法

任何使用 JavaScript 事件的人都可能遇到了一种情况,他们需要将对象的方法分配给事件处理程序。 这里的问题是事件处理程序会被 HTML 调用,即使它们最初被绑定到另一个对象。 为了解决这个问题,我用一个函数将对象和方法绑定; 它他会运行对象和方法,并返回一个在该对象调用该方法的函数。 我在 Prototype 中找到了一个窍门,并且写了以下函数来在不包含 Prototype 的项目中使用它:

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

如何使用:

var obj = {
  msg: 'Name is',
  buildMessage: function (name) {
    return this.msg + ' ' + name;
  }
}

alert(obj.buildMessage('John')); // displays: Name is John

f = obj.buildMessage;
alert(f('Smith')); // displays: undefined Smith

g = bind(obj, obj.buildMessage);
alert(g('Smith')); // displays: Name is Smith

使用自定义排序

排序是一个常见的工作。JavaScript 提供了一种排序数组的方法。 但是,该方法默认按字母顺序排列 —— 非字符串元素在排序之前被强制转换为字符串,这个使得数字排序会有意想不到的结果:

var list = [5, 10, 2, 1];
list.sort()
// list is now: [1, 10, 2, 5]

这个解释很容易: 数字被强制转换成了字符串,所以 10 编程了’ 10 ’而 2 变成了’ 2 ’,那么 JavaScript 对比两个字符串的时候,先对比第一个字符。如果 str1 的第一个字符出现在字符集中的第一个字符之前,则 str1 被认为是“小于” str2。 在我们的情况下,’ 1 ’在’ 2 ’之前,所以’ 10 ’小于’ 2 ’。

幸运的是,JavaScript 提供一个重写机制,让我们可以自定义如何排序,我们用 a 和 b 两个元素最为例子:

写这样的程序比较容易:

function cmp(a, b) {
  return a - b;
}

我们现在可以使用这个方法来做排序:

var list = [5, 10, 2, 1];
list.sort(cmp);
// list is now: [1, 2, 5, 10]

Array.sort() 牛逼的地方是允许更复杂的排序。 假设你有一个论坛帖子,每个帖子看起来像:

var post = {
  id: 1,
  author: '...',
  title: '...',
  body: '...'
}

如果你想用 id 排序,只需要创建以下函数:

function postCmp(a, b) {
  return a.id - b.id;
}

可以说,使用浏览器的方法进行排序将比在 JavaScript 中实现排序函数更有效。 但是,数据应该在服务器端进行排序。所以,除非必要,否则不应该让数据在客户端排序。

Assertion

Assertion 是一种常用的调试技术,它用于确保表达式在执行期间计算为真。 如果表达式计算为假,则表示代码中可能出现的错误。JavaScript 缺少一个内置的 Assertion 功能,但幸运的是,它很容易编写一个。 如果传递的表达式的计算结果为假,以下实现会抛出 AssertException 类型的异常:

function AssertException(message) { this.message = message; }
AssertException.prototype.toString = function () {
  return 'AssertException: ' + this.message;
}

function assert(exp, message) {
  if (!exp) {
    throw new AssertException(message);
  }
}

自己抛出异常并不是非常有用,但是当与有用的错误消息或调试工具结合使用时,您可以检测到有问题的部分。

您还可以使用以下代码段检查异常是否为 Assertion 异常:

try {
  // ...
}
catch (e) {
  if (e instanceof AssertException) {
    // ...
  }
}

该函数的使用类似于 C 或 Java:

assert(obj != null, 'Object is null');

如果 obj 碰巧为 null,Firefox 将在 JavaScript 控制台中打印以下消息:

uncaught exception: AssertException: Object is null

Static Local Variables

大家知道,一些语言像 C ++,他们有静态变量的概念,用于函数调用。JavaScript 并不支持此技术。 然而,“功能也是对象”让这个功能成为可能。 方法是:将静态变量变为函数的属性。 假设我们要创建一个计数器函数:

function count() {
  if (typeof count.i == 'undefined') {
    count.i = 0;
  }
  return count.i++;
}

当第一次调用 count 时,count.i 是未定义的,所以 if 条件为 true,count.i 为 0。因为我们将变量存储为一个函数属性,它将在函数调用之间保留它的值, 因此它可以被认为是一个静态变量。

这里有个性能提升小技巧:

function count() {
  return count.i++;
}
count.i = 0;

虽然第一个例子将 Count 的所有逻辑封装在主体中,但第二个例子更为有效。

null, undefined, and delete

因为 JavaScript 有undefined 和 null 所以他不同于其他语言。null是一个特别的数值,他表示没有数值。null会被认为一个特别的对象,因为typeof null会返回 null。

undefined表示变量没有被定义,或者定义了但没有给数值。以下情况都会显示undefined

// i is not declared anywhere in code
alert(typeof i);

var i;
alert(typeof i);

虽然undefined 和 null是两个不同的类型,但是如果使用 == ,会被判定为相等,但是如果是 === 则不等。

JavaScript 还有一个删除操作符,” undefines ”一个属性,可以将其应用于对象属性和数组成员,使用 var 声明的变量不能被删除,而是隐式声明( implicitly declared )的变量可以:

var obj = {
  val: 'Some string'
}

alert(obj.val); // displays 'Some string'

delete obj.val;
alert(obj.val); // displays 'undefined'

深层嵌套

如果您需要在深层嵌套对象上执行多个操作,最好将其引用到临时变量中,而不是每次对其进行解引用。 例如,假设您想在文本字段上执行一系列操作:

document.forms[0].elements[0]

建议您存储到变量中,并使用此变量而不是以上构造:

var field = document.forms[0].elements[0];
// Use field in loop

每个点都导致一个操作来检索一个属性,在一个循环中,这些操作是相加的,所以最好做一次,将对象存储在变量中并重新使用它。

Using Firebug

Firefox 有一个非常棒的扩展,用于调试名为 Firebug 的 JavaScript 代码。 它提供一个具有断点和堆栈视图的调试器,以及一个 JavaScript 控制台。 它还可以监视 Ajax 请求。 此外,扩展提供了一组 JavaScript 函数和对象来简化调试。 您可以在 Firebug 的文档页面中详细研究它们。 这里有一些我觉得有用的:

$ and $

熟悉 Prototype 的人马上就认出他们了,

$() 接受一个字符串参数,并返回其 ID 是传递的字符串的 DOM 元素。(译者注:Jquery

$('nav') // returns the element whose id is #nav.

$() 返回 DOM 的数组

$('div li.menu') // returns an array of li elements that are 
                  // located inside a div and has the .menu class

console.log(format, obj1, …)

console 对象提供显示 log 消息的方法,这个将比 alert 更加好用, console.log()有点像 C 里面的printf,他会将输入转化为字符串在 console 中展示:

var i = 1;
console.log('i value is %d', i);
// prints:
// i value is 3

console.trace()

此方法打印一个堆栈跟踪调用它。 它不需要输入参数

inspect(obj)

该功能需要一个参数。 它切换到检查选项卡并检查传递的对象。

1389 次点击
所在节点    问与答
4 条回复
msg7086
2017-07-12 05:32:49 +08:00
前几条和 JavaScript 都没啥关系……
binux
2017-07-12 05:57:33 +08:00
10 年前的内容还是别参考了。。
有些已经是错误的了
xcatliu
2017-07-12 08:41:16 +08:00
Sorry I know all of those
kyuuseiryuu
2017-07-12 08:48:15 +08:00
最烦这种你字派标题。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/374655

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX