每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,欢迎关注开源日报。交流QQ群:202790710;微博:https://weibo.com/openingsource;电报群 https://t.me/OpeningSourceOrg


今日推荐开源项目:《React 组件库工具 Compositor Kit》传送门:GitHub链接

推荐理由:这是一个可以帮助你管理手上的 React 组件,你可以很方便的通过它来改变每个组件的属性然后实际的打开它们进行测试,它还有很多测试用的组件:Debug 可以提供对象的 JSON 格式的输出用来调查它的属性,而 XRay 提供网格背景和组件的轮廓范围等等,有兴趣的朋友可以自己试试看。


今日推荐英文原文:《Why THIS is so powerful in JavaScript》作者:Kristy Burge

原文链接:https://medium.com/@thefrontendcat/10-things-you-will-eventually-learn-about-javascript-projects-efd7646b958a

推荐理由:介绍了 JavaScript 中关于 this 的二三事

Why THIS is so powerful in JavaScript

What’s “this” all about anyways?

In vanilla JavaScript, this is a topic that is confusing to newbies. (See what I did there?!)

Especially to those of us learning to code from a non-programming background.

I worked in accounting. We used acronyms like JIB, AP, or AR. But the dictionary easily defines these terms.

The concept of “this” can be thoroughly confusing for beginners learning JavaScript as their first programming language because of the nuances.

I remember learning about objects for the first time and ‘this’ being used in creating constructor functions, but never truly explained.

So, let’s change that.

Everything’s clear as mud, right?!

What is this?

Here’s my unofficial, no jibberish definition:

“this” is a keyword used in JavaScript that has a special meaning depending on the context it’s being used

The reason “this” is so confusing when you’re first learning JavaScript is because the context of “this” changes depending on how it’s used.

You might want to think of ‘this’ as a dynamic keyword because it changes depending on the context.

I like this excerpt from Ryan Morr’s article, Understanding Scope and Context in JavaScript:

Context is always the value of the this keyword which is a reference to the object that “owns” the currently executing code. — Ryan Morr

But context around ‘this’ is not really the same thing as execution context.

So, when we want to use the ‘this’ keyword, we are using that keyword to reference an OBJECT. But which object?

Let’s dig into some examples:

  1. When “this” points to the Window object (Global)
  2. Methods on objects
    When “this” is used as a method on an object
    When “this” is used as a method on a nested object
    When “this” is used as a method on an object (arrow functions)
  3. Function contexts
  4. The ‘new’ keyword — why this is so powerful
    Creating instances of an object from a constructor function with “new” keyword

1. When “this” points to the Window object

If you try to refer to ‘this’ outside of a function, it will refer to the GLOBAL context (a.k.a. the Window object in the browser).

Functions that sit in the global context (not as a method on an object) will point the ‘this’ keyword back to the Window object.

Try it for yourself.

console.log(this);

// returns the Window object
// Window {	postMessage: ƒ, 
// blur: ƒ, 
// focus: ƒ, 
// close: ƒ, 
// frames: Window, …}

function myFunction() {
	console.log(this);
}

// Call the function
myFunction(); 

// returns the Window object! 
// Window {	postMessage: ƒ, 
// blur: ƒ, 
// focus: ƒ, 
// close: ƒ, 
// frames: Window, …}

2. Methods on Objects

When “this” is inside an object, it refers to the object itself (*)

Let’s say you create an object and attach a method. When ‘this’ is used inside the method, ‘this’ returns the ‘dog’ object.

var dog = {
  name: 'Chester',
  breed: 'beagle',
  intro: function(){
    console.log(this);
  }
};

dog.intro();

// returns the dog object and all of it's properties and methods
// {name: "Chester", breed: "beagle", intro: ƒ}
//    breed:"beagle"
//    intro:ƒ ()
//    name:"Chester"
//    __proto__:Object

Nested Objects

The value of ‘this’ can get more convoluted based on nesting in objects.

Anytime you have an object nested inside another object, and then ‘this’ points to the object on which the method is defined.

Example:

var obj1 = {
  hello: function() {
    console.log('Hello world');
    return this;
  },
  obj2: {
      breed: 'dog',
      speak: function(){
            console.log('woof!');
            return this;
        }
    }
};
 
console.log(obj1);
console.log(obj1.hello());  // logs 'Hello world' and returns obj1
console.log(obj1.obj2);
console.log(obj1.obj2.speak()); // logs 'woof!' and returns obj2

* But wait! Arrow functions are TOTALLY different.

I’m not kidding.

Remember when I said that if you use ‘this’ as a method inside an object, the ‘this’ keyword gets assigned to that object?

Arrow functions don’t work like that.

Instead, ‘this’ points to the Window object / global context.

Try the code below in the console:

var objReg = {
  hello: function() {
    return this;
  }
};
 
var objArrow = {
  	hello: () => this
};
 
objReg.hello(); // returns the objReg object that we expect
objArrow.hello(); // returns the Window object!

According to MDN: “An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.”

So, listen to the experts and don’t use arrow functions as methods on your objects!

See, it wasn’t that bad.


3. When ‘this’ is used in a normal function context:

When a function sits in the global scope, then the value of ‘this’ is the Window object. Think of it as the test function being a method on the context in which it sits (the window object).

So, it makes sense then that ‘this’ will point to the Window object.

Example:

function test() {
  console.log('hello world');
  console.log(this);
}

test();

// hello world
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}

However, if a function is executed in strict mode, ‘this’ will return undefined because strict mode does not allow default binding.

Try the below code in your console:

function test() {
  'use strict';
  return this;
}

console.log( test() );
// returns undefined, not the Window object … interesting

