Heim Web-Frontend js-Tutorial 让JavaScript拥有类似Lambda表达式编程能力的方法_javascript技巧

让JavaScript拥有类似Lambda表达式编程能力的方法_javascript技巧

May 16, 2016 pm 06:19 PM
lambda Programmierfähigkeit 表达式

但是我后来也跟人说,因为接受的参数太多,所以如果不把智能提示写得很清楚的话,连我自己都常常搞不清楚该怎么用。

不过,接受参数多,除了容易弄错用法以外,还会产生另一个问题,这也是我编写出今天发布的这个东西的原因。
来看一下JS版的页码呈现组件的完全版函数签名:

复制代码 代码如下:

function pnView(
currentPage, actionCurrent,
beginPage, endPage,
actionBegin, actionEnd,
currentSiblings, actionCurrentSibling,
preventFolding, actionFolding,
endOfBegin, beginOfEnd,
actionBeginSibling, actionEndSibling
)

可以看到,这个可以最大幅度进行自定义的完全版函数签名,接受14个参数,而其中有一半——也就是7个参数,是要接受回调函数的。
由此产生的问题就是需要手工写入多次的“function(){}”等字符组合,就像这样:
复制代码 代码如下:

function ww(s) { document.write(s); }
function ws(n) { ww(' (' + n + ') '); }
pnView(14, function (n) { ww(' [' + n + '] ') },
1, 27,
function (n) { ww('| '); }, function (n) { ww(' ' + n + ' >|'); },
2, ws,
1, function () { ww(' ... '); },
2, 2,
ws, ws
);

当我在网页中测试这个组件的时候我就觉得自己非常想念C#中的Lambda表达式——虽然有些人、有些JS框架会把匿名函数称作“Lambda表达式”,但其实那只相当于C#中的“匿名委托/函数”,而Lambda的(表面)特征是使用简短的语法模式来反映一个(回调)函数/过程(或者说动作)的逻辑,而不被“delegate”或者别的什么关键字分散精力。

出于这样的原因,我编写了这个可以转译JS代码、把一种特定的模式(pattern)翻译成函数定义的模块。
在使用这个模块以后,前面的那个调用可以变成这个样子:
复制代码 代码如下:

eval(function () {
var ww = (s, document.write(s));
var ws = (n, ww(' (' + n + ') '));
pnView(14, (n, ww(' [' + n + '] ')),
1, 27,
(n, ww('| ')), (n, ww(' ' + n + ' >|')),
2, ws,
1, (0, ww(' ... ')),
2, 2,
ws, ws
);
}.lamda())();

模块的完整代码如下:
复制代码 代码如下:

