Про это...

this

Контекст исполнения.
call. apply. bind.

Жигалов Сергей

Git + WebStorm = ♡

fork
fork
fork
fork
fork

Итого

fork

вернемся к THIS

C++

C++


class User {
private:
    int age=30;
public:
    void showAge() {
        std::cout << this->age;
    };
};
                

C++


int main() {
    User* sergey = new User;

    sergey->showAge();
}
                

Java

Java


public class User {
    private int age = 30;

    public void showAge() {
       System.out.println(this.age);
    }
}
                

Java


public static void
    main(String []args) {
    User sergey = new User();

    sergey.showAge();
}
                

Свойства this

  • является ключевым словом языка
  • указывает на текущий объект
  • нельзя перезаписать

JavaScript

JavaScript


function User () {
    return {
        age: 30,
        showAge: function () {
            console.log(this.age);
        }
    }
}
                

JavaScript


var sergey = new User();

sergey.showAge();
                

Свойства this в JS

  • является ключевым словом языка
  • указывает на текущий объект
  • нельзя перезаписать
  • использовать this за пределами объектов

В браузере


console.log(this.innerWidth); //1684
            

В Node.js


this.process.version; // 'v0.12.7'
            

Контекст исполнения


function add(a, b) {
    return a + b;
}

var sum = add(4, 2);
            

Код

function add(a, b) {
    return a + b;
}

var sum = add(4, 2);
                

Контекст

globalContext = {
  lexicalEnv: {
  }
};
                

Код

function add(a, b) {
    return a + b;
}

var sum = add(4, 2);
                

Контекст

globalContext = {
  lexicalEnv: {
    sum: undefined
  }
};
                

Код

function add(a, b) {
    return a + b;
}

var sum = add(4, 2);
                

Контекст

globalContext = {
  lexicalEnv: {
    sum: undefined,
    add: <ссылка на add>
  }
};
                

Код

function add(a, b) {
    return a + b;
}

var sum = add(4, 2);
                

Контекст

functionContext = {
    lexicalEnv: {
    },
    parentEnv:
globalContext.lexicalEnv
};
                

Код

function add(a, b) {
    return a + b;
}

var sum = add(4, 2);
                

Контекст

functionContext = {
    lexicalEnv: {
        a: 4,
        b: 2
    },
    parentEnv:
globalContext.lexicalEnv
};
                

Код

function add(a, b) {
    return a + b;
}

var sum = add(4, 2);
                

Контекст

functionContext = {
    lexicalEnv: {
        a: 4,
        b: 2
    },
    parentEnv:
globalContext.lexicalEnv,
    this:
<ссылка на объект>
};
                

This зависит от

  1. режима работы интерпретатора
  2. как мы попали на участок кода
  3. типа участка кода

В обычном режиме


function func() {
    return this;
}
            

func(); // window
            

В строгом режиме


function strictFunc() {
    'use strict';

    return this;
}
            

strictFunc(); // undefined
            

This зависит от

  1. режима работы интерпретатора
  2. как мы попали на участок кода
  3. типа участка кода

1. Метод объекта


var sergey = {
    age: 30,

    getAge: function () {
        'use strict';
        return this.age;
    }
}
            

1. Метод объекта


sergey.getAge(); // 30
            

2. Заимствованный метод


var sergey = {
    age: 30,

    getAge: function () {
        'use strict';
        return this.age;
    }
}
            

2. Заимствованный метод


var getAge = sergey.getAge;

getAge();
// TypeError:
//     Cannot read property
//     'age' of undefined
            

2. Заимствованный метод


var block = {
    innerHeight: 300,

    getHeight: function () {
        return this.innerHeight;
    }
}
            

var getHeight = block.getHeight;

getHeight(); // ?
            

2. Заимствованный метод


var block = {
    innerHeight: 300,

    getHeight: function () {
        return this.innerHeight;
    }
}
            

var getHeight = block.getHeight;

getHeight(); // 1274
            

3. call


var sergey = {
    age: 30,

    getAge: function () {
        'use strict';
        return this.age;
    }
}
            

var oleg = {
    age: 23
}
            

3. call


func.call(thisArg, arg1, arg2, ...);
            

3. call


// Заимствуем метод
var getAge = sergey.getAge;

// Безопасно вызываем от лица Олега
getAge.call(oleg); // 23
            

3. call


function func() {
    var args =
        [].slice.call(arguments);
}
            

4. apply

4. apply


func.apply(thisArg, [arg1, ...]);
            

4. apply


var fruits = [];

// Добавляем по одному
fruits.push('Banana');

// Сразу пачкой
fruits.push('Orange', 'Lemon');
            

4. apply


var fruits = [];
var citrus = ['Orange', 'Lemon'];
            

// Добавляем по одному
fruits.push('Banana');

// Сразу массивом
fruits.push.apply(fruits, citrus);
            

4. apply


Math.min(3, 5, 2, 6);        // 2

var numbers = [3, 5, 2, 6];
Math.min(numbers);           // NaN
            

4. apply


var numbers = [3, 5, 2, 6];
Math.min.apply(null, numbers); // 2
            

5. callback

5. callback


'use strict';

