«Серия вопросов для предварительного собеседования 4», принцип и использование этого

опрос
«Серия вопросов для предварительного собеседования 4», принцип и использование этого

Это четвертый вопрос из серии вопросов для фронтенд-интервью, возможно, вы пропустили предыдущие, вы можете найти их здесь:

На фронтенд-интервью часто задают точечные вопросы по этому поводу. Недавно мой друг Z обратился ко мне за помощью и сказал, что когда он увидел название этого, он был в беде и не мог понять, к кому это относится. Я подготовил для него ответ и организовал его в этой статье, надеясь помочь нуждающимся студентам.

вопрос интервью

Мой друг Z показал мне этот вопрос:

var length = 10;

function fn () {
    console.log(this.length);
}
 
var obj = {
    length: 5,
    method: function (fn) {
        fn();
        arguments[0]();
    }
};
 
obj.method(fn, 1);

В: Что выдает браузер?

Его ответ: сначала выведите a10, затем выведите2.

Расшифруем почему:

  • В нашей задаче, несмотря на то, что fn передается в качестве параметра метода, его вызывающая сторона не затрагивается, она по-прежнемуwindow, поэтому выводится 10.
  • arguments[0]();Это утверждение не является распространенным, и здесь у вас могут возникнуть сомнения. фактически,arguments - это объект особого типа. В функции мы можем получить к ней доступ без указания имени параметра. Его можно рассматривать как неявную форму передачи параметров.
  • при выполнении аргументов0; , фактически вызывается fn(). В это время this в функции fn указывает на аргументы этого специального объекта. Метод obj.method получает 2 параметра, поэтому длина аргументов, очевидно, равна 2.

Для студентов, которые не понимают анализ текстовой версии, вы можете увидеть аннотированную версию ниже:

var length = 10;

function fn () {
    // 这里可以多打印一行 this,来看清每一步的指向
    // console.log(this);
    console.log(this.length);
}
 
var obj = {
    length: 5,
    method: function (fn) {
        // 大家可以试着,把 function 里的参数 fn 去掉,
        // 并不影响 fn 的调用者,还是 window
        fn();
        // arguments 有 2 个参数,fn 和 1,
        // 当执行 arguments[0]() 等价于调用 fn(),
        // arguments是一种特殊的对象,在这里作为 fn 的调用者
        arguments[0]();
    }
};
 
obj.method(fn, 1);

Сделать макияж

Кроме того, многие студенты не понимают, что это указывает, потому что это не указывает на объект, которого мы ожидали.

Как и в этом вопросе, семантически мы ожидаем, что fn() выведет собственную длину obj, которая равна 5, а не 10. Итак, как нам изменить этот код, если мы хотим получить результат 5?

На самом деле, просто сделайте еще один шаг, чтобы справиться с этим. Просто позвольте этому пункту стать объектом obj. Здесь мы можем использовать call для изменения этого указателя, например:

var length = 10;

function fn () {
    console.log(this.length);
}

var obj = {
    length: 5,
    method: function (fn) {
        // 在这里用call 将 this 指向 obj 自己
        fn.call(this);
    }
};
 
obj.method(fn);

Выходной результат 5, готово.

Смотрите, это не так уж и сложно, нам нужно всего лишь несколько простых операций, чтобы управлять наведением этого. Итак, вопрос в том, почему это иногда выходит из-под контроля?

Собственно, это связано с принципом, лежащим в основе этого механизма. Но не волнуйтесь, давайте начнем с понимания основной концепции этого, давайте посмотрим, что именно это такое?

что это ?

это ключевое слово в JavaScript. Обычно используется в теле функции, в зависимости от контекстных условий вызова функции, связанных с тем, как была вызвана функция. На кого она указывает, полностью определяется сайтом вызова, на котором вызывается функция.

Итак, это связано во время выполнения, независимо от привязки во время записи. Значение этого параметра изменяется в зависимости от того, где используется функция. Но есть один общий принцип: т.это всегда указывает на объект, вызвавший функцию.

Зачем использовать это?

Это кажется немного трудоемким для понимания концептуально. Так почему же мы все еще используем это? Каковы преимущества использования этого?

Давайте сначала посмотрим на следующий пример:

function identify() {
    return this.name.toUpperCase();
}

var me = {
    name: "Kyle"
};

var you = {
    name: "Reader"
};

identify.call( me ); // KYLE
identify.call( you ); // READER

Сначала мы можем быть слишком смущены тем, почему вывод такой. Вместо этого мы можем явно передать объект окружения в функциюidentify(). так:

function identify(context) {
    return context.name.toUpperCase();
}
identify( you ); // READER

В этом простом примере результат тот же. Мы можем передать объект окружения непосредственно в функцию, что кажется более интуитивным. но,Передача среды выполнения в качестве явного параметра функции становится очень запутанной, когда шаблон становится более сложным..

Этот механизм может предоставить более элегантный способ неявной «передачи» ссылки на объект, что сделает дизайн API более чистым и удобным для повторного использования.

Принцип этого

После понимания концепции этого, не вызывая у меня любопытства, почему это указывает на среду выполнения операции функции?

Ранее я видел статью г-на Жуана, в которой подробно анализировался принцип этого. По моему собственному разумению, я организую это следующим образом.

Многие учебники скажут вам, что это относится к среде, в которой работает функция. Но почему это так? Другими словами, как определяется рабочая среда функции?