/*!
L-amda "a-Lambda", a module provides Alternate "Lambda" style programming ability for JavaScript.
Created By NanaLich. 2010-09-08
This module is published under WTFPL v2, so you just DO WHAT THE Fxxx YOU WANT TO with it.
*/
!function () {
function attachEntry(o, a, m) {
var i, j, n;
o = [].concat(o);
//if (!(o instanceof Array)) o = [o];
while (i = o.shift()) {
for (j in a) {
if (!i[n = a[j]]) i[n] = m;
}
}
}
var rx0 = /^\s*(0|NaN|null)\s*,$/;
var rx1 = /([\W]\s*)\((\s*0\s*,|(?:\s*[a-z_$][\w$]*\s*,)+)|"(\\[\s\S]|[^\x22])*"|'(\\[\s\S]|[^\x27])*'/gi;
var rx2 = /\)/g;
function rxGetFlags(rx) { // 取出正则表达式的创建选项
return (rx.global ? 'g' : '') + (rx.ignoreCase ? 'i' : '') + (rx.multiline ? 'm' : '');
//return /\/([gim]*)$/.exec('' + rx)[1];
}
attachEntry(RegExp, ['getFlags'], rxGetFlags);
attachEntry(RegExp.prototype, ['getFlags'], function () {
return rxGetFlags(this);
});

function translateLambda(s) {
if (typeof (s) != 'string') s = '' + s;
var x = new RegExp(rx1.source, rx1.getFlags());
var y = new RegExp(rx2.source, rx2.getFlags()); // 由于firefox、safari等浏览器对全局匹配正则表达式有过度的优化,所以这里采用一种迂回的办法创建不重复的正则表达式实例
var m, l = 0, r = '';
while (m = x.exec(s)) {
var c = m[0], h, p, q, t;
switch (c.charAt(0)) { // 判断期待的语法成分
case '$':
case ')':
case ']':
case '"':
case "'":
continue; // 函数传参,跳过
}

h = m[2];
if (rx0.test(h))
h = '';
else
h = h.substr(0, h.length - 1); // 去掉末尾的逗号
r += s.substring(l, p = m.index); // 在结果字符串上附加之前余留的内容
y.lastIndex = l = p + c.length; // 从逗号之后开始寻找右括号
while (q = y.exec(s)) {
q = q.index;
try {
t = 'return ' + s.substring(l, q) + ';';
new Function(t); // 语法测试
//r += c + 'function(' + h + '){ ' + translateLambda(t) + ' }'; // 翻译里面的内容
r += m[1] + 'function(' + h + '){ ' + translateLambda(t) + ' }'; // 翻译里面的内容
x.lastIndex = l = q + 1; // 下一次查找从括号之后开始
break;
} catch (ex) { }
}
if (!q) l = p; // 说明找不到右括号或者有效的代码,直接附加所有匹配的内容
}
try {
r += s.substr(l);
if (/[\w$]*|"(\\[\s\S]|[^\x22])*"|'(\\[\s\S]|[^\x27])*'/.exec(r)[0] == 'function') // 粗略判断产生的是不是函数(可以应付绝大部分情况)
r = '0,' + r; // 使用这种“怪”形式可以在所有浏览器(包括IE)中得到预期的效果
new Function(r); // 语法测试
return r;
} catch (ex) { // 失败,返回原文
return s;
}
};

var lamdaAliases = ["translateLambda", "lambda", "lamda"];
attachEntry([Function, String], lamdaAliases, translateLambda);
attachEntry([Function.prototype, String.prototype], lamdaAliases, function () {
return translateLambda(this);
});
} ();

如果和C#中的Lambda表达式相比的话,我的这个模块还是有很多不足的,不过现在这个样子已经让我很满意了,至少我不用再写太多的“function”了。
简单来说,这个模块的规格特性是这样的——
优点:
减少编写代码时“function”的出现次数;
使用可以在一般的JavaScript编辑器中正常编辑的语法模式(pattern),直接写在函数体中不会导致语法错误。
局限性:
在任何时候使用这个模块都必须调用转译方法(“translateLambda”、“lambda”或者“lamda”)和eval函数,无法省略;
如果存在一个函数A,不可能通过对A进行处理来达到转译传递至A的参数的目的(也就是说a.lambda()或者类似的操作并不会让a((x, x * 2))等同于a(function(x){ return x * 2; }));
不能包含表达式之外的任何语句、不能包含使用“;”来分隔的多条语句。
缺点:
连续出现的括号可能会让代码变得难以理解;
任何编辑器都无法为Lambda表达式提供语法高亮;
存在错误地转译现有代码的可能性——这个模块选择进行匹配的模式是在正常的代码中没有实用价值、也通常不会出现的模式,如:(x, x * 2)等价于单纯的x * 2、(0, a.method())等价于单纯的a.method(),所以这个缺点影响到实际代码的可能性无限趋近于0。
以下是几种不当的用法:
1、使用这个模块并不会节省很多代码量的时候:本末倒置。
复制代码 代码如下:

eval(function(){ // 不仅没减少代码量,反而还增加了
window.onload = (0, alert('载入完成!'));
}.lambda());

2、对接受参数的函数进行转译处理:之前已经提到过这种情况。
复制代码 代码如下:

