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 这个数值复制到一块内存,并以 x 表示这块内存
- 把 x 这块内存存储的值,复制到另一块内存,并以 y 表示
简单点说就是:
- 把 1 复制给 x
- 把 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。上面的代码表示什么?
- 从内存中领取一块内存,用 x 表示
- 填充这块内存,第一段用整数 1 填充,用 a 表示
- 填充这块内存,第二段用整数 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 填充内存第一段,用符号 a 表示
- 用数值 3 填充内存第二段,用符号 b 表示
- 把这块内存的地址,也就是这块内存相对内存最开始的位置,用符号 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);
上面的代码发生了什么?
- 从内存领取一块内存,填充函数的数据,并把内存的地址用符号 f 表示
- 调用 f(100),从函数所属的内存中领取一块内存,把整数 100 复制给这块内存,并用符号 a 表示
- 输出符号 a 存储的数据
- 释放符号 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),并使用这块代码的任何变量和数据,以及函数。
善用函数式的嵌入,和上下文关联,可以让你将大问题逐步的变小。