So remember: if you want to use ‘this’ in strict mode, ‘this’ must be defined by the execution context because ‘this’ is not automatically attached to any object (not even the Window object).

When ‘this’ is called from a function declared outside of an object:

You can assign a property to the object and assign it to the function chase().

The ‘dog’ variable doesn’t have any methods … until I create dog.foo and assign it to the chase() function.

Invoke the new foo method (which calls chase() to execute).

The values of ‘this’ inside of chase() point to the object ‘dog’ which called the function.

But, chase() cannot be called without being assigned to an object first. Or it will return undefined.

var dog = {
  breed: 'Beagles',
  lovesToChase: 'rabbits'
};

function chase() {
  console.log(this.breed + ' loves chasing ' + this.lovesToChase + '.'); 
//   console.log(this);
}

dog.foo = chase;
dog.foo(); // returns Beagles loves chasing rabbits.

chase(); // returns undefined because when run in the global context, window object does not have these properties defined

In this context, the function chase() returns undefined because when run in the global context, ‘this’ points to the window object by default and does not have the properties (‘breed’ or ‘lovesToChase’) defined on that object.


4. The ‘new’ keyword and why ‘this’ is so powerful as you continue your JavaScript journey

It’s useful to use the keyword “this” when creating constructor functions for objects because that allows the method to work on any object.

A constructor function allows us to define an object (like Number or String, except it has its own special properties and methods).

I love dogs, so let’s create a dog object type and store some information inside of it.

function Dog(breed, name, friends){
    this.breed = breed;
    this.name = name;
    this.friends = friends;	
    this.intro = function() {
        console.log(`Hi, my name is ${this.name} and I’m a ${this.breed}`);
        return this;
    }; 
}

It’s important to note that ‘this’ does not have a value while in the constructor function.

However, each time you create a new instance of the Dog object, the value of ‘this’ will point to the object you just instantiated. It will NOT point to the Dog prototype itself.

Use the ‘new’ keyword with the name of the object type and pass any required parameters when instantiating the new instance of the object.

It’s similar to doing something like this:

var str = new String('Hello world');
/*******
You could do the above, but it's best to avoid it (instead do like the variable str2 below)
(because JavaScript knows that anything inside single or double quotes has the type of String)
Same goes for other primitives. This is for example purposes only. 
NOTE: To clarify -- the only time I ever use the new keyword in practice is when I use a function constructor and create my own object type.
*******/

var str2 = 'Hello world';
// both have the prototype of String and inherit all the String methods and properties
// Using the Dog prototype, create a new instance 
var chester = new Dog('beagle', 'Chester', ['Gracie', 'Josey', 'Barkley']);
chester.intro();        // returns Hi, my name is Chester and I'm a beagle
console.log(chester);   // returns Dog {breed: "beagle", name: "Chester", friends: Array(3), intro: ƒ}

// Here's another example:
var City = function(city, state) {
  this.city = city || "Phoenix";
  this.state = state || "AZ";
  this.sentence = function() {
    console.log(`I live in ${this.city}, ${this.state}.`);
  };
};

var phoenix = new City(); // use the default parameters
console.log(phoenix); // returns the phoenix object (an instance of the City prototype)
phoenix.sentence(); // returns "I live in Phoenix, AZ."

var spokane = new City('Spokane', 'WA');
console.log(spokane); // returns the spokane object (another instance of the City prototype)
spokane.sentence(); // returns "I live in Spokane, WA."

The ‘new’ keyword is important!

It changes the context of ‘this’ to the instance that it created from the Object constructor function.

That’s why when ‘this’ is used inside a constructor function, it doesn’t point to it’s object prototype. It actually points to the new instance of that object type.

So, what makes THIS so powerful in this context, you’re asking?

It allows us to scale applications and make repeatable code.

Think about your social media accounts.

Each account could be an object based off a prototype called ‘Friend’. Each time a new account is created, it inherits the methods and properties from its prototype, while passing in unique information like name, password, interests, job, etc.

// Constructor Function
var Friend = function(name, password, interests, job){
  this.fullName = name;
  this.password = password;
  this.interests = interests;
  this.job = job;
};

function sayHello(){
   // uncomment the console.log to see the object that 'this' points to
  //  console.log(this); 
  return `Hi, my name is ${this.fullName} and I'm a ${this.job}. Let's be friends!`;
}

// Create one or multiple instances of the Friend prototype with the keyword 'new'
var john = new Friend('John Smith', 'badpassword', ['hiking', 'biking', 'skiing'], 'teacher'); 

console.log(john); 

// Assign the function to the greeting key on the john object
john.greeting = sayHello; 

// Call the the new method
console.log( john.greeting() ); 

// Remember, you can't call sayHello() as a function; it will return "Hi, my name is undefined and I'm a undefined. Let's be friends!"
// Because the function's context is global and the window object does NOT have the keys that belong to the Friend prototype
console.log( sayHello() ) ;

Conclusion

There’s one more way that ‘this’ can take a value in JavaScript. That’s with call, apply, and bind.

I intentionally didn’t go over call(), apply(), and bind() in this article (since this is a 101 level article); but just know that you can set the value of ‘this’ using these three methods.

That’s a post for another time.

The reason I’m leaving it out of this article is because it caused me more confusion around ‘this’ as a newbie until I understood objects more deeply.

I hope this article helped clear up some confusion around the keyword THIS in JavaScript.

It took me some time to understand the concept and I’m still learning.

The most helpful thing for me was to keep reading and keep practicing.

You’ll reach the point when everything will finally click!


每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,欢迎关注开源日报。交流QQ群:202790710;微博:https://weibo.com/openingsource;电报群 https://t.me/OpeningSourceOrg