eval(somefunction.lambda())((x, x.toString(16))); // somefunction可能会产生预料外的结果,而且收到的参数会是x.toString(16)的结果(如果x在此处并没有被定义过,还会产生一个“变量不存在”的异常),而非一个回调函数。

3、为了使用此模块而规避语法检查:
因为使用的是在JavaScript中有效但无实用价值的语法,所以规避语法检查是完全没有必要的。
复制代码 代码如下:

eval("somefunction((x, x.toString(16)))".lamda()); // 失去了语法检查,可能在运行的时候产生意外

4、在(伪)Lambda中使用过多的操作,甚至多条语句:
在设计这个模块的时候我并没有找出可以使用多条语句且可以通过语法检查的模式(pattern),原因是在Lambda表达式中使用多条语句时,“function”、“return”等几个字符所增加的代码量通常是可以忽略的,这样去使用Lambda表达式本身就属于一种偏离了初衷的做法。
复制代码 代码如下:

eval(function(){ somefunction((x, y.something(x); return x.toString(16))); }.lamda())(); // 语法错误
eval(function(){ somefunction((x, y.something(x), x.toString(16))); }.lamda())(); // 容易产生理解上的歧义
eval(function(){ somefunction((x, ++x)); }.lamda())(); // 简单的表达式可以被接受

最佳使用场合:
现在很多人写JavaScript的时候喜欢把自己的代码都写在一个闭包里面,这样可以避免全局作用域污染问题,就像这样:
复制代码 代码如下:

(function(){
// 所有的代码都放在这里
})();

——而这种“大”闭包正好是使用本模块的最佳场合:
复制代码 代码如下:

eval(function(){ // 括号前增加eval
// 所有的代码都放在这里
}.lamda())(); // 括号里增加.lamda()

昨天codeplex抽疯,代码和release上传总出错。再考虑到这个模块的使用场合比较有限,不适合缺乏JavaScript经验的人使用,所以不另外提供源代码打包下载——有需要的话请直接从文中复制。
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Wie behandeln Lambda-Ausdrücke Ausnahmen in C++? Wie behandeln Lambda-Ausdrücke Ausnahmen in C++? Apr 17, 2024 pm 12:42 PM

In C++ gibt es zwei Möglichkeiten, Ausnahmen mit Lambda-Ausdrücken zu behandeln: Die Ausnahme mit einem Try-Catch-Block abfangen und die Ausnahme im Catch-Block behandeln oder erneut auslösen. Mithilfe einer Wrapper-Funktion vom Typ std::function kann die Methode try_emplace Ausnahmen in Lambda-Ausdrücken abfangen.

Was bedeutet der Abschluss im C++-Lambda-Ausdruck? Was bedeutet der Abschluss im C++-Lambda-Ausdruck? Apr 17, 2024 pm 06:15 PM

In C++ ist ein Abschluss ein Lambda-Ausdruck, der auf externe Variablen zugreifen kann. Um einen Abschluss zu erstellen, erfassen Sie die äußere Variable im Lambda-Ausdruck. Abschlüsse bieten Vorteile wie Wiederverwendbarkeit, Ausblenden von Informationen und verzögerte Auswertung. Sie sind in realen Situationen nützlich, beispielsweise bei Ereignishandlern, bei denen der Abschluss auch dann noch auf die äußeren Variablen zugreifen kann, wenn diese zerstört werden.

Welche Vorteile bietet die Verwendung von C++-Lambda-Ausdrücken für die Multithread-Programmierung? Welche Vorteile bietet die Verwendung von C++-Lambda-Ausdrücken für die Multithread-Programmierung? Apr 17, 2024 pm 05:24 PM

Zu den Vorteilen von Lambda-Ausdrücken in der C++-Multithread-Programmierung gehören Einfachheit, Flexibilität, einfache Parameterübergabe und Parallelität. Praktischer Fall: Verwenden Sie Lambda-Ausdrücke, um Multithreads zu erstellen und Thread-IDs in verschiedenen Threads zu drucken, was die Einfachheit und Benutzerfreundlichkeit dieser Methode demonstriert.

