每天推薦一個 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
推薦理由:介紹了 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:
- When 「this」 points to the Window object (Global)
- 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) - Function contexts
- 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