The Javascript `this` keyword explained

Hi friend, welcome to my blog!

In this tutorial, we are going to explore the javascript this keyword...

The this keyword in javascript is always a hard nut to crack for people learning javascript. However, the rules governing this are relatively straightforward once you get more familliar wit them.

This, a reserved word in javascript, refers to some object. Its value is detemined at execution.

I am going to explain how to determine the value of the this keyword using the terms default bindg, implicit binding, explicit binding, and 'new' bindg from my favorite javascript book You Don't Know JS by kyle Simpson.

Defaut Binding:

The default binding exists when the this keyword is in the global context. When the this keyword is out of a declared object, its value is the global object, which is the Window object in the browser.

console.log(this); //Output: window object

let item = this;
console.log(item); //Output: window object

function defaultBindingExample() {
    return this;
}

console.log(defaultBindingExample()); //Output: window object

Note:In strict mode, when the this keyword is out in the whild, outside of a funciton or class, its value is still the global object, however, inside a function,  if the value of this is not set when entering an execution context, it remains undefined.

'use strict'

console.log(this); //Output: window object

let item = this;
console.log(item); //Output: window object

function defaultBindingExample() {
    return this;
}

console.log(defaultBindingExample()); //Output: undefined

Implicit binding

When the keyword this is inside a decalred object, the value is the closest parent object

const schoolProfile = {
    name: 'Amaka,
    university: 'University of Nigeria',
    department: 'Computer Science',
    greeting: function() {
        return `Hi, my name is ${this.name} I attend ${this.universiy}`
    },
    checkThisContext: function() {
        return this === SchoolProfile
    },
    courses: {
        listCourses: function() {
            return `At the department of ${this.department}, we study python`
        },
        checkThisContext: function() {
            return this === SchoolProfile
        }
    }
}


console.log(schoolProfile.greeting())
//Output: Hi, my name is Amaka I attend University of Nigeria

console.log(schoolProfile.checkThisContext())
//Output: true

console.log(schoolProfile.courses.listCourses())
//Output: At the department of undefined, we study python

console.log(schoolProfile.course.checkThisContext())
//Output: false

Following the implicit binding rule, this in schoolProfile.greeting() refers to schoolProfile which is the closest parent object, in the same way, this in schoolProfile.courses.listCourses() refers to courses, and because the courses object does not have a key named departmet, the value is undefined.

To solve this, we need to explicitly change the value of this and that is where the call, bind, apply comes in as we will see in next section.

Explicit binding

To explicitly determing which object the this keyword refers to, we can use one of three methods: call, apply, or bind. These funcitons allow us to choose what we want the context of this to be.

Note that the three methods can only be used by functions.

 Call()

The first argument in the call method is whatever you want the value of this to be. The second to nth parameters of call are the parameters of the function you are invoking, separated by commas.

Note: call will immediately invoke the function that is attached to.

const studentGreetings = {
    greeting: function() {
        return `${this.name} is a ${this.gender} studying ${this.department}`
    }
}

const student1 = {
    name: 'Solomon Ant',
    gender: 'male',
    department: 'Computer Science'
}

const student2 = {
    name: 'Amanda Brandy',
    gender: 'female',
    department: 'Medicine and Surgery'
}

console.log(studentGreetings.greeting.call(student1));
//Output: Solomon Ant is a male studying Computer Science

console.log(studentGreetings.greeting.call(student2));
//Output: Amanda Brandy is a female studying Medicine and Surgery

With argument:

const classmates = {
    person1: "Kim",
    person2: "Khole",
    person3: "Kylie"
}

function greet(myName) {
    return `Hi, my name is ${myName}, my classmates are ${this.person1}, ${this.person3},                         
            and ${this.person3}.`;
}

console.log(greet.call(classmates, 'Amaka'))
//Output: Hi, my name is Amaka, my classmates are Kim, Khloe, and Kylie.

One of the most common use cases of the call method is to convert array-like object eg functions arguments into actual array.

function addArguments1() {
    return arguments.reduce((acc, next) => {
        return acc + next;
    }, 0);
}

console.log(addArguments1(1,2,3,4,5))

//Output: TypeError: arguments.reduce is not a function at addArguments1

function addArguments2() {
    //using the slice method, make a copy of the array using call to set arguments as the         
    //context
    let argumentArr = [].slice.call(arguments)

    return argumentArr.reduce((acc, next) => {
        return acc + next;
    }, 0)
}

console.log(addArguments2(1,2,3,4,5))
//Output: 15

Apply()

Apply is almost identical to call, the only difference is that it takes two parameters at most. The first is what we want the value of the keyword this to be, the second is an array of arguments we want to pass the function in which we are changing the value of this. Apply also immediately invokes the function that it is attached to.