Wie implementiert man einen Abschluss im C++-Lambda-Ausdruck? Wie implementiert man einen Abschluss im C++-Lambda-Ausdruck? Jun 01, 2024 pm 05:50 PM

C++-Lambda-Ausdrücke unterstützen Abschlüsse, die Funktionsbereichsvariablen speichern und sie für Funktionen zugänglich machen. Die Syntax lautet [capture-list](parameters)->return-type{function-body}. Capture-Liste definiert die zu erfassenden Variablen. Sie können [=] verwenden, um alle lokalen Variablen nach Wert zu erfassen, [&], um alle lokalen Variablen nach Referenz zu erfassen, oder [Variable1, Variable2,...], um bestimmte Variablen zu erfassen. Lambda-Ausdrücke können nur auf erfasste Variablen zugreifen, den ursprünglichen Wert jedoch nicht ändern.

Wie erfasst ein C++-Lambda-Ausdruck externe Variablen? Wie erfasst ein C++-Lambda-Ausdruck externe Variablen? Apr 17, 2024 pm 04:39 PM

Es gibt drei Möglichkeiten, Lambda-Ausdrücke externer Variablen in C++ zu erfassen: Erfassung nach Wert: Erstellen Sie eine Kopie der Variablen. Erfassen nach Referenz: Rufen Sie eine Variablenreferenz ab. Gleichzeitige Erfassung nach Wert und Referenz: Ermöglicht die Erfassung mehrerer Variablen, entweder nach Wert oder nach Referenz.

C++-Funktionsaufruf Lambda-Ausdruck: Callback-Optimierung für Parameterübergabe und Rückgabewert C++-Funktionsaufruf Lambda-Ausdruck: Callback-Optimierung für Parameterübergabe und Rückgabewert May 03, 2024 pm 12:12 PM

In C++ können Sie Lambda-Ausdrücke als Funktionsparameter verwenden, um die Flexibilität von Rückruffunktionen zu erreichen. Konkret: Parameterübergabe: Wickeln Sie den Lambda-Ausdruck durch std::function und übergeben Sie ihn in Form eines Funktionszeigers an die Funktion. Rückgabewertverarbeitung: Geben Sie den Rückgabewerttyp an, wenn Sie den Rückruffunktionszeiger mit std::function deklarieren. Praktischer Fall: Optimieren Sie Rückrufe bei der GUI-Ereignisverarbeitung, vermeiden Sie die Erstellung unnötiger Objekte oder Funktionszeiger und verbessern Sie die Einfachheit und Wartbarkeit des Codes.

Wie führt man eine verzögerte Auswertung mit C++-Lambda-Ausdrücken durch? Wie führt man eine verzögerte Auswertung mit C++-Lambda-Ausdrücken durch? Apr 17, 2024 pm 12:36 PM

Wie führt man eine verzögerte Auswertung mit C++-Lambda-Ausdrücken durch? Verwenden Sie Lambda-Ausdrücke, um verzögert ausgewertete Funktionsobjekte zu erstellen. Eine verzögerte Berechnung verzögert die Ausführung, bis sie benötigt wird. Berechnen Sie Ergebnisse nur bei Bedarf und verbessern Sie so die Leistung.

Welche Techniken gibt es zur Leistungsoptimierung von C++-Lambda-Ausdrücken? Welche Techniken gibt es zur Leistungsoptimierung von C++-Lambda-Ausdrücken? Apr 17, 2024 pm 05:45 PM

Zu den Leistungstipps für die Optimierung von C++-Lambda-Ausdrücken gehören: Vermeidung unnötiger Erstellung von Lambda-Objekten, explizite Erfassung des kleinsten Objekts über std::bind, Verwendung von std::move zum Verschieben erfasster Variablen, um Kopieren zu vermeiden, Optimierung des Lambda-Körpers, um unnötige Speicherzuweisungen und wiederholte Berechnungen zu vermeiden. und globaler Variablenzugriff

See all articles