用 markdown 转换了下,貌似还是有点问题,大家凑合凑合(字数限制原文加不上去了),如果想体验舒适的阅读,可以点击原文: https://blog.jing.do/4212
前言:本篇文章是我在查询时偶然间发现的,虽然年代久远但是依旧非常适合入门学习,特此翻译下分享给大家,顺便给大家加了一些备注方便阅读(特意加粗刷存在感)。原文全文和链接在最后
———————-以下是正文———————-
备注:本篇文章是 34 岁的程序员 Ayman Hourieh 在 2006 年发布的。我找到了他网站的链接,但是网站已经不存在了。我用 Wayback Machine 找到了文章的镜像。为了让更多人看到把他摘录分享出来。我只做了一些链接的修改。
JavaScript 是一个功能强大的面向对象语言。表面上看他他和 JAVA 和 C 非常相似,但是他却截然不同,其核心在于 JavaScript 更像一个功能性语言。本篇文章是一些 JavaScript 的小提示,一部分提供一些类 C 语言的功能模拟,另一部分是想要提高性能并探讨下脚本语言中一些比较难懂的东西。索引如下:
尽管 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 循环一样简单。但是,这种方法的缺点是随着树的深度增加,浪费的存储空间越来越多。
大家都知道,如果做太多的字符串链接会让性能下降(译者注:不知道的去补课),并且这个非常容易避免。如果你想要用各个字符来组成一个字符串,最差的方法是使用+把所有的字符结合到一起:
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 是一种常用的调试技术,它用于确保表达式在执行期间计算为真。 如果表达式计算为假,则表示代码中可能出现的错误。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
大家知道,一些语言像 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 的所有逻辑封装在主体中,但第二个例子更为有效。
因为 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
每个点都导致一个操作来检索一个属性,在一个循环中,这些操作是相加的,所以最好做一次,将对象存储在变量中并重新使用它。
Firefox 有一个非常棒的扩展,用于调试名为 Firebug 的 JavaScript 代码。 它提供一个具有断点和堆栈视图的调试器,以及一个 JavaScript 控制台。 它还可以监视 Ajax 请求。 此外,扩展提供了一组 JavaScript 函数和对象来简化调试。 您可以在 Firebug 的文档页面中详细研究它们。 这里有一些我觉得有用的:
熟悉 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 消息的方法,这个将比 alert 更加好用, console.log()
有点像 C 里面的printf
,他会将输入转化为字符串在 console 中展示:
var i = 1;
console.log('i value is %d', i);
// prints:
// i value is 3
此方法打印一个堆栈跟踪调用它。 它不需要输入参数
该功能需要一个参数。 它切换到检查选项卡并检查传递的对象。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.