Heim > Artikel > Web-Frontend > Eingehende Analyse dieses Zeigeproblems in JavaScript
Es gibt viele Blogs, die das Problem dieses Verweisens in JavaScript erklären, und viele Leute fragen immer noch danach.
Im Gegensatz zu vielen unserer gängigen Sprachen wird der This-Zeiger in einer JavaScript-Funktion nicht bei der Definition der Funktion, sondern beim Aufruf bestimmt. Mit anderen Worten: Die Art und Weise, wie die Funktion aufgerufen wird, bestimmt, worauf sie verweist.
In JavaScript gibt es drei gängige Methoden zum Aufrufen von Funktionen: direkter Aufruf, Methodenaufruf und neuer Aufruf. Darüber hinaus gibt es einige spezielle Aufrufmethoden, z. B. das Binden der Funktion an das Objekt über bind() und den anschließenden Aufruf, den Aufruf über call(), apply() usw. Nachdem es6 Pfeilfunktionen eingeführt hat, unterscheidet sich dieser Zeiger beim Aufruf der Pfeilfunktion. Lassen Sie uns den this-Zeiger in diesen Situationen analysieren.
an Der direkte Aufruf erfolgt über den Funktionsnamen (...). Zu diesem Zeitpunkt zeigt dies innerhalb der Funktion auf das globale Objekt. Im Browser ist das globale Objekt window und in NodeJs ist das globale Objekt global.
Schauen wir uns ein Beispiel an:
// 简单兼容浏览器和 NodeJs 的全局对象 const _global = typeof window === "undefined" ? global : window; function test() { console.log(this === _global); // true } test(); // 直接调用
Hierbei ist zu beachten, dass sich der direkte Aufruf nicht auf den Aufruf im globalen Bereich bezieht. In jedem Bereich wird der direkte Aufruf einer Funktion über den Funktionsnamen (...) als direkter Aufruf bezeichnet. Das folgende Beispiel ruft beispielsweise auch direkt
(function(_global) { // 通过 IIFE 限定作用域 function test() { console.log(this === _global); // true } test(); // 非全局作用域下的直接调用 })(typeof window === "undefined" ? global : window);
Zu beachten ist auch die Auswirkung von bind(). Die Funktion von Function.prototype.bind() besteht darin, die aktuelle Funktion an das angegebene Objekt zu binden und eine neue Funktion zurückzugeben. Unabhängig davon, wie sie aufgerufen wird, zeigt diese neue Funktion immer auf das gebundene Objekt. Schauen wir uns ein Beispiel an:
const obj = {}; function test() { console.log(this === obj); } const testObj = test.bind(obj); test(); // false testObj(); // true
Was macht bind() also? Lassen Sie uns ein bind() simulieren, um zu verstehen, wie es sich darauf auswirkt.
const obj = {}; function test() { console.log(this === obj); } // 自定义的函数,模拟 bind() 对 this 的影响 function myBind(func, target) { return function() { return func.apply(target, arguments); }; } const testObj = myBind(test, obj); test(); // false testObj(); // true
Wie Sie dem obigen Beispiel entnehmen können, wird zunächst durch den Abschluss das Ziel, das das gebundene Objekt ist, beibehalten. Wenn die Funktion dann aufgerufen wird, wird die Methode „apply“ für die ursprüngliche Funktion verwendet, um dies der Funktion anzugeben . Natürlich kann die native bind()-Implementierung anders und effizienter sein. Aber dieses Beispiel zeigt, was bind() kann.
Im obigen Beispiel wird Function.prototype.apply() verwendet, und Function.prototype.call() ähnelt diesem. Für die Verwendung dieser beiden Methoden lesen Sie bitte selbst die Dokumentation über den Link. Ihr erster Parameter zeigt jedoch alle darauf, wenn die angegebene Funktion ausgeführt wird.
Allerdings müssen Sie bei der Verwendung von „Apply“ und „Call“ dennoch aufpassen. Wenn die Verzeichnisfunktion selbst eine an dieses Objekt gebundene Funktion ist, werden „Apply“ und „Call“ nicht wie erwartet ausgeführt, z. B.
const obj = {}; function test() { console.log(this === obj); } // 绑定到一个新对象,而不是 obj const testObj = test.bind({}); test.apply(obj); // true // 期望 this 是 obj,即输出 true // 但是因为 testObj 绑定了不是 obj 的对象,所以会输出 false testObj.apply(obj); // false
Es ist ersichtlich, dass bind() einen tiefgreifenden Einfluss auf Funktionen hat. Verwenden Sie es daher mit Vorsicht!
Der Methodenaufruf bezieht sich auf den Aufruf seiner Methodenfunktion über ein Objekt. Er hat die Form object.method function (...). In diesem Fall zeigt this in der Funktion auf das Objekt, auf dem die Methode aufgerufen wird. Sie müssen sich jedoch auch der Auswirkungen von bind() bewusst sein.
const obj = { // 第一种方式,定义对象的时候定义其方法 test() { console.log(this === obj); } }; // 第二种方式,对象定义好之后为其附加一个方法(函数表达式) obj.test2 = function() { console.log(this === obj); }; // 第三种方式和第二种方式原理相同 // 是对象定义好之后为其附加一个方法(函数定义) function t() { console.log(this === obj); } obj.test3 = t; // 这也是为对象附加一个方法函数 // 但是这个函数绑定了一个不是 obj 的其它对象 obj.test4 = (function() { console.log(this === obj); }).bind({}); obj.test(); // true obj.test2(); // true obj.test3(); // true // 受 bind() 影响,test4 中的 this 指向不是 obj obj.test4(); // false
Hierbei ist zu beachten, dass die letzten drei Methoden alle vordefinierte Funktionen sind und dann als Methode an das Objekt obj angehängt werden. Auch hier hat der Zeiger this innerhalb einer Funktion nichts mit der Definition zu tun und wird von der aufrufenden Methode beeinflusst.
Beachten Sie, dass sich das, was hier gesagt wird, auf die Methode und nicht auf den Methodenaufruf bezieht. Dies in der Methode zeigt auf das globale Objekt. Wenn es nicht an bind() liegt, muss es daran liegen, dass die Methode nicht aufgerufen wird, z. B.
const obj = { test() { console.log(this === obj); } }; const t = obj.test; t(); // false
t ist die Testmethode von obj, aber wenn t() aufgerufen wird, zeigt dies auf das Globale.
Der Grund, warum diese Situation besonders erwähnt wird, liegt darin, dass oft nach der Übergabe einer Objektmethode als Callback an eine Funktion festgestellt wird, dass das laufende Ergebnis nicht den Erwartungen entspricht – weil die Auswirkungen der aufrufenden Methode darauf ignoriert werden. Das folgende Beispiel ist beispielsweise ein Problem, das besonders leicht auftritt, nachdem bestimmte Dinge auf der Seite gekapselt wurden:
class Handlers { // 这里 $button 假设是一个指向某个按钮的 jQuery 对象 constructor(data, $button) { this.data = data; $button.on("click", this.onButtonClick); } onButtonClick(e) { console.log(this.data); } } const handlers = new Handlers("string data", $("#someButton")); // 对 #someButton 进行点击操作之后 // 输出 undefined // 但预期是输出 string data
Nachdem this.onButtonClick als Parameter an on() übergeben wurde, wird diese Funktion beim Auslösen des Ereignisses offensichtlich direkt anstelle eines Methodenaufrufs aufgerufen, sodass sie auf das globale Objekt verweist. Es gibt viele Möglichkeiten, dieses Problem zu lösen
// 这是在 es5 中的解决办法之一 var _this = this; $button.on("click", function() { _this.onButtonClick(); }); // 也可以通过 bind() 来解决 $button.on("click", this.onButtonClick.bind(this)); // es6 中可以通过箭头函数来处理,在 jQuery 中慎用 $button.on("click", e => this.onButtonClick(e));
Bitte beachten Sie jedoch, dass Sie bei der Verwendung von Pfeilfunktionen als jQuery-Callbacks bei der Verwendung innerhalb der Funktion vorsichtig sein müssen. jQuery stellt in den meisten Rückruffunktionen (Nicht-Pfeilfunktionen) das Aufrufziel dar, sodass Sie Anweisungen wie $(this).text() schreiben können, aber jQuery kann den This-Zeiger von Pfeilfunktionen nicht ändern völlig anders.
在 es6 之前,每一个函数都可以当作是构造函数,通过 new 调用来产生新的对象(函数内无特定返回值的情况下)。而 es6 改变了这种状态,虽然 class 定义的类用 typeof 运算符得到的仍然是 "function",但它不能像普通函数一样直接调用;同时,class 中定义的方法函数,也不能当作构造函数用 new 来调用。
而在 es5 中,用 new 调用一个构造函数,会创建一个新对象,而其中的 this 就指向这个新对象。这没有什么悬念,因为 new 本身就是设计来创建新对象的。
var data = "Hi"; // 全局变量 function AClass(data) { this.data = data; } var a = new AClass("Hello World"); console.log(a.data); // Hello World console.log(data); // Hi var b = new AClass("Hello World"); console.log(a === b); // false
先来看看 MDN 上对箭头函数的说明
An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target. Arrow functions are always anonymous. These function expressions are best suited for non-method functions, and they cannot be used as constructors.
这里已经清楚了说明了,箭头函数没有自己的 this 绑定。箭头函数中使用的 this,其实是直接包含它的那个函数或函数表达式中的 this。比如
const obj = { test() { const arrow = () => { // 这里的 this 是 test() 中的 this, // 由 test() 的调用方式决定 console.log(this === obj); }; arrow(); }, getArrow() { return () => { // 这里的 this 是 getArrow() 中的 this, // 由 getArrow() 的调用方式决定 console.log(this === obj); }; } }; obj.test(); // true const arrow = obj.getArrow(); arrow(); // true
示例中的两个 this 都是由箭头函数的直接外层函数(方法)决定的,而方法函数中的 this 是由其调用方式决定的。上例的调用方式都是方法调用,所以 this 都指向方法调用的对象,即 obj。
箭头函数让大家在使用闭包的时候不需要太纠结 this,不需要通过像 _this 这样的局部变量来临时引用 this 给闭包函数使用。来看一段 Babel 对箭头函数的转译可能能加深理解:
// ES6 const obj = { getArrow() { return () => { console.log(this === obj); }; } }
// ES5,由 Babel 转译 var obj = { getArrow: function getArrow() { var _this = this; return function () { console.log(_this === obj); }; } };
另外需要注意的是,箭头函数不能用 new 调用,不能 bind() 到某个对象(虽然 bind() 方法调用没问题,但是不会产生预期效果)。不管在什么情况下使用箭头函数,它本身是没有绑定 this 的,它用的是直接外层函数(即包含它的最近的一层函数或函数表达式)绑定的 this。
Das obige ist der detaillierte Inhalt vonEingehende Analyse dieses Zeigeproblems in JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!