let aboutNick = {
    name: 'Nick',
    sayHi: function() {
        return `Hi ${this.name}`
    }
}

let aboutTom = {
    name: 'Tom'
}

console.log(aboutNick.sayHi())
// Output: Hi Nick

console.log(aboutNick.sayHi.apply(aboutTom))
// Output: Hi Tom

With arguments:

const person = {
    firstName: 'Adam',
    lastName: 'Levine'
}

function greet(greeting, message) {
    return `${greeting} ${this.firstName}. ${message}`;
}

console.log(greet.apply(person, ['Hello', 'How are you?']));
// Output: Hello Adam. How are you?

One of my favorite use cases of the apply method is when used with Math.max function. The Math.max function return the largest number among a group of numbers passed to it. When used with apply, you can pass in an array as parameter and get the largest number

//Normal use of Math.max()
console.log(Math.max(1,16,22,30)) 
// Output: 30

//Math.max() - passing an array to it, we get NaN
console.log(Math,max([1,16,22,30])) 
// Output: NaN

//apply() to rescue!
console.log(Math.max.apply(this, [1,16,22,30]))
// Output: 30

Another helpful use case is in flattering an array of arrays:

const arrays = [[29, 392, 18], ['Orange', 'Mango'], ['London', 'Canda']]

let flatenedArr = [].concat.apply([], arrays)

console.log(flateneArr) //Output: [29,392,18,"Orange", "Mango", "London, "Canda"]

Bind()

Bind is also similar to call, the difference is instead of invoking the function immediately, it returns a function definition. Bind is powerful and is the building block for some advanced programming concepts eg Currying.

const person = {
    firstName: 'Adam',
    lastName: 'Levine
}

function greet(greeting, message) {
    return `${greeting} ${this.firstName}. ${message}`;
}

let adamGreeting = greet.bind(person, 'Hi', 'How are you?')

console.log(adamGreeting);
//Output: bind() returns a function definition

console.log(adamGreeting());
//Hi Adam, How are you?

Bind is useful when we do not know the whole argument that will be passed to a function, which means we don't want to invoke the function right away, we just want to return a function definition.

const student = {
    name: 'Christiene',
}

function calculation(x,y,z) {
    return `${this.name} calculated ${x + y + z}`;
}

let chrisCalc = calculation.bind(student, 2, 3)

console.log(christCalc);
// Output: it will output a function definition

console.log(christCalc(10));
// Output: Chritiene calculated 15

Another use case is to set the context o the this keyword for a function that will be called later.

In the code below, the this in the asynchronous setTimeOut() function, loses its context to the global object

const aboutNick = {
    name: 'Nick',
    sayHi: function() {
        setTimeout(function() {
            console.log(`Hi ${this.name}`)
            }, 1000)
        }
    }
}

aboutNick.sayHi() // Hi undefined (after one sec)

Using bind(), we can set the context of this to be the object it's in

const aboutNick = {
    name: 'Nick',
    sayHi: function() {
        setTimeout(function() {
            console.log(`Hi ${this.name}`)
        }.bind(this), 1000)
    }
}

aboutNick.sayHi() // Hi Nick (after one sec)

Important Note 

With the introduction of ES6 array function, using bind in this way is no longer required since the array function retain the scope of this.

const aboutNick = {
    name: 'Nick',
    sayHi: function () {
        setTimeout(() => {console.log(`Hi ${this.name}`)}, 1000)
    }
}

aboutNick.sayHi() //Hi Nick

Summary of the call, bind, apply in a tabular form:

 The new keyword

Consider the following code:

function Person (name, age) {
    this.name = name,
    this.age = age
}

Notice the capital letter P I am using to start the name of the function, it's just a convention for writing a special type of function called the constructor function.

Let's see what happens when we invoke this function.

let aboutJohn = Person('John Doe', 35)

console.log(aboutJohn); //undefined

oops! we get undefined, Rightly so, there's no return keyword on the funciton, there's no explicit binding of the this keyword, the Person function isn't called inside of a parent object, the default binding took place.

function Person(name, age) {
    this.name = name,
    this.age = age
}

let aboutJohn = Person('Jonh Doe', 35)

console.log(aboutJohn); //undefined

console.log(window.name); //Jonh Doe

console.log(window.age); //35

To change the value of this to the consrtuctor funciton we use the new keywork.

Let's see how that works below:

function Person(name, age) {
    this.name = name,
    this.age = age
}

let aboutJohn = new Person('John Doe', 35)

console.log(aboutJohn); //{name: "John Doe', 35}

This is what the new keyworkd did above

1. it creates a new empty object.

2. sets the context of the this keyword to the created object.

3. adds an implicit return this to the function.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值