Это "это" определенно сбивает с толку...
ключевое слово "это"
Когда объект javascript выполняет действие, используя свой метод и свойства, он должен иметь возможность читать свое собственное свойство. this — это ключевое слово в javascript, которое позволяет объекту ссылаться на собственное свойство.
const circle = { // property: state of object radius: 5, // method: an action that read and update state. getDiameter() { // When this method wants to refer other property or methods of its own // it should be able to refer circle object. return 2 * circle.radius; } };
Литерал объекта может иметь возможность ссылаться на себя рекурсивным способом вызова самого себя, но это не рекомендуется, поскольку объект может не быть выделен в памяти до вызова метода.
function Circle(radius) { // we never know whom we are referring to. ???.radius = radius; }; Circle.prototype.getDiameter = function () { // we never know whom we are referring to. return 2 * ???.radius; }; // we must define constructor function before we create instance using it. const circle = new Circle(5);
В отличие от литерала объекта, создание объекта с помощью функции конструктора требует, чтобы объект ссылался рекурсивно, но функция конструктора требует, чтобы объект ссылался.
Ой, там есть проблема, здесь «это» играет важную роль.
«Это» — это самоссылающаяся переменная, которая указывает на существующий объект или экземпляр, который она собирается создать. «это» создается движком javascript, и на него можно ссылаться в любом месте кода. Однако эта привязка (экземпляр, на который ссылается this) определяется динамически.
Способы вызова функции и эта привязка
Эта привязка определяется динамически в зависимости от того, как функция вызывается в javascript.
- Обычная функция
- Метод Функция
- Функция конструктора
- Косвенные способы, такие как Function.prototype.apply/call/bind
// this will be different depending on how its called. const foo = function () { console.dir(this); } // 1. regular function // call foo function in a common way. // this in foo refers to a global object window. foo(); // window // 2. method // assign foo as a property of obj // this in foo refers to an object obj where method is called. const obj = { foo }; obj.foo(); // obj // 3. constructor function // call foo with new operator as constructor function. // this in foo refers to an instance the constructor function created. new foo(); // foo {} // 4. Indirect ways such as function.prototype.apply/call/bind // this in foo is decided by its argument. const bar = {name: 'bar'}; foo.call(bar); // bar foo.apply(bar); // bar foo.bind(bar)(); // bar
Обычная функция
Все «это» в функции Regular привязывается к глобальному объекту.
//Nested Regular Function function foo() { console.log("foo's this: ", this) // window function bar() { console.log("bar's this: ", this) // window } } foo(); // Regular Function in Method Function var value = 1; const obj = { value: 100, foo() { console.log("foo's this: ", this) // {value:100, foo: f} console.log("foo's this.value: ", this.value) // 100 // Nested function inside method function bar() { console.log("bar's this: ", this) // window console.log("bar's this.value: ", this.value) // 1 } bar(); } } obj.foo(); // Callback function const obj = { value: 100, foo() { console.log("foo's this: ", this) // {value:100, foo: f} // Callback function setTimeout(function () { console.log("callback's this: ", this) // window console.log("callback's this.value: ", this.value) // 1 }, 100); } } obj.foo();
Значение var объявлено как свойство окна глобального объекта и вызывается this.value в обычных функциях как 1 вместо 100.
Метод Функция
«this» в функции метода привязывается к объекту, который ее вызывает. Это означает, что this не привязывается к объекту, у которого есть метод, а привязывается к объекту, который вызывает метод.
const person = { name: 'Lee', getName() { return this.name; } } console.log(person.getName()); // Lee const anotherPerson = { name: 'Kim' }; // allocate getName method to anotherPerson object. anotherPerson.getName = person.getName; //getName is called by object of anotherPerson. console.log(anotherPerson.getName()); // kim // allocate getName method to a variable const getName = person.getName; // getName is called as a regular function. console.log(getName()); // ''
Функция конструктора
«Это» внутри функции конструктора будет привязано к экземпляру, который будет создан.
function Circle(radius) { this.radius = radius; this.getDiameter = function () { return 2 * this.radius; }; } // create circle object with radius of 5 const circle1 = new Circle(5); // create circle object with radius of 10 const circle2 = new Circle(10); console.log(circle1.getDiameter()); // 10 console.log(circle2.getDiameter()); // 20
Функция-конструктор — это функция, которая создает новый объект. Мы определяем функцию-конструктор так же, как и обычную функцию, и когда мы вызываем эту функцию с оператором «новый», она запускается как функция-конструктор или будет работать как обычная функция.
// when function is not called with new operator, it is regular function. const circle3 = Circle(15); // Circle does not have return value so it returns undefined. console.log(circle3); // undefined // this in regular function bind to global object. console.log(radius); // 15
Косвенные способы, такие как Function.prototype.apply/call/bind
методы применения, вызова и связывания являются методами Function.prototype, поэтому все функции в javascript могут их использовать. Мы можем передать «этот аргумент», который мы хотим, чтобы функция имела, как «это».
// apply receives this argument and an argument array as function argument Function.prototype.apply(thisArg, [arg1, arg2, arg3...]) //call receives this argument and arguments seperated by commas. Function.prototype.call(thisArg, arg1, arg2, arg3...) //Example code function getThisBinding() { return this; } // object to use as this const thisArg = {a: 1}; console.log(getThisBinding.apply(thisArg)); // {a: 1} console.log(getThisBinding.call(thisArg)); // {a: 1}
Принимая во внимание, что bind не вызывает функцию, а возвращает функцию с указанием, что эта привязка завершена.
function getThisBinding() { return this; } // object to use as this const thisArg = {a: 1}; // bind method pass its first Argument thisArg to change this of function // and return new getThisBinding function. console.log(getThisBinding.bind(thisArg)); // getThisBinding // bind method does not call the new function so we have to call it. console.log(getThisBinding.bind(thisArg)()); // {a: 1}
Bind полезен, когда мы хотим решить ситуацию, когда this в методе и this во вложенной или callback-функции в методе не соответствуют друг другу.
const person = { name: 'Lee', foo(callback) { // (1) setTimeout(callback, 100); } } person.foo(function() { console.log(`Hi! my name is ${this.name}.`); // (2) Hi! my name is . }); // global this.name is window.name in browser and default value is '' // global this.name is undefined in Node.js
В ситуации (1) это будет относиться к объекту person, который вызывает метод foo. Однако, когда функция обратного вызова person.foo вызывается в ситуации (2), это относится к глобальному объекту window, который называется window.name.
Функция обратного вызова в person.foo является вспомогательной функцией person.foo, поэтому эти «это» должны быть одинаковыми, иначе код может работать не так, как предполагалось. К счастью, Bind может решить эту проблему.
const person = { name: 'Lee', foo(callback) { // this binding is passed into callback function using bind. setTimeout(callback.bind(this), 100); } } person.foo(function() { console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee. });
В заключение, эта привязка определяется тем, как функция вызывается следующими способами.
- Обычная функция — глобальный объект
- Функция метода — Объект, который вызывает метод
- Функция конструктора — Экземпляр, который будут создавать функции конструктора.
- Function.prototype.apply/call/bind — Первый аргумент, переданный в команду apply/call/bind.
Следующее содержание — это то, что я узнал из корейской книги Javascript Deep Dive. Комментарии по исправлению любой неправильной интерпретации приветствуются