Heim  >  Artikel  >  Web-Frontend  >  Eingehende Analyse dieses Zeigeproblems in JavaScript

Eingehende Analyse dieses Zeigeproblems in JavaScript

巴扎黑
巴扎黑Original
2017-04-29 15:50:241106Durchsuche

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.

Rufen Sie direkt

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);

auf ​Die Auswirkung von bind() auf direkte Aufrufe

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.

​Die Auswirkung von „Anrufen und Bewerben“ darauf

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!

Methodenaufruf

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.

Wenn dies in der Methode auf das globale Objekt zeigt

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.

 new 调用

  在 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

 箭头函数中的 this

  先来看看 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!

Stellungnahme:
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