Статья "Front-end package" досконально разбирается в этом, вызовите, примените и привяжите в JavaScript

JavaScript
Статья "Front-end package" досконально разбирается в этом, вызовите, примените и привяжите в JavaScript

предисловие

Это еще одна распространенная тема в JavaScript, а также классический экзаменационный вопрос, которого нельзя избежать на собеседовании.«Фронтальный пакет»На эту тему будет рассказано в третьей части цикла. Надпись очень белая, если что-то не так, пожалуйста, поправьте меня~

Прежде чем говорить об этом, давайте поговорим о环境это понятие. Когда язык работает, ему нужна среда, называемая宿主环境. Для JavaScript наиболее распространенной хост-средой является веб-браузер, а другой наиболее распространенной — Node.Также как хост-среда, node также имеет свой собственный движок JavaScript:V8(В настоящее время самый быстрый движок JavaScript, созданный Google). Эта статья в узле не будет расширяться. Обновите позже~

Первоначальный замысел этого

Первоначальный замысел этого дизайнаИспользуется внутри функции для ссылки на текущую среду выполнения.. Почему ты это сказал?

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

global this

В браузере глобально:

  1. это эквивалентно объекту окна;
  2. Объявление переменной с помощью var эквивалентно добавлению свойств к this или окну;
  3. Если вы объявляете переменную без использования var, let или const (es6), вы добавляете или изменяете свойства глобального this.
// 1
console.log(this === window); //true
//2
var name = "Jake";
console.log(this.name ); // "Jake"
console.log(window.name ); // "Jake"

//3
 age = 23;
 function testThis() {
   age = 18;
 }
 console.log(this.age ); // 23
 testThis();
 console.log(this.age ); // 18

Подводя итог: это старший брат в глобальной области видимости, он эквивалентен объекту окна (то есть указывает на окно), если вы объявите какие-то глобальные переменные (неважно где), эти переменные будут использоваться как свойства этого.

function this

Для задачи этой проблемы в функции есть предложение, которое очень полезно:运行时это всегда указывает на объект, который вызвал его последним.

взять каштан

var name = "windowsName";
function sayName() {
var name = "Jake";
console.log(this.name);   // windowsName
console.log(this);    // Window
}
sayName();
console.log(this) // Window

Давайте посмотрим на последний вызов sayNamesayName(); объект, не вызываемый ранее, является глобальным окном объекта, что эквивалентноwindow.sayName().

Следует отметить, что для строгого режима привязка глобального объекта по умолчанию недопустима, и для него установлено значение undefined. сообщит об ошибкеUncaught TypeError: Cannot read property 'name' of undefined。

Посмотрите на каштан ниже

function foo() {
    console.log( this.age );
}

var obj1 = {
    age : 23,
    foo: foo
};

var obj2 = {
    age : 18,
    obj1: obj1
};

obj2.obj1.foo(); // 23

Или предложение в начале, последний звонокfoo()даobj1, так что это указывает наobj1, выход 23.

это в конструкторе

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

Вот еще одна классическая тема:новый процесс

var a = new Foo("zhang","jake");

new Foo{
    var obj = {};
    obj.__proto__ = Foo.prototype;
    var result = Foo.call(obj,"zhang","jake");
    return typeof result === 'obj'? result : obj;
}

Если выполняется new Foo(), процесс выглядит следующим образом: 1) Создать новый объект obj;

2) Присвоить значения внутренним свойствам нового объекта и построить цепочку прототипов (указать неявный прототип нового объекта на явный прототип его конструктора);

3) Выполнить функцию Foo, и внутренне this указывает на вновь созданный объект obj во время выполнения (вызов здесь используется для изменения этой точки);

4) Если Foo явно возвращает данные типа объекта, данные возвращаются и выполнение завершается, в противном случае возвращается только что созданный объект obj.

var name = "Jake";jiuzhixiang
function testThis(){
  this.name = 'jakezhang';
  this.sayName = function () {
		return this.name;
	}
}
console.log(this.name ); // Jake

