Heim >Web-Frontend >js-Tutorial >dies in JavaScript!

dies in JavaScript!

高洛峰
高洛峰Original
2017-02-04 16:16:391267Durchsuche

Globale Ausführung

Sehen wir uns zunächst an, was das in der globalen Umgebung ist:

zuerst Browser:

console.log(this
// Fenster {speechSynthesis: SpeechSynthesis, Caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}

Sie können sehen, dass das Fensterobjekt gedruckt wird;

zweiter. Knoten:

console.log(this);


// global

Sie können sehen, dass das globale Objekt gedruckt wird;

Zusammenfassung: Im globalen Bereich führt dies das aktuelle globale Objekt aus (Fenster auf der Browserseite, global im Knoten).

Ausführung in Funktion

Reiner Funktionsaufruf

Dies ist die gebräuchlichste Art, Funktionen zu verwenden:

function test() {
  console.log(this);
};
test();
// Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
Wir können sehen, dass a Wenn a Funktion wird direkt aufgerufen, es handelt sich um einen globalen Aufruf, und dies zeigt auf das globale Objekt;

Strikter Modus 'verwenden Sie strikt';

Wenn eine reine Funktion im strikten Modus ausgeführt wird. dann wird dies hier nicht auf das Globale verweisen, sondern auf Undefiniert. Dieser Ansatz besteht darin, einige ungenaue Verhaltensweisen in js zu beseitigen:

'use strict';
function test() {
  console.log(this);
};
test();
// undefined
Natürlich ist es besser, es in eine sofort ausgeführte Funktion einzufügen, um eine Verschmutzung zu vermeiden die globale Situation:

(function (){
  "use strict";
 console.log(this);
})();
// undefined
Wird als Methode eines Objekts aufgerufen

Wenn eine Funktion als Methode eines Objekts aufgerufen wird:

var obj = {
  name: 'qiutc',
  foo: function() {
    console.log(this.name);
  }
}
obj.foo();
// 'qiutc'
Zu diesem Zeitpunkt , dies zeigt auf das aktuelle Objekt;

Natürlich können wir auch Folgendes tun:

function test() {
  console.log(this.name);
}
var obj = {
  name: 'qiutc',
  foo: test
}
obj.foo();
// 'qiutc'
Das Gleiche bleibt unverändert, da alles in js ein Objekt und eine Funktion ist Bei test handelt es sich lediglich um einen Funktionsnamen und einen Verweis auf die Funktion. Wenn foo = test, zeigt foo auch auf diese Funktion.

Was passiert, wenn Sie die Methode des Objekts einer Variablen zuweisen und dann die Variable direkt aufrufen:

var obj = {
  name: 'qiutc',
  foo: function() {
    console.log(this);
  }
}
var test = obj.foo;
test();
// Window
Sie können sehen, dass dies zu diesem Zeitpunkt das Globale ausführt, wenn wir test = obj eingeben .foo, test zeigt direkt auf eine Funktion. Zu diesem Zeitpunkt hat es tatsächlich nichts mit dem Objekt obj zu tun. Daher wird dies direkt auf das globale Objekt verwiesen.

Einige Fallstricke

In der Rückruffunktion stoßen wir häufig auf einige Fallstricke:

var obj = {
  name: 'qiutc',
  foo: function() {
    console.log(this);
  },
  foo2: function() {
    console.log(this);
    setTimeout(this.foo, 1000);
  }
}
obj.foo2();
Wenn wir diesen Code ausführen, werden wir feststellen, dass dieser zweimal gedruckte Code nicht dasselbe ist :

Wenn dies zum ersten Mal direkt in foo2 gedruckt wird, das auf das Objekt obj zeigt, haben wir keinen Zweifel

Aber this.foo, das in setTimeout ausgeführt wird, zeigt auf das globale Objekt, ist es nicht; Wird es hier nicht als Funktionsmethode verwendet? Dies verwirrt oft viele Anfänger.

Tatsächlich ist setTimeout nur eine Funktion, und die Funktion muss Parameter erfordern. Wir übergeben this.foo als Parameter an die setTimeout-Funktion, genau wie es bei der Übergabe einen Spaßparameter erfordert Parameter, wir führen tatsächlich die Operation fun = this.foo aus. Hier verweisen wir direkt auf die Referenz von fun(), die tatsächlich ausgeführt wird, also ist es irrelevant , wird sie direkt als gewöhnliche Funktion aufgerufen, sodass diese auf das globale Objekt zeigt.

Dieses Problem tritt häufig bei vielen asynchronen Rückruffunktionen auf

Lösung

Um dieses Problem zu lösen, können wir die Eigenschaften von Abschlüssen verwenden, um damit umzugehen:

var obj = {
  name: 'qiutc',
  foo: function() {
    console.log(this);
  },
  foo2: function() {
    console.log(this);
    var _this = this;
    setTimeout(function() {
      console.log(this);  // Window
      console.log(_this);  // Object {name: "qiutc"}
    }, 1000);
  }
}
obj.foo2();
Sie können sehen, dass die direkte Verwendung von this immer noch Window ist, da this in foo2 auf obj zeigt. Wir können es zuerst mit einer Variablen _this speichern und dann _this in der Callback-Funktion verwenden, um auf zu zeigen current


Ein weiterer Fallstrick von setTimeout

Wie ich bereits sagte, wenn die Rückruffunktion direkt ohne gebundenen Bereich ausgeführt wird, dann zeigt sie auf das globale Objekt (Fenster). Zeigen Sie im strikten Modus auf undefiniert. Die Rückruffunktion in setTimeout verhält sich jedoch im strikten Modus anders:

'use strict';
function foo() {
  console.log(this);
}
setTimeout(foo, 1);
// window
Es liegt auf der Hand, dass wir den strikten Modus hinzugefügt haben und der Aufruf „foo“ nicht angegeben wurde sollte undefiniert sein, aber das globale Objekt wird hier immer noch angezeigt. Ist der strikte Modus ungültig?

Nein, selbst im strikten Modus führt die Funktion, wenn die setTimeout-Methode die eingehende Funktion aufruft und dies nicht angibt, eine implizite Operation aus – automatisch den globalen Kontext einfügen, was dem Aufruf von foo.apply entspricht (window) anstelle von foo();

Wenn wir dies bei der Übergabe der Funktion angegeben haben, wird das globale Objekt natürlich nicht injiziert, wie zum Beispiel: setTimeout(foo .bind(obj), 1);;

http://stackoverflow.com/questions/21957030/why-is-window-still-defined-in-this-strict-mode-code

Wird als verwendet Konstruktor

Um in js eine Klasse zu implementieren, müssen wir einige Konstruktoren definieren. Beim Aufruf eines Konstruktors müssen wir das neue Schlüsselwort

hinzufügen

function Person(name) {
  this.name = name;
  console.log(this);
}
var p = new Person('qiutc');
// Person {name: "qiutc"}

我们可以看到当作构造函数调用时,this 指向了这个构造函数调用时候实例化出来的对象;

当然,构造函数其实也是一个函数,如果我们把它当作一个普通函数执行,这个 this 仍然执行全局:

function Person(name) {
  this.name = name;
  console.log(this);
}
var p = Person('qiutc');
// Window

其区别在于,如何调用函数(new)。

箭头函数

在 ES6 的新规范中,加入了箭头函数,它和普通函数最不一样的一点就是 this 的指向了,还记得在上文中(作为对象的方法调用-一些坑-解决)我们使用闭包来解决 this 的指向问题吗,如果用上了箭头函数就可以更完美的解决了:

var obj = {
  name: 'qiutc',
  foo: function() {
    console.log(this);
  },
  foo2: function() {
    console.log(this);
    setTimeout(() => {
      console.log(this);  // Object {name: "qiutc"}
    }, 1000);
  }
}
obj.foo2();

可以看到,在 setTimeout 执行的函数中,本应该打印出在 Window,但是在这里 this 却指向了 obj,原因就在于,给 setTimeout 传入的函数(参数)是一个箭头函数:

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

根据例子我们理解一下这句话:
在 obj.foo2() 执行的时候,当前的 this 指向 obj;在执行 setTimeout 时候,我们先是定义了一个匿名的箭头函数,关键点就在这,箭头函数内的 this 执行定义时所在的对象,就是指向定义这个箭头函数时作用域内的 this,也就是 obj.foo2 中的 this,即 obj;所以在执行箭头函数的时候,它的 this -> obj.foo2 中的 this -> obj;

简单来说, 箭头函数中的 this 只和定义它时候的作用域的 this 有关,而与在哪里以及如何调用它无关,同时它的 this 指向是不可改变的。

call, apply, bind

在 js 中,函数也是对象,同样也有一些方法,这里我们介绍三个方法,他们可以更改函数中的 this 指向:

call

fun.call(thisArg[, arg1[, arg2[, ...]]])

它会立即执行函数,第一个参数是指定执行函数中 this 的上下文,后面的参数是执行函数需要传入的参数;

apply

fun.apply(thisArg[, [arg1, arg2, ...]])

它会立即执行函数,第一个参数是指定执行函数中 this 的上下文,第二个参数是一个数组,是传给执行函数的参数(与 call 的区别);

bind

var foo = fun.bind(thisArg[, arg1[, arg2[, ...]]]);

它不会执行函数,而是返回一个新的函数,这个新的函数被指定了 this 的上下文,后面的参数是执行函数需要传入的参数;

这三个函数其实大同小异,总的目的就是去指定一个函数的上下文(this),我们以 call 函数为例;

为一个普通函数指定 this

var obj = {
  name: 'qiutc'
};
function foo() {
  console.log(this);
}
foo.call(obj);
// Object {name: "qiutc"}

可以看到,在执行 foo.call(obj) 的时候,函数内的 this 指向了 obj 这个对象,成功;

为对象中的方法指定一个 this

var obj = {
  name: 'qiutc',
  foo: function () {
    console.log(this);
  }
}
var obj2 = {
  name: 'tcqiu222222'
};
obj.foo.call(obj2);
// Object {name: "tcqiu222222"}

可以看到,执行函数的时候这里的 this 指向了 obj2,成功;

为构造函数指定 this

function Person(name) {
  this.name = name;
  console.log(this);
}
var obj = {
  name: 'qiutc2222222'
};
var p = new Person.call(obj, 'qiutc');
// Uncaught TypeError: Person.call is not a constructor(…)

这里报了个错,原因是我们去 new 了 Person.call 函数,而非 Person ,这里的函数不是一个构造函数;

换成 bind 试试:

function Person(name) {
  this.name = name;
  console.log(this);
}
var obj = {
  name: 'qiutc2222222'
};
var Person2 = Person.bind(obj);
var p = new Person2('qiutc');
// Person {name: "qiutc"}
console.log(obj);
// Object {name: "qiutc2222222"}

打印出来的是 Person 实例化出来的对象,而和 obj 没有关系,而 obj 也没有发生变化,说明,我们给 Person 指定 this 上下文并没有生效;

因此可以得出: 使用 bind 给一个构造函数指定 this,在 new 这个构造函数的时候,bind 函数所指定的 this 并不会生效;

当然 bind 不仅可以指定 this ,还能传入参数,我们来试试这个操作:

function Person(name) {
  this.name = name;
  console.log(this);
}
var obj = {
  name: 'qiutc2222222'
};
var Person2 = Person.bind(obj, 'qiutc111111');
var p = new Person2('qiutc');
// Person {name: "qiutc111111"}

可以看到,虽然指定 this 不起作用,但是传入参数还是起作用了;

为箭头函数指定 this

我们来定义一个全局下的箭头函数,因此这个箭头函数中的 this 必然会指向全局对象,如果用 call 方法改变 this 呢:

var afoo = (a) => {
  console.log(a);
  console.log(this);
}
afoo(1);
// 1
// Window
var obj = {
  name: 'qiutc'
};
afoo.call(obj, 2);
// 2
// Window

可以看到,这里的 call 指向 this 的操作并没有成功,所以可以得出: 箭头函数中的 this 在定义它的时候已经决定了(执行定义它的作用域中的 this),与如何调用以及在哪里调用它无关,包括 (call, apply, bind) 等操作都无法改变它的 this。

只要记住箭头函数大法好,不变的 this。

更多JavaScript 中的 this !相关文章请关注PHP中文网!

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