高性能编程的秘密

var x = 1; 与 int x = 1;

当我们做这样的赋值语句时,代表什么意思?

var x = 1;      int x = 1;

= 的意思是 复制, copy

而不是 使,也绝对不是什么 指向!当然你可能在教科书听说过指向这种说法。

请记住, = 的意思是 复制

上面的代码告诉我们:把 1 这个数值复制给 x。

x 是什么?

x 是一个符号,代指一块内存。

内存?

我们用内存来存储一切数据。你想要存储一块数据吗?向内存要一块,比如 var x = 1 ,内存就会给你一块内存,并用 x 表示这块内存。

var x = 1; var y = x;

var x = 1;        int x = 1;
var y = x;        int y = x;

根据上面的说明,我们可以清晰的表达这段代码的意思:

  1. 把 1 这个数值复制到一块内存,并以 x 表示这块内存
  2. 把 x 这块内存存储的值,复制到另一块内存,并以 y 表示

简单点说就是:

  1. 把 1 复制给 x
  2. 把 x 的值复制给 y

所以,在内存中 x y 是这样表示的:

+-----+
|  1  | x
+-----+

...

+-----+
|  1  | y
+-----+

内存中存储了两个 1,而不是一个 1。
符号 x y,分别表示这两块内存。

var x = { a:1, b:2 }

var x = {};        struct x *x = malloc(/*sizeof*/);
x.a = 1;           (*x).a = 1;
x.b = 2;           (*x).b = 2; 

让我们进一步,来看看 Object,也就是 C 中的 Struct。上面的代码表示什么?

  1. 从内存中领取一块内存,用 x 表示
  2. 填充这块内存,第一段用整数 1 填充,用 a 表示
  3. 填充这块内存,第二段用整数 2 填充,用 b 表示

当我们探讨 x 的时候,我们知道我们指的是 {a:1, b:2} 表示的内存。

x = { a:1, b:2 },x 是什么?

我得告诉你,x 不是内存数据,而是地址!是数据 { a:1, b:2 } 在内存中的地址,也就是第几块内存。

为什么我们不用 x 直接表示 { a:1, b:2 } 数据,而是表示地址?

e,为了性能。

什么影响到性能?

复制!

当我们这样做:

var x = {a:1, b:2};
var y = x;

我们首先从内存中领取内存,填充 {a:1, b:2} 的数据,并以 x 表示。然后我们领取另一块内存,把 x 的数据复制给这块内存,并以 y 表示。

记住,= 是复制的意思!!!

这时候你会看到,从 x 复制到 y,我们会复制两个数据 a 和 b 。如果 x 中的数据很多,那么从 x 复制到 y 会是浪费时间的。

简单的方法!

所以,在编译器中,当 x = {a:1, b:2} 的时候跟 x = 1 有了本质的不同:

  1. 从内存中领取一块内存
  2. 用数值 1 填充内存第一段,用符号 a 表示
  3. 用数值 3 填充内存第二段,用符号 b 表示
  4. 把这块内存的地址,也就是这块内存相对内存最开始的位置,用符号 x 表示

这样,x 表示内存的地址,而不是数据。同时你也看到,x 的地址是领取内存开始的地址,也就是第一段内存的起始地址。

     x:地址
       |
       v
-------+-------+------
       | 1 | 2 |
-------+-------+------
         a   b

数组也是一样的!

var a = [];
a[0] = 1;
a[1] = 2;

数组其实也是相同的道理!

那么函数?

function f() {

}

var f1 = f;
var f2 = f1;

函数也是相同的道理!

f 是表示函数的地址
f1 复制 f 存储的值
f2 复制 f1 存储的值

你不知道的函数参数!

function f(a) {
    console.log(a);
}

f(100);