new testThis(); 
console.log(this.name ); // Jake

var result = new testThis();
console.log(result.name ); // jakezhang
console.log(result.sayName()); // jakezhang

testThis();  
console.log(this.name ); // jakezhang

Очевидно, кто новичок, на это указывает.

это в классе

Этот раздел взят из статьи Али Года Ниляна ---->[ПОСЛЕДНИЙ РАЗ] это: позвонить, подать заявку, связать

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

class Hero {
  constructor(heroName) {
    this.heroName = heroName;
  }
  dialogue() {
    console.log(`I am ${this.heroName}`)
  }
}
const batman = new Hero("Batman");
batman.dialogue();

Это в конструкторе относится к вновь созданному экземпляру класса. Когда мы вызываем batman.dialogue(), dialog() вызывается как метод, а batman является его получателем. Но если мы сохраним ссылку на метод dialog() и вызовем его позже как функцию, мы потеряем получателя метода, а параметр this теперь указывает на undefined .

const say = batman.dialogue;
say();

Причина ошибки в том, что классы JavaScript неявно выполняются в строгом режиме. Мы вызываем функцию say() без какой-либо автоматической привязки. Чтобы исправить это, нам нужно вручную связать функцию dialog() с batman, используя bind().

const say = batman.dialogue.bind(batman);
say();

это в вызове, применить и связать

call, apply и bind называются сильными привязками this, которые используются для изменения указания this при выполнении функции.В настоящее время все их приложения основаны на этом.

var name = 'zjk';
  function fun() {
  console.log (this.name);
}

var obj= {
  name: 'jake'
};
fun(); // zjk
fun.call(obj); //Jake

надfun.ccall(obj)Эквивалентноfun.capply(obj)а такжеfun.cbind(obj)()

это в стрелочных функциях

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

    let name = "zjk";

    let o = {
        name : "Jake",

        sayName: function () {
            console.log(this.name)     
        },

        func: function () {
            setTimeout( () => {
                this.sayName()
            },100);
        }

    };

    o.func()     // Jake

Используйте такие методы, как call, apply или bind, чтобы передать значение this, которое игнорируется стрелочными функциями. Стрелочные функции относятся к этому значению, установленному стрелочной функцией при ее создании.

let obj = {
  name: "Jake",
  func: (a,b) => {
      console.log(this.name,a,b);
  }
};
func.call(obj,1,2);// 1 2
func.apply(obj,[1,2]);//  1 2

最后放一道常见的this面试题

var number = 1;

var obj = {

	number:2,

	showNumber:function(){

	this.number = 3;

	(function(){

	console.log(this.number);

})();

	console.log(this.number);

}

};

obj.showNumber();// 答案就欢迎留在评论区囖~

call & apply

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

apply()

Метод apply() принимает два аргумента: область, в которой выполняется функция, и массив аргументов. Среди них вторым параметром может быть экземпляр Array или объект arguments.

function sum(num1, num2){ 
 return num1 + num2; 
} 
function callSum1(num1, num2){ 
 return sum.apply(this, arguments); // 传入 arguments 对象
} 
function callSum2(num1, num2){ 
 return sum.apply(this, [num1, num2]); // 传入数组
} 
console.log(callSum1(10,10)); //20
console.log(callSum2(10,10)); //20

В строгом режиме, если функция вызывается без указания объекта среды, это значение не будет преобразовано в окно. Если функция явно не добавлена ​​к объекту или не вызывается apply() или call(), это значение будет неопределенным.

call()

Метод call() работает так же, как и метод apply(), единственная разница между ними заключается в том, как они получают параметры. При использовании метода call() параметры, передаваемые функции, должны быть перечислены один за другим.

function sum(num1, num2){ 
 return num1 + num2; 
}
function callSum(num1, num2){ 
 return sum.call(this, num1, num2); 
} 
console.log(callSum(10,10)); //20

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

  • Если количество/порядок параметров определено, используйте call, а если количество/порядок параметров неясно, используйте apply.
  • Позаботьтесь о читабельности: используйте call, если количество параметров невелико.Если количество параметров велико, интегрируйте параметры в массив и используйте apply.

