Простое и быстрое понимание этого, звони и применяй в js

JavaScript

Примечание. Это окружение случая не является строгим режимом, и ключевое слово THIS запрещено в строгом режиме указывать на глобальный объект.

Во-первых, метод исполнения - это как?

Сначала поговорим о выполнении метода в js, объявим метод a под окном глобальным:

function a () {
  console.log(this);
}
a();//window

Обычный способ выполнить этот метод в глобальном масштабе — напрямую вызвать a(), среда выполнения этого метода — окно, а консоль выведет объект окна.

Так зачем распечатывать объект окна? Мы можем понять это, выполнение метода должно иметьпрямой абонент, метод a только что определен под глобальным окном, переменные и методы под окном имеют функцию, позволяющую опустить окно при доступе и вызове! Итак, только что выполненный a() === window.a(), то есть при выполнении метода aпрямой абонент是window。 !

упомянутый вышепрямой абонент, Что ты думаешь об этомпрямой абонентШерстяная ткань? Например, объявите глобальный объект obj:

var name = "window-name";
var obj = {
    name:"obj-name",
    a:function(){
        console.log(this.name);
    },
    b:{
        name:"b-name",
        a:function(){
            console.log(this.name);
        }
    }
}
obj.a();//obj-name
obj.b.a();//b-name

Выполните obj.a(); и obj.ba() соответственно, консоль выведет obj-name и b-name соответственно (здесь obj.a() === window.obj.a(), obj.ba( ) === window.obj.ba()), при выполнении методапрямой абонентЭто ближайший к вызываемому методу объект, два из них — obj и obj.b, а печатаемые имена — это имя obj и имя obj.b соответственно.

2. На кого это указывает?

Тогда в функцииthisКто это?thisкогда этот метод вызываетсяпрямой абонент. Может быть и другой частный пример, который можно хорошо понять, разобравшись с этим примером.thisуказал на кого. Определите глобальную переменную на основе только что:

var ax = obj.b.a;
ax();//window-name

В этот момент выполните ax(); консоль выведет имя окна; почему выводится имя окна? Это связано с тем, что ax является переменной, определенной в глобальном окне, при выполнении ax()прямой абонентэто окно (ax() === window.ax()), поэтому внутреннее this при выполнении ax() является егопрямой абонентwindow, поэтому напечатанное значение является значением имени, определенного в window, поэтому a() в начале этой статьи будет печатать окно после выполнения, потому что внутреннее this указывает на вызывающее окно a.

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

3. Что изменилось в вызове и применении?

понять функциюпрямой абонента такжеthis, а call и apply легче понять. Я не буду делать слишком много определений вызова и применим здесь.Давайте сначала посмотрим, кто есть кто после вызова вызоваметод, который должен быть выполнен, пример прямого кода:

function fn1 () {
    console.log(1);
};
function fn2 () {
    console.log(2);
};
fn1.call(fn2);//1

Выполните fn1.call(fn2), консоль выведет 1, что показывает, что после вызова fn1 callметод, который должен быть выполненЕще фн1. надо выяснить кто этометод, который должен быть выполнен, это функция, которая вызывает call, а текущая идентификация fn2 заключается в замене окна как fn1прямой абонент, это ключ к пониманию вызова и применения, вы также можете запустить fn2.call(fn1);//2 для проверкиметод, который должен быть выполненкто это. Итак, какова роль звонка? Другой пример кода:

var obj1 = {
    num : 20,
    fn : function(n){
        console.log(this.num+n);
    }
};
var obj2 = {
    num : 15,
    fn : function(n){
        console.log(this.num-n);
    }
};
obj1.fn.call(obj2,10);//25

Выполните obj1.fn.call(obj2,10), консоль выведет 25. Функция вызова здесь на самом деле очень проста, то есть, когда obj1.fn выполняется,прямой абонентОт obj1 к obj2 это внутри obj1.fn(n) указывает на obj2 через функцию вызова, поэтому this.num — это obj2.num, 10 — параметр, передаваемый при выполнении obj1.fn, obj2.num — 15, Таким образом, напечатанное значение равно 15+10=25.

Таким образом, мы можем понять это так: эффект звонка заключается в изменении этогометод, который должен быть выполнен(то есть метод, вызывающий call)прямой абонент! и этометод, который должен быть выполненВнутреннее this также будет повторно указывать на нового вызывающего объекта, который является первым параметром obj, полученным методом вызова. Также есть два особых случая, когда параметр obj равен нулю или не определен, это будет указывать на окно.

В-четвертых, разница между вызовом и применением

В дополнение к первому параметру obj метод call также принимает строку параметров в качестве параметров метода, который будет выполнен. полученный параметр представляет собой массив в виде параметров выполняемого метода.

Пять, расширение

Выполним следующий код:

fn1.call.call(fn2);//2

Выполните fn1.call.call(fn2); консоль напечатает 2, не будем говорить о том, почему она напечатает 2, давайте сначала разберемся, что такое fn1.call.call, метод call() — это метод в цепочке прототипов Function object , поэтому функция fn1 может использовать этот метод через наследование цепочки прототипов, то есть fn1.call === Function.prototype.call === Function.call. Итак, fn1.call.call(fn2) === Function.call.call(fn2), Function.call можно рассматривать как единое целое, и FunCall используется для его представления следующим образом:

FunCall.call(fn2);

Это легче понять, эквивалентно fn2 как FunCallпрямой абонентДля выполнения FunCall и FunCall === Function.call, поэтому это эквивалентно fn2.call().

В это время вызов не проходит в объект, тогда глобальный объект window будет использоваться как объект по умолчанию, что эквивалентно fn2.call(window), а затем продолжить объяснять, что это window.fn2.call( window), а объект прямого вызова fn2 становится окном, поэтому это эквивалентно прямому выполнению fn2(); консоль выведет 2.

Кроме того есть разные комбинации типа Function.call.apply и Function.apply.call, принципы схожи, но типы получаемых параметров не совпадают, можете попробовать. Углубите свое понимание призыва и применения.

6. Дополнительная привязка

Использование bind похоже на call, за исключением того, что метод не может быть выполнен сразу после вызова bind и его нужно вызывать снова, что на самом деле является синтаксическим сахаром для каррирования. Давайте реализуем упрощенную версию метода привязки с именем bindFn, и мы сможем примерно понять привязку:

Function.prototype.bindFn = function() {
    var args = Array.prototype.slice.call(arguments);//得到传入的参数
    var obj = args.shift();//得到第一个传入的对象
    var self = this; // 调用bindFn的函数
    
    return function() { // return一个函数 实现柯里化
        //拼接新参数
        var newArgs = args.concat(Array.prototype.slice.call(arguments));
        //下面这里使用了apply,用来改变self的直接调用者
        return self.apply(obj,newArgs);
    }
}
//测试一下,doSum方法实现对传入的参数的累加,并把累加结果返回
function doSum(){
    var arg = Array.prototype.slice.call(arguments);
    return arg.length ? arg.reduce((a,b) => a + b) : "";
}
var newDoSum = doSum.bindFn(null,1,2,3);
console.log(newDoSum());//6
console.log(newDoSum(4));//10
console.log(newDoSum(4,5));//15