Понимание принципа этого помогает нам лучше понять его использование.Причина, по которой язык JavaScript имеет такой дизайн, связана со структурой данных в памяти..

Давайте посмотрим на простой пример:

var obj = { foo: 5 };

Приведенный выше код присваивает объект переменной obj. Механизм JavaScript сначала сгенерирует объект { foo: 5 } в памяти, а затем присвоит адрес памяти этого объекта переменной obj.

То есть переменная obj на самом деле просто адрес. Если вы хотите прочитать obj.foo позже, движок сначала получает адрес памяти из obj, затем читает исходный объект из адреса и возвращает его свойство foo.

Эта структура понятна, но что, если значением свойства является функция? Например:

var obj = { foo: function () {} };

В это время движок JavaScript сохранит функцию отдельно в памяти, а затем назначит адрес функции свойству value свойства foo.

Как видите, функция представляет собой одно значение (назначенное в виде адреса), поэтому ее можно выполнять в разных средах.

И потому что JavaScript позволяет ссылаться на другие переменные текущей среды внутри тела функции. Таким образом, должен быть механизм для получения текущей рабочей среды (контекста) внутри тела функции. Итак, это появилось, и его конструктивное назначениеВнутри тела функции он ссылается на текущую рабочую среду функции..

использование этого

Поняв принцип this, мы используем следующие пять ситуаций, чтобы обсудить использование this.

1. Чистый вызов функции

Это наиболее распространенное использование функции, которая является глобальным вызовом, поэтому она представляет окно глобального объекта.

function test(){
    this.x = 1;
    console.log(this.x);
}
test(); // 1

2, как вызов метода объекта

Когда функция вызывается как метод объекта, это относится к вышестоящему объекту.

function test(){
    console.log(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1

3, как вызов конструктора

Так называемый конструктор должен генерировать новый объект (object) через эту функцию. В данном случае this относится к новому объекту.

function test(){
    this.x = 1;
}
var o = new test();
console.log(o.x); // 1

4, применить вызов

apply() — это метод объекта функции. Его функция заключается в изменении вызывающего объекта функции. Его первый параметр представляет измененный объект, вызывающий функцию. Следовательно, это относится к этому первому параметру.

var x = 0;
function test() {
    console.log(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply(); //0

Когда параметр apply() пуст, по умолчанию вызывается глобальный объект. Следовательно, текущий результат в это время равен 0, что доказывает, что это относится к глобальному объекту.

Он имеет ту же функцию, что и вызов, упомянутый выше, но написан немного по-другому. Из-за недостатка места я начну отдельную статью, чтобы подробно описать их использование.

5. Стрелочные функции

Стрелочные функции в ES6 в большинстве случаев оправдывают наши ожидания. Но иногда это не панацея, если вы не будете осторожны, это тоже выйдет из-под контроля.

Из-за длины содержания я напишу еще одну статью, чтобы представить его.

еще один вопрос интервью

Наконец, давайте посмотрим, чтобы консолидировать концепцию и использование этого. Посмотрите на вопросы лица:

window.val = 1;
 
var obj = {
    val: 2,
    dbl: function () {
        this.val *= 2; 
        val *= 2;
        console.log('val:', val);
        console.log('this.val:', this.val);
    }
};

 // 说出下面的输出结果
 obj.dbl();
 var func = obj.dbl;
 func();

Выводится ответ:2 、 4 、 8 、 8.

Разобрать:

  • Когда выполняется obj.dbl();, this.val this указывает на obj, а val следующей строки указывает на окно. Итак, 2 выводится с помощью window.val, а 4 — с помощью obj.val.
  • Вызывающий код последней строки func(); — это window. Итак, this из this.val теперь указывает на окно.
  • Не забудьте результат только что выполненной операции, window.val уже равен 2, поэтому теперь результат выполнения this.val *= 2; равен 4.
  • Результат выполнения val *= 2; равен 8. Итак, окончательный результат равен 8 и 8 .

Опять же, вот аннотированная версия кода:

window.val = 1;
 
var obj = {
    val: 2,
    dbl: function () {
        // 第一次的 this 指向 obj。所以,2 * 2 得 4。
        
        // 第二次的调用者变为了 window。this.val 等价于 window.val,
        // 第一次运算之后,window.val 已经为 2 了, 再 * 2 得到的结果就为 4 了。
        this.val *= 2; 
        
        // 第一次的 val 的调用者是 window。1 * 2 得 2。
        
        // 第二次的 val 的调用者还是 window,
        // 执行到此处时,window.val 已经为 4 了,再 * 2 的结果就是 8 了。
        val *= 2;    
        
        // 第二次的调用者都是 window,所以两行打印的结果是一样的,都是8。
        console.log('val:', val);
        console.log('this.val:', this.val);
    }
};

 // 说出下面的输出结果
 obj.dbl();
 var func = obj.dbl;
 func();

Суммировать

this относится к текущей рабочей среде функции, зависит от условий контекста вызова функции и будет привязано только во время выполнения. Помните об общих принципах:это всегда указывает на объект, вызвавший функцию.

использованная литература

возобновить

Статья о конкретном использовании call и apply была обновлена, а в конце статьи есть пасхалки! ! ! смотрите подробности«Галантерейные товары» уточняет разницу и использование терминов «вызов, применение и связывание». (2019-01-27)

PS: Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись «Super Brother Front-end Small Stack», чтобы обменяться идеями и технологиями.