bind()

Метод bind() создает экземпляр функции, чье значение this привязано к значению, переданному в функцию bind(). Это означает, что bind() вернет новую функцию. Например:

window.color = "red"; 
var o = { color: "blue" }; 
function sayColor(){ 
 alert(this.color); 
} 
var objectSayColor = sayColor.bind(o); 
objectSayColor(); //blue

Разница между call/apply и bind

воплощать в жизнь:

  • Выполнить функцию сразу после того, как call/apply изменит этот контекст функции
  • bind возвращает функцию после изменения контекста и не выполняет функцию
function add (a, b) {
    return a + b;
}

function sub (a, b) {
    return a - b;
}

add.bind(sub, 5, 3); // 这时,并不会返回 8
add.bind(sub, 5, 3)(); // 调用后,返回 8

возвращаемое значение:

  • call/apply возвращает результат выполнения fun
  • bind возвращает копию fun и указывает эту точку fun, сохраняя параметры fun.

Основная идея call/apply/bind

Из приведенных выше нескольких простых примеров видно, чтоcall/apply/bindЭто заимствование методов у других объектов, что также соответствует нашему обычному мышлению, для простого каштана. Я прекрасно провел время с одним из моих одноклассников. Мы все вместе носили одежду и обувь. Когда я ходил по магазинам, он покупал одежду, а я покупал обувь. После того, как я вернулся, я хотел носить одежду, которую он купил, но я Так что я одолжил его одежду. Таким образом, я не только достигаю цели ношения новой одежды, но и экономлю деньги~ У объекта А есть метод, и объекту Б тоже по какой-то причине нужно использовать тот же метод.В это время Б может заимствовать метод объекта А, что не только достигает цели, но и экономит память.

Это основная идея call/apply/bind: заимствование.

Сценарии применения call/apply/bind

Использование call/apply/bind не будет расширяться из-за ограниченного места.Вы можете взглянуть на следующую статью.Лично я думаю, что она очень хорошо написана!

В «Галантерее» подробно рассказывается о различии и использовании терминов «вызвать, применить и связать».

Рукописная реализация apply, call, bind

apply

1. Сначала расширить метод на прототипе функции и получить 2 параметра,

Function.prototype.myApply = function (context, args) {}

2. Потому что если контекст не передан, this будет указывать на окно, поэтому контекст и аргументы здесь будут отказоустойчивыми.

Function.prototype.myApply = function (context, args) { 
    // 处理容错
    context = (typeof context === 'object' ? context : window)
    args = args ? args : []
}

3. Используйте неявную привязку для реализации явной привязки

Function.prototype.myApply = function (context, args) {
    // 处理容错
   context = (typeof context === 'object' ? context : window)
   args = args ? args : []
    //给context新增一个独一无二的属性以免覆盖原有属性
    const key = Symbol()
    context[key] = this
    //通过隐式绑定的方式调用函数
    context[key](...args)
}

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

Function.prototype.myApply = function (context, args) {
   // 处理容错
    context = (typeof context === 'object' ? context : window)
    args = args ? args : []
    //给context新增一个独一无二的属性以免覆盖原有属性
    const key = Symbol();
    context[key] = this;
    //通过隐式绑定的方式调用函数
    const result = context[key](...args);
    //删除添加的属性
    delete context[key]
    //返回函数调用的返回值
    return result
}

Реализована такая нищенская версия apply.Что касается оптимизации, то в интернете есть много отличных писателей, которые хорошо их написали.Можете поискать их.Мы не будем продолжать их оптимизацию здесь.

Проверьте это~


function fun(...args) {
  console.log(this.name,...args)
}
const result = { 
name: 'Jake' 
}
// 参数为数组;方法立即执行
fun.myApply (result, [1, 2])

Результат выглядит следующим образом, указывая на то, что метод применения был реализован.

在这里插入图片描述

call

