Maison  >  Article  >  interface Web  >  Résumé de cette méthode de liaison en JavaScript

Résumé de cette méthode de liaison en JavaScript

angryTom
angryTomavant
2019-11-29 15:42:212131parcourir

Récemment, j'ai révisé quelques connaissances de base sur js et j'ai relu la série "JS You Don't Know" C'est toujours la priorité absolue, j'ai donc décidé de faire un résumé systématique des connaissances pertinentes à ce sujet, qui. sera également pratique pour moi dans le futur examen.

Résumé de cette méthode de liaison en JavaScript

Ceci sont quatre règles contraignantes

Liaison par défaut

Voici la. type d'appel de fonction le plus courant : un appel de fonction autonome (c'est-à-dire que la fonction est appelée directement à l'aide d'une référence de fonction sans aucune modification). Considérez cette règle comme la règle par défaut lorsqu'aucune autre règle ne peut être appliquée.

La valeur par défaut lie this à window en mode non strict, et à undefined en mode strict. Par exemple, la fonction suivante foo en mode non strict :

var a = 2;
function foo(){
    var a = 3;
    console.log(this.a);
}
foo(); //2

[Recommandations de cours associées :

Tutoriel vidéo JavaScript]

Ceci dans la méthode foo() pointe ici vers window, donc

window.a = 2;

En mode strict, this. pointe vers undefined, donc accéder à this.a signalera une erreur :

var a = 2;
function foo(){
    "use strict";
    var a = 3;
    console.log(this.a);
}
foo(); //Uncaught TypeError: Cannot read property 'a' of undefined

Liaison implicite

. S'il existe un objet contextuel à l'emplacement appelant, ou s'il est « possédé » ou « contenu » par un objet, une liaison implicite est utilisée.

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
obj.foo(); // 2

Dans l'exemple ci-dessus, foo est appelé via

obj.foo() L'emplacement appelant utilise le contexte obj pour référencer la fonction, donc ceci dans foo pointe vers obj.

De plus, foo est ajouté à obj comme référence, mais qu'il soit défini directement dans obj ou défini d'abord puis ajouté comme attribut de référence, à proprement parler, foo n'appartient pas à obj, donc " " Avoir" et "inclure" sont mis entre guillemets pour une meilleure compréhension.

Scénarios d'appels implicites courants :

obj.fn();
arguments[i]();//其实就是将点的调用方式变为了[]调用
el.onClick(function(){console.log(this);//this指向el})

Perte implicite

Regardons d'abord un morceau de code :

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "global"; // a 是全局对象的属性
bar(); // "global"

Ce qui précède code En fait, regardez simplement la méthode appelante :

bar() Il s'agit en fait d'un appel de fonction sans aucune modification, donc la liaison par défaut est appliquée.

Il existe une autre façon de transmettre des paramètres qui entraîneront également une perte implicite. Le principe est en fait le même que l'exemple ci-dessus :

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"

Afficher la liaison<.>Utiliser Les méthodes call, apply et bind peuvent spécifier la valeur this de la fonction de liaison. Cette méthode de liaison est appelée liaison d'affichage.

function foo() {
    console.log( this.a );
}
var obj = {
    a:2
};
foo.call( obj ); // 2

Grâce à foo.call(obj), nous pouvons forcer foo à lier ceci à obj lors de son appel

nouvelle liaison

nouvel opérateur peut créer une nouvelle instance d'objet basée sur un "constructeur". Le processus d'instanciation de new est le suivant :

● Créer (ou construire) un tout nouvel objet.

● Ce nouvel objet sera connecté par [[ prototype]].

● Ce nouvel objet sera lié à ceci dans l'appel de fonction.

● Si la fonction ne renvoie pas d'autres objets, alors l'appel de fonction dans la nouvelle expression renverra automatiquement le nouvel objet.

Après avoir clarifié le processus d'instanciation de new, pensez au code suivant :

function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2

new foo(2) crée une nouvelle barre d'objet d'instance, puis lie cette nouvelle barre d'objet à foo this dans la fonction, donc après avoir exécuté this.a = a, a est en fait attribué à bar.a

Priorité

En général, cette liaison Basée sur les quatre liaisons ci-dessus les règles, lorsqu'elles apparaissent en même temps, dans quel ordre faut-il en juger le sens ? Voici les règles spécifiques :

La fonction est-elle appelée en new (nouvelle liaison) ? Si tel est le cas, cela est lié à l'objet nouvellement créé (var bar = new foo()).

La fonction est-elle appelée via call, apply (liaison explicite) ou liaison matérielle ? Si tel est le cas, ceci est lié à l'objet spécifié ( var bar = foo.call(obj2) ).

La fonction est-elle appelée dans un objet contextuel (implicitement lié) ? Si tel est le cas, cela est lié à cet objet contextuel. ( var bar = obj1.foo() )

Si aucun des deux, utilisez la liaison par défaut. S'il est en mode strict, il est lié à undefined, sinon il est lié à l'objet global. (var bar = foo())

Exception de liaison

1 Utilisez des méthodes de liaison explicites telles que call, appy et bind, et transmettez des paramètres nuls Ou quand. undefined est utilisé comme contexte, l'appel de fonction utilisera toujours la liaison par défaut

function foo() {
    console.log( this.a );
}
var a = 2;
foo.call( null ); // 2

Dans quelles circonstances doit-il passer le contexte comme nul ?

1. Utilisez la fonction bind pour implémenter le currying

function foo(a,b) {
    console.log(a,b);
}
// 使用 bind(..) 进行柯里化
var bar = foo.bind( null, 2 );
bar( 3 ); // 2,3

2. Utilisez apply(..) pour développer un tableau et le passer dans une fonction en tant que paramètre

function foo(a,b) {
    console.log(a,b);
}
// 把数组展开成参数
foo.apply( null, [2, 3] ); // 2,3

. En fait, les deux scénarios d'utilisation ci-dessus ne se soucient pas de la valeur du premier paramètre de call/app/bind, ils veulent simplement transmettre une valeur d'espace réservé.

Mais toujours passer null peut provoquer des bugs difficiles à suivre. Par exemple, lorsque vous avez cela dans une fonction d'une bibliothèque tierce que vous utilisez, cela sera lié de manière incorrecte à l'objet global. , provoquant des conséquences imprévisibles (modification des variables globales)

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!

Une approche plus sûre :

var o = Object.create(null);
Utils.changeA.call(o,6);
a //1, 全局变量没有修改
o.a // 6 改的是变量o

Référence indirecte

.

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(&#39;sven&#39;);  // B {name: "luo"}
console.log( b.getName() ); // 输出:  &#39;luo&#39;

执行new B('seven')后会返回一个新对象b,并且B函数中的this会绑定到新对象b上,B的函数体内执行A.apply(this.arguments)也就是执行b.name = name;这个时候b的值就是{name:'luo'},所以b.getName()就能输出'luo'啦~

实际在业务使用中,逻辑会更复杂一些,但是万变不离其宗,都按照上面写的规则来代入就好了

本文来自 js教程 栏目,欢迎学习!  

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer