Dieser Artikel bietet Ihnen eine Einführung in fünf Anwendungstechniken von JavaScript-Funktionen. Ich hoffe, dass er für Freunde hilfreich ist.
Funktion ist ein Kernkonzept für jede Sprache, insbesondere in JavaScript. In diesem Artikel werden 5 fortgeschrittene Fähigkeiten von Funktionen vorgestellt
Bereichssicherer Konstruktor
Der Konstruktor ist eigentlich eine Funktion, die mit dem neuen Operator aufgerufen wird
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } var person=new Person('match',28,'Software Engineer'); console.log(person.name);//match
If Wenn der neue Operator nicht verwendet wird, werden die drei Attribute, die ursprünglich auf das Personenobjekt abzielten, dem Fensterobjekt hinzugefügt.
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } var person=Person('match',28,'Software Engineer'); console.log(person);//undefinedconsole.log(window.name);//match
Das Namensattribut von Fenster wird verwendet, um das Linkziel und den Frame zu identifizieren. Dieses Attribut wird hier versehentlich überschrieben . Es kann zu anderen Fehlern auf der Seite kommen.
function Person(name,age,job){ if(this instanceof Person){ this.name=name; this.age=age; this.job=job; }else{ return new Person(name,age,job); } }var person=Person('match',28,'Software Engineer'); console.log(window.name); // ""console.log(person.name); //'match'var person= new Person('match',28,'Software Engineer'); console.log(window.name); // ""console.log(person.name); //'match'
Die Vererbung des Konstruktor-Stealing-Musters hat jedoch Nebenwirkungen. Dies liegt daran, dass dieses Objekt im folgenden Code keine Polygon-Objektinstanz ist, sodass der Konstruktor Polygon() eine neue Instanz erstellt und zurückgibt
function Polygon(sides){ if(this instanceof Polygon){ this.sides=sides; this.getArea=function(){ return 0; } }else{ return new Polygon(sides); } } function Rectangle(wifth,height){ Polygon.call(this,2); this.width=this.width; this.height=height; this.getArea=function(){ return this.width * this.height; }; } var rect= new Rectangle(5,10); console.log(rect.sides); //undefined
Wenn Sie das bereichssichere Konstruktor-Stealing-Muster verwenden möchten , Sie müssen in Kombination mit der Vererbung der Prototypkette das Prototypattribut von Rechteck neu schreiben, sodass seine Instanz auch eine Instanz von Polygon wird
function Polygon(sides){ if(this instanceof Polygon){ this.sides=sides; this.getArea=function(){ return 0; } }else{ return new Polygon(sides); } } function Rectangle(wifth,height){ Polygon.call(this,2); this.width=this.width; this.height=height; this.getArea=function(){ return this.width * this.height; }; } Rectangle.prototype= new Polygon();var rect= new Rectangle(5,10); console.log(rect.sides); //2
Lazy-Loading-Funktion
Aufgrund der Unterschiede zwischen Browsern Aufgrund der Verhaltensunterschiede zwischen Browsern fügen wir häufig eine große Anzahl von if-Anweisungen in Funktionen ein, um Browsereigenschaften zu überprüfen und Kompatibilitätsprobleme mit verschiedenen Browsern zu lösen. Zum Beispiel unsere häufigste Funktion zum Hinzufügen von Ereignissen zu Dom-Knoten
function addEvent(type, element, fun) { if (element.addEventListener) { element.addEventListener(type, fun, false); } else if(element.attachEvent){ element.attachEvent('on' + type, fun); } else{ element['on' + type] = fun; } }
Bei jedem Aufruf der Funktion „addEvent“ prüft sie zunächst, ob die Methode „addEventListener“ unterstützt wird Wenn es unterstützt wird, prüfen Sie, ob die Methode attachmentEvent unterstützt wird. Wenn sie noch nicht unterstützt wird, verwenden Sie die Methode der Ebene dom0, um das Ereignis hinzuzufügen. Dieser Vorgang muss jedes Mal wiederholt werden, wenn die Funktion addEvent aufgerufen wird. Wenn der Browser eine der Methoden unterstützt, unterstützt er sie tatsächlich immer und es besteht keine Notwendigkeit, andere Zweige zu erkennen. Mit anderen Worten: Die if-Anweisung muss nicht jedes Mal ausgeführt werden und der Code kann schneller ausgeführt werden.
Die Lösung ist Lazy Loading. Das sogenannte Lazy Loading bedeutet, dass der Zweig der Funktionsausführung nur einmal erfolgt. Es gibt zwei Möglichkeiten, Lazy Loading zu implementieren
1. Die erste besteht darin, die Funktion beim Aufrufen zu verarbeiten. Wenn die Funktion zum ersten Mal aufgerufen wird, wird die Funktion als eine andere Funktion überschrieben, die in geeigneter Weise ausgeführt wird, sodass jeder Aufruf der ursprünglichen Funktion nicht den Ausführungszweig durchlaufen muss
Wir können die folgende Methode verwenden: Verwenden Sie Lazy Loading, um addEvent() neu zu schreiben
function addEvent(type, element, fun) { if (element.addEventListener) { addEvent = function (type, element, fun) { element.addEventListener(type, fun, false); } } else if(element.attachEvent){ addEvent = function (type, element, fun) { element.attachEvent('on' + type, fun); } } else{ addEvent = function (type, element, fun) { element['on' + type] = fun; } } return addEvent(type, element, fun); }
Bei diesem Lazy Loading addEvent() weist jeder Zweig der if-Anweisung der addEvent-Variablen einen Wert zu, wodurch die ursprüngliche Funktion effektiv abgedeckt wird. Der letzte Schritt besteht darin, die neue Zuweisungsfunktion aufzurufen. Beim nächsten Aufruf von addEvent() wird die neu zugewiesene Funktion direkt aufgerufen, sodass die if-Anweisung nicht ausgeführt werden muss
Diese Methode hat jedoch einen Nachteil. Wenn sich der Funktionsname ändert, wird dies der Fall sein Es wird schwieriger sein, es zu ändern.
2. Die zweite besteht darin, die entsprechende Funktion anzugeben, wenn die Funktion deklariert wird. Auf diese Weise geht die Leistung nicht verloren, wenn die Funktion zum ersten Mal aufgerufen wird, aber ein wenig Leistung geht verloren, wenn der Code geladen wird
Im Folgenden wird addEvent() gemäß dieser Idee neu geschrieben. Der folgende Code erstellt eine anonyme selbstausführende Funktion, die verschiedene Wege durchläuft, um zu bestimmen, welche Funktion zum Implementieren der
var addEvent = (function () { if (document.addEventListener) { return function (type, element, fun) { element.addEventListener(type, fun, false); } } else if (document.attachEvent) { return function (type, element, fun) { element.attachEvent('on' + type, fun); } } else { return function (type, element, fun) { element['on' + type] = fun; } } })();
Funktionsbindung
in Javascript verwendet werden soll, die mit dem interagiert DOM Es ist häufig erforderlich, eine Funktion zu definieren und sie dann an einen Ereignisauslöser eines bestimmten DOM-Elements oder einer bestimmten Sammlung zu binden. Die Bindungsfunktion wird häufig mit Rückruffunktionen und Ereignishandlern verwendet, um die Funktion als Variable zu übergeben Gleichzeitig bleibt die Codeausführungsumgebung erhalten
<button id="btn">按钮</button><script> var handler={ message:"Event handled.", handlerFun:function(){ alert(this.message); } }; btn.onclick = handler.handlerFun; </script>
Der obige Code erstellt ein Objekt namens Handler. Die Methode handler.handlerFun() wird als Ereignishandler für eine DOM-Schaltfläche zugewiesen. Wenn die Taste gedrückt wird, wird diese Funktion aufgerufen und eine Warnbox angezeigt. Obwohl es so aussieht, als ob im Warnfeld „Ereignis behandelt“ angezeigt werden sollte, wird tatsächlich „undefiend“ angezeigt. Das Problem besteht darin, dass die Umgebung von handler.handleClick() nicht gespeichert wird, sodass dieses Objekt letztendlich auf die DOM-Schaltfläche statt auf den Handler zeigt
Sie können Schließungen verwenden, um dieses Problem zu beheben
<button id="btn">按钮</button> <script> var handler={ message:"Event handled.", handlerFun:function(){ alert(this.message); } }; btn.onclick = function(){ handler.handlerFun(); } </script>
Dies ist natürlich eine spezielle Lösung für dieses Szenario. Das Erstellen mehrerer Schließungen kann dazu führen, dass der Code schwer zu verstehen und zu debuggen ist. Ein besserer Ansatz ist die Verwendung der Funktionsbindung
Eine einfache Bindungsfunktion bind() akzeptiert eine Funktion und eine Umgebung und gibt eine Funktion zurück, die die gegebene Funktion in der gegebenen Umgebung aufruft, wobei alle Argumente sie unverändert übergeben
function bind(fn,context){ return function(){ return fn.apply(context,arguments); } }
Diese Funktion scheint einfach, aber ihre Funktion ist sehr leistungsstark. In bind() wird ein Abschluss erstellt. Der Abschluss ruft mit apply() die eingehende Funktion auf und übergibt das Kontextobjekt und die Parameter an apply(). Wenn die zurückgegebene Funktion aufgerufen wird, führt sie die übergebene Funktion in der angegebenen Umgebung aus und gibt alle Parameter an
<button id="btn">按钮</button><script> function bind(fn,context){ return function(){ return fn.apply(context,arguments); } } var handler={ message:"Event handled.", handlerFun:function(){ alert(this.message); } }; btn.onclick = bind(handler.handlerFun,handler); </script>
ECMAScript5 definiert eine native bind()-Methode für alle Funktionen, was die Operation weiter vereinfacht
只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效用就突显出来了。它们主要用于事件处理程序以及setTimeout()和setInterval()。然而,被绑定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所以最好只在必要时使用
函数柯里化
与函数绑定紧密相关的主题是函数柯里化(function currying),它用于创建已经设置好了一个或多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数
function add(num1,num2){ return num1+num2; }function curriedAdd(num2){ return add(5,num2); } console.log(add(2,3));//5console.log(curriedAdd(3));//8
这段代码定义了两个函数:add()和curriedAdd()。后者本质上是在任何情况下第一个参数为5的add()版本。尽管从技术来说curriedAdd()并非柯里化的函数,但它很好地展示了其概念
柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。下面是创建柯里化函数的通用方式
function curry(fn){ var args = Array.prototype.slice.call(arguments, 1); return function(){ var innerArgs = Array.prototype.slice.call(arguments), finalArgs = args.concat(innerArgs); return fn.apply(null, finalArgs); }; }
curry()函数的主要工作就是将被返回函数的参数进行排序。curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。为了获取第一个参数之后的所有参数,在arguments对象上调用了slice()方法,并传入参数1表示被返回的数组包含从第二个参数开始的所有参数。然后args数组包含了来自外部函数的参数。在内部函数中,创建了innerArgs数组用来存放所有传入的参数(又一次用到了slice())。有了存放来自外部函数和内部函数的参数数组后,就可以使用concat()方法将它们组合为finalArgs,然后使用apply()将结果传递给函数。注意这个函数并没有考虑到执行环境,所以调用apply()时第一个参数是null。curry()函数可以按以下方式应用
function add(num1, num2){ return num1 + num2; }var curriedAdd = curry(add, 5); alert(curriedAdd(3)); //8
在这个例子中,创建了第一个参数绑定为5的add()的柯里化版本。当调用cuurriedAdd()并传入3时,3会成为add()的第二个参数,同时第一个参数依然是5,最后结果便是和8。也可以像下例这样给出所有的函数参数:
function add(num1, num2){ return num1 + num2; } var curriedAdd2 = curry(add, 5, 12); alert(curriedAdd2()); //17
在这里,柯里化的add()函数两个参数都提供了,所以以后就无需再传递给它们了
函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的bind()函数
function bind(fn, context){ var args = Array.prototype.slice.call(arguments, 2); return function(){ var innerArgs = Array.prototype.slice.call(arguments), finalArgs = args.concat(innerArgs); return fn.apply(context, finalArgs); }; }
对curry()函数的主要更改在于传入的参数个数,以及它如何影响代码的结果。curry()仅仅接受一个要包裹的函数作为参数,而bind()同时接受函数和一个object对象。这表示给被绑定的函数的参数是从第三个开始而不是第二个,这就要更改slice()的第一处调用。另一处更改是在倒数第3行将object对象传给apply()。当使用bind()时,它会返回绑定到给定环境的函数,并且可能它其中某些函数参数已经被设好。要想除了event对象再额外给事件处理程序传递参数时,这非常有用
var handler = { message: "Event handled", handleClick: function(name, event){ alert(this.message + ":" + name + ":" + event.type); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));
handler.handleClick()方法接受了两个参数:要处理的元素的名字和event对象。作为第三个参数传递给bind()函数的名字,又被传递给了handler.handleClick(),而handler.handleClick()也会同时接收到event对象
ECMAScript5的bind()方法也实现函数柯里化,只要在this的值之后再传入另一个参数即可
var handler = { message: "Event handled", handleClick: function(name, event){ alert(this.message + ":" + name + ":" + event.type); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler, "my-btn"));
javaScript中的柯里化函数和绑定函数提供了强大的动态函数创建功能。使用bind()还是curry()要根据是否需要object对象响应来决定。它们都能用于创建复杂的算法和功能,当然两者都不应滥用,因为每个函数都会带来额外的开销
函数重写
由于一个函数可以返回另一个函数,因此可以用新的函数来覆盖旧的函数
function a(){ console.log('a'); a = function(){ console.log('b'); } }
这样一来,当我们第一次调用该函数时会console.log('a')会被执行;全局变量a被重定义,并被赋予新的函数
当该函数再次被调用时, console.log('b')会被执行
再复杂一点的情况如下所示
var a = (function(){ function someSetup(){ var setup = 'done'; } function actualWork(){ console.log('work'); } someSetup(); return actualWork; })()
我们使用了私有函数someSetup()和actualWork(),当函数a()第一次被调用时,它会调用someSetup(),并返回函数actualWork()的引用.
Das obige ist der detaillierte Inhalt vonEinführung in fünf Anwendungstechniken von Javascript-Funktionen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!