var person = {
    name: 'Sergey',
    showItems: function () {
        var cb = function (item) {
           console.log(this.name + ' have ' + item);
        };
        ['keys', 'phone'].forEach(cb);
    }
}
            

5. callback


person.showItems(); // TypeError:
    // Cannot read property
    // 'name' of undefined
            

Способ 1

сохранение this в лексическом окружении

5. callback


'use strict';

var person = {
    name: 'Sergey',
    showItems: function () {
        var _this = this;
        var cb = function (item) {
           console.log(_this.name + ' have ' + item);
        };
        ['keys', 'phone'].forEach(cb);
    }
}
            

5. callback


person.showItems();
    // Sergey have keys
    // Sergey have phone
            

Под капотом

Код

'use strict';

var person = {
  name: 'Sergey',
  showItems: function () {
    var _this = this;
    var cb = function (item) {
      console.log(_this.name +
        ' have ' + item);
      };
      ['keys', 'phone']
        .forEach(cb);
  }
}
                

Контекст

showItemsContext = {
  lexicalEnv: {
    _this: undefined
  },
  parentLexicalEnv:
    globalContext.lexicalEnv,
  this: Ссылка на `person`
};
                

Под капотом

Код

'use strict';

var person = {
  name: 'Sergey',
  showItems: function () {
    var _this = this;
    var cb = function (item) {
      console.log(_this.name +
        ' have ' + item);
      };
      ['keys', 'phone']
        .forEach(cb);
  }
}
                

Контекст

showItemsContext = {
  lexicalEnv: {
    _this: Ссылка на `person`
  },
  parentLexicalEnv:
    globalContext.lexicalEnv,
  this: Ссылка на `person`
};
                

Под капотом

Код

'use strict';

var person = {
  name: 'Sergey',
  showItems: function () {
    var _this = this;
    var cb = function (item) {
      console.log(_this.name +
        ' have ' + item);
      };
      ['keys', 'phone']
        .forEach(cb);
  }
}
                

Контекст

callbackContext = {
  lexicalEnv: {
    item: 'keys'
  },
  parentLexicalEnv:
    showItemsContext.lexicalEnv,
  this: undefined
};
                

Под капотом

Код

'use strict';

var person = {
  name: 'Sergey',
  showItems: function () {
    var _this = this;
    var cb = function (item) {
      console.log(_this.name +
        ' have ' + item);
      };
      ['keys', 'phone']
        .forEach(cb);
  }
}
                

Контекст

callbackContext = {
  lexicalEnv: {
    item: 'keys'
  },
  parentLexicalEnv:
    showItemsContext.lexicalEnv,
  this: undefined
};
                

ES2015

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

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


'use strict';

var person = {
    name: 'Sergey',
    showItems: function () {
        var cb = item => {
           console.log(this.name + ' have ' + item);
        };
        ['keys', 'phone'].forEach(cb);
    }
}
                

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


person.showItems();
    // Sergey have keys
    // Sergey have phone
            

Способ 2

передача this в качестве аргумента

Через аргумент


'use strict';

var person = {
  name: 'Sergey',
  showItems: function () {
    var cb = function (item) {
      console.log(this.name + ' have ' + item);
    };
    ['keys', 'phone'].forEach(cb, this);
  }
}
            

Через аргумент


person.showItems();
    // Sergey have keys
    // Sergey have phone
            

Способ 3

.bind()

bind


var bindedFunc = func.bind(thisArg, arg1, ...);
            

bind


'use strict';

var person = {
  name: 'Sergey',
  showItems: function () {
    var cb = function (item) {
      console.log(this.name + ' have ' + item);
    };
    var bindedCb = cb.bind(this);
    ['keys', 'phone'].forEach(bindedCb);
  }
}
            

bind


person.showItems();
    // Sergey have keys
    // Sergey have phone
            

myBind


cb.myBind = function(_this) {
    var fn = this;

    return function () {
        var args = [].slice.call(arguments);
        return fn.apply(_this, args);
    };
};
            

var bindedCb = cb.myBind(this);
            

6. Конструктор

6. Конструктор


function User (age) {
    return {
        age: age,
        showAge: function () {
            console.log(this.age);
        }
    }
}
            

6. Конструктор


var sergey = new User(30);

sergey.showAge(); // 30
            

This зависит от

  1. режима работы интерпретатора
  2. как мы попали на участок кода
  3. типа участка кода
  • Global code

Браузер


this.close();
            

window.close();
            

Node.js


this.process.version;
            

global.process.version;
            
  • Global code
  • Function code

Function code


function add(a, b) {
    return a + b;
}

var sum = add(2, 5);
            
  • Global code
  • Function code
  • Eval code

Eval code


var sum = eval('2 + 7');
            

Eval code


var person = {
    name: 'Sergey',
    showName: function () {
        return eval('this.name');
    }
}
            

person.showName(); // Sergey
            

Eval code


var person = {
    name: 'Sergey',
    showName: function () {
        var evil = eval;
        return evil('this.name');
    }
}
            

person.showName(); // ""
            
  • Global code
  • Function code
  • Eval code
  • Module code nodejs logo

Module code


// В module.exports кладётся то,
// что будет торчать наружу
module.exports.weeks = 54;
module.exports.days = 366;
            

// Так как this это module.exports,
// мы можем писать так:
this.isLeap = true;
            

Домашечка