Реализация call почти такая же, как и у apply, только переходим непосредственно к коду.

//传递参数从一个数组变成逐个传参了,不用...扩展运算符的也可以用arguments代替
Function.prototype.NealCall = function (context, ...args) {
    //这里默认不传就是给window,也可以用es6给参数设置默认参数
     context = (typeof context === 'object' ? context : window)
    args = args ? args : []
    //给context新增一个独一无二的属性以免覆盖原有属性
    const key = Symbol();
    context[key] = this;
    //通过隐式绑定的方式调用函数
    const result = context[key](...args);
    //删除添加的属性
    delete context[key];
    //返回函数调用的返回值
    return result;
}

bind

Реализация bind чуть более хлопотная, т. к. bind возвращает связанную функцию, а apply — прямой вызов, а по факту — просто возвращает функцию, выполняющую вышеперечисленные операции применения, однако есть момент, который необходимо быть судимым, потому что возвращая новую функцию, необходимо учитывать использование новой для вызова, а приоритет новой относительно высок, поэтому необходимо судить о вызове новой, и еще одна особенность заключается в том, что при вызове связывания , параметры могут быть переданы, и новая функция, сгенерированная после вызова, также может передавать параметры, эффект тот же, поэтому с этой частью также следует разобраться.

Function.prototype.myBind = function (objThis, ...params) {
    const thisFn = this; // 存储源函数以及上方的params(函数参数)
    // 对返回的函数 secondParams 二次传参
    let fToBind = function (...secondParams) {
        const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用
        const context = isNew ? this : Object(objThis) // new调用就绑定到this上,否则就绑定到传入的objThis上
        return thisFn.call(context, ...params, ...secondParams); // 用call调用源函数绑定this的指向并传递参数,返回执行结果
    };
    if (thisFn.prototype) {
        // 复制源函数的prototype给fToBind 一些情况下函数没有prototype,比如箭头函数
        fToBind.prototype = Object.create(thisFn.prototype);
    }
    return fToBind; // 返回拷贝的函数
};

Суммировать

  1. В браузере это указывает на объект окна в глобальной области видимости;
  2. В функции это всегда указывает на объект, вызвавший его последним;
  3. В конструкторе this указывает на новый объект из new;
  4. Это в вызове, применении и привязке строго привязано к указанному объекту;
  5. Это особенность стрелочных функций. Это this родительской области, а не this во время вызова. Вы должны знать, что все первые четыре метода определяются во время вызова, то есть являются динамическими, а this стрелочной функции является статической. , она определяется при выполнении оператора;
  6. apply, call и bind — это все API, встроенные в функции с помощью js. Вызов их может указать выполнение this для функции, а также может передавать параметры.

Поставьте картинку в конце, чтобы помочь памяти

позже

Стыдно сказать, когда я только изучал JS, я написал на эту тему учебную заметку. Казалось, она была закончена за 2 часа. Я думал, что смогу закончить это за день. Я также прочитал несколько статей, написанных разными большие ребята раньше.Я разместил ссылки ниже.Я могу только вздохнуть, что письмо действительно хорошо! Однако в последние несколько дней у меня все еще есть новое понимание этой точки знаний. Кроме того, Сяошэн - новичок в области переднего плана. Первоначальная цель написания статьи - получить более глубокое впечатление и понимание этого пункта знаний. То, что он написал, также очень белое. Если в тексте есть что-то не так, пожалуйста, поправьте меня~ и тогда есть надежда Друзья, которые прочитали это, могут поставить лайк или подписаться на волну~ Я буду продолжать выводить!

ссылка на личный блог

Персональная домашняя страница CSDN

Персональная домашняя страница Nuggets

Личная страница Цзяньшу

Справочная статья

红宝书第五章

JavaScript 的 this 原理

【THE LAST TIME】this:call、apply、bind

JavaScript中的this陷阱的最全收集--没有之一

详解 JS 中 new 调用函数原理

js基础-面试官想知道你有多理解call,apply,bind?[不看后悔系列]