Vor kurzem habe ich einige grundlegende Kenntnisse über js überprüft und die Reihe „JS, die Sie nicht kennen“ erneut gelesen. Dies hat immer oberste Priorität, daher habe ich beschlossen, eine systematische Zusammenfassung der relevanten Kenntnisse darüber zu erstellen wird auch für mich in der Zukunft praktisch sein.
Das sind vier verbindliche Regeln
1. Standardbindung
Das ist die Die häufigste Art von Funktionsaufrufen: ein eigenständiger Funktionsaufruf (d. h. die Funktion wird direkt unter Verwendung einer unveränderten Funktionsreferenz aufgerufen). Stellen Sie sich diese Regel als Standardregel vor, wenn keine anderen Regeln angewendet werden können.
Die Standardeinstellung verweist auf „window“ im nicht strikten Modus und auf undefiniert im strikten Modus. Beispielsweise die folgende Funktion foo im nicht strikten Modus:
var a = 2; function foo(){ var a = 3; console.log(this.a); } foo(); //2
JavaScript-Video-Tutorial]
Dies in der foo()-Methode hier zeigt auf window, alsowindow.a = 2;
Im strikten Modus zeigt dies auf undefiniert, daher wird beim Zugriff auf this.a ein Fehler gemeldet:var a = 2; function foo(){ "use strict"; var a = 3; console.log(this.a); } foo(); //Uncaught TypeError: Cannot read property 'a' of undefined
2. Implizite Bindung
Wenn am aufrufenden Ort ein Kontextobjekt vorhanden ist oder ein Objekt „im Besitz“ oder „enthalten“ ist, wird implizite Bindung verwendet.function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; obj.foo(); // 2
obj.foo() aufgerufen. Der aufrufende Ort verwendet den obj-Kontext, um auf die Funktion zu verweisen, sodass dies in foo auf obj verweist.
Außerdem wird foo als Referenz zu obj hinzugefügt, aber unabhängig davon, ob es direkt in obj definiert wird oder zuerst definiert und dann als Referenzattribut hinzugefügt wird, gehört foo genau genommen nicht zu obj, also " " „Have“ und „include“ werden zum besseren Verständnis in Anführungszeichen gesetzt. Häufige implizite Aufrufszenarien:obj.fn(); arguments[i]();//其实就是将点的调用方式变为了[]调用 el.onClick(function(){console.log(this);//this指向el})
Impliziter Verlust
Schauen wir uns zuerst einen Code an:function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; // 函数别名! var a = "global"; // a 是全局对象的属性 bar(); // "global"
bar() Dies ist eigentlich ein Funktionsaufruf ohne jegliche Änderung, daher wird die Standardbindung angewendet.
Es gibt eine andere Möglichkeit, Parameter zu übergeben, die ebenfalls implizite Verluste verursachen. Das Prinzip ist eigentlich das gleiche wie im obigen Beispiel:function foo() { console.log( this.a ); } function doFoo(fn) { // fn 其实引用的是foo fn(); // <-- 调用位置! } var obj = { a: 2, foo: foo }; var a = "global"; // a 是全局对象的属性 doFoo( obj.foo ); // "global"
Anzeigebindung
Verwendung Die Methoden call, apply und bind können den Wert dieser Bindungsfunktion angeben. Diese Bindungsmethode wird als Anzeigebindung bezeichnet.function foo() { console.log( this.a ); } var obj = { a:2 }; foo.call( obj ); // 2
neue Bindung< Der 🎜>neue Operator kann eine neue Objektinstanz basierend auf einem „Konstruktor“ erstellen. Der Instanziierungsprozess von new ist wie folgt:
● Erstellen (oder konstruieren) Sie ein brandneues Objekt.
● Dieses neue Objekt wird durch [[ Prototyp]] verbunden.
● Dieses neue Objekt wird im Funktionsaufruf daran gebunden.
● Wenn die Funktion keine anderen Objekte zurückgibt, gibt der Funktionsaufruf im neuen Ausdruck automatisch das neue Objekt zurück.
Nachdem Sie den Instanziierungsprozess von new geklärt haben, denken Sie über den folgenden Code nach:
function foo(a) { this.a = a; } var bar = new foo(2); console.log( bar.a ); // 2
new foo(2) erstellt eine neue Instanzobjektleiste und bindet dann diese neue Objektleiste, um diese einzubinden Die Funktion, also nach der Ausführung von this.a = a, wird a tatsächlich bar.a zugewiesen
PrioritätIm Allgemeinen basiert diese Bindung auf den oben genannten vier Bindungen Regeln, wenn sie gleichzeitig auftreten, in welcher Reihenfolge sollte die Richtung davon beurteilt werden? Im Folgenden sind die spezifischen Regeln aufgeführt:
Wird die Funktion neu aufgerufen (neue Bindung)? Wenn ja, wird dies an das neu erstellte Objekt gebunden (var bar = new foo()).
Wird die Funktion durch Aufruf, Anwenden (explizite Bindung) oder feste Bindung aufgerufen? Wenn ja, wird dies an das angegebene Objekt gebunden ( var bar = foo.call(obj2) ).
Wird die Funktion innerhalb eines Kontextobjekts aufgerufen (implizit gebunden)? Wenn ja, ist dies an dieses Kontextobjekt gebunden. ( var bar = obj1.foo() )
Wenn beides nicht der Fall ist, verwenden Sie die Standardbindung. Im strikten Modus ist es an undefiniert gebunden, andernfalls ist es an das globale Objekt gebunden. (var bar = foo())
Bindungsausnahme1 Verwenden Sie explizite Bindungsmethoden wie call, appy und bind und übergeben Sie Nullparameter. Oder wann undefiniert als Kontext verwendet wird, verwendet der Funktionsaufruf weiterhin die Standardbindung
function foo() { console.log( this.a ); } var a = 2; foo.call( null ); // 2
Unter welchen Umständen muss der Kontext als Null übergeben werden?
1. Verwenden Sie die Bind-Funktion, um Currying zu implementieren
function foo(a,b) { console.log(a,b); } // 使用 bind(..) 进行柯里化 var bar = foo.bind( null, 2 ); bar( 3 ); // 2,3
2. Verwenden Sie apply(..), um ein Array zu erweitern und es als Parameter an eine Funktion zu übergeben
function foo(a,b) { console.log(a,b); } // 把数组展开成参数 foo.apply( null, [2, 3] ); // 2,3
Tatsächlich ist den beiden oben genannten Verwendungsszenarien der Wert des ersten Parameters von call/app/bind egal, sie möchten lediglich einen Platzhalterwert übergeben.
Aber die ständige Übergabe von Null kann zu schwer nachvollziehbaren Fehlern führen. Wenn Sie dies beispielsweise in einer Funktion in einer von Ihnen verwendeten Bibliothek eines Drittanbieters haben, wird dies fälschlicherweise an das globale Objekt gebunden , was zu unvorhersehbaren Konsequenzen führt (Änderung globaler Variablen)
var a = 1;//全局变量 const Utils = { a: 2, changeA: function(a){ this.a = a; } } Utils.changeA(3); Utils.a //3 a //1 Utils.changeA.call(null,4); Utils.a //3 a //4,修改了全局变量a!
Ein sichererer Ansatz:
var o = Object.create(null); Utils.changeA.call(o,6); a //1, 全局变量没有修改 o.a // 6 改的是变量o
2. Indirekter Verweis
function foo() { console.log( this.a ); } var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo(); // 3 (p.foo = o.foo)(); // 2
赋值表达式p.foo = o.foo 的返回值是目标函数的引用,因此调用位置是foo() 而不是p.foo() 或者o.foo()。根据我们之前说过的,这里会应用默认绑定。
this词法(箭头函数)
上述的几种规则适用于所有的正常函数,但不包括ES6的箭头函数。箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域(词法作用域)来决定this
function foo() { // 返回一个箭头函数 return (a) => { //this 继承自foo() console.log( this.a ); }; } var obj1 = { a:2 }; var obj2 = { a:3 }; var bar = foo.call( obj1 ); bar.call( obj2 ); // 2, 不是3 !
foo() 内部创建的箭头函数会捕获调用时foo() 的this。由于foo() 的this 绑定到obj1,bar(引用箭头函数)的this 也会绑定到obj1,箭头函数的绑定无法被修改。(new 也不行!)
几个例子加深理解
this的理论知识讲解得差不多了,来几个例子看看自己有没有理解全面:
1.经典面试题:以下输出结果是什么
var length = 10; function fn() { console.log(this.length); } var obj = { length: 5, method: function(fn) { fn(); arguments[0](); } }; obj.method(fn, 1);
obj中method方法里面调用了两次fn。第一次是直接调用的“裸露”的fn,因此fn()中this使用默认绑定,this.length为10.第二次调用时通过arguments0的方式调用的,arguments[0]其实指向的就是fn,但是是通过obj[fn]这种对象上下文的隐式绑定的,因此this指向arguments,而arguments只有一个一项(method中只有fn一个参数),因此arguments.length为1。因此打印的结果为:
10 1
2.以下输出什么
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = function () { return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn(); } }; obj.getAge();
答案是严格模式下会报错,非严格模式下输出NaN
原因也是因为在调用obj.getAge()后,getAge方法内的this使用隐式绑定。但是return fn()的时候用的是“裸露的fn”使用默认绑定,fn里面的this指向window或者undefined。
使用箭头函数来修正this的指向:
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象 return fn(); } }; obj.getAge(); // 25
使用箭头函数后,fn中的this在他的词法分析阶段就已经确定好了(即fn定义的时候),跟调用位置无关。fn的this指向外层的作用域(即getAge中的this)
3.以下输出为什么是'luo'
var A = function( name ){ this.name = name; }; var B = function(){ A.apply(this,arguments); }; B.prototype.getName = function(){ return this.name; }; var b=new B('sven'); // B {name: "luo"} console.log( b.getName() ); // 输出: 'luo'
执行new B('seven')后会返回一个新对象b,并且B函数中的this会绑定到新对象b上,B的函数体内执行A.apply(this.arguments)也就是执行b.name = name;这个时候b的值就是{name:'luo'},所以b.getName()就能输出'luo'啦~
实际在业务使用中,逻辑会更复杂一些,但是万变不离其宗,都按照上面写的规则来代入就好了
本文来自 js教程 栏目,欢迎学习!
Das obige ist der detaillierte Inhalt vonZusammenfassung dieser Bindungsmethode in JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!