上面的代码发生了什么?

  1. 从内存领取一块内存,填充函数的数据,并把内存的地址用符号 f 表示
  2. 调用 f(100),从函数所属的内存中领取一块内存,把整数 100 复制给这块内存,并用符号 a 表示
  3. 输出符号 a 存储的数据
  4. 释放符号 a 表示的内存

记住,f 表示的内存并没有被释放。

所以,你看到了,f(100) 其实是复制 100 ,填充到函数所属的其中一块内存中。一个潜在的问题就是当你复制一大段文本字符串的时候,会发生严重的性能问题:

function f(text, i) {
    console.log(text.charAt(i));
} 

var text = '这是一段文字,很长 .........';

f(text, 0);
f(text, 1);
f(text, 2);
f(text, 3);
f(text, 4);
f(text, 5);

表面看,没有任何问题,但是!

你把 text 文本复制了 6 次给函数 f 所属的内存,并释放了 6 次!

你可以不停调用 f(text, i),但是你也在不停的复制释放一大段内容。

要知道字符串可是由很多个数据组成的,而整数只是一个数据。

'a' 在 JavaScript 中表示一个 16 位的内存,而 1 则表示一个 32 位的内存,x = {a:1} 中的 x 表示一个 64 位的内存。

'abcdefghij' 则表示 160 位的内存!

试想一下你可以把 'abcdefghij' 的长度提升 100 倍长度,那就是 16000 位的内存,也就是相当 16K 内存!如果你不停的复制释放 16K 内存,那么这可不是什么好的编程建议!

好的方法是什么?

还记得 x = {a:1} 中的 x 是表示地址吗? 地址只有 64 位,这可是很廉价的!

function f(object, i) {
    console.log(object.text.charAt(i));
} 

var object = {
    text: '这是一段文字,很长 .........'
};

f(object, 0);
f(object, 1);
f(object, 2);
...

这次,我们不再复制整个字符串,而是复制包含字符串的内存的地址 object。

函数式编程,语法?

如果你会说英语,而我只会说汉语,那就表示你懂函数式而我不懂,这不是太滑稽了嘛!!!

无论如何,语言,除非设计的有缺失,比如 PHP,一般不大可能会相比另一个语言有绝对的使用方式优势。

define(f
  (lambda (x y g) (g x y)))

define(g
  (lambda (x y) (+ x y)))

(f (1 2 g))

因为你会这种语法,才能表示这样才能写函数式?

NONONO

function f(x, y, g) {
    return g(x, y);
}

function g(x, y) {
    return x + y;
}

f(1, 2, g);

我们需要的是机制!而不是语法!

如何善用 JavaScript 函数式?

我们列出一个文本

Hi, tom!
How are you doing?

HiHiHi!

假入我想从这段文本中提取所有的 Hi,然后对其做些操作,做什么操作我现在也没想好,但是我可以先写提取所有的 Hi 的代码,这就是函数式的优势:

function parse(text, f) {
    var match;
    for (;;) {
        match = /Hi/.exec(text);
        if (match === null) {
            break;
        }
        f(match);
    }
}

如你所见,这里用正则表达式提取 Hi,并做了一个循环,直到匹配结束。

现在,使用这个 parse(text, f) 最佳的方式是什么?

绝对不是简简单单的调用:parse(text, function () {...})。

函数式最大的优势是:让你可以拥有一个语境,也就是上下文。

function test(text) {
    var i = 0;
    var bala = 'haha';
    var x = 1;
    var y = 2;

    parse(text, function (match) {
        var z = x + y;   // 看到没,就是这里
                         // 我可以使用 x y,这是 parse 所没有的
    });
}

我可以在上面的 parse() 中使用 x y,而这些内容是另外一个空间的内容。

函数式最大的优势就是让你将一块操作代码 (parse),嵌入到另一块操作代码中 (test),并使用这块代码的任何变量和数据,以及函数。

善用函数式的嵌入,和上下文关联,可以让你将大问题逐步的变小。

你可能感兴趣的:(高性能编程的秘密)