preface
The last time, I have learned
[THE LAST TIME] has always been a series THAT I wanted to write, aiming to build on and revisit THE front end.
But also to their own shortcomings and technology sharing.
Welcome more comments, comments and jokes.
A series of articles were published on the public account “Full Stack Front-end Selection”. See Nealyang/personalBlog for a collection of my articles. All catalogues are tentative
To be fair, this article is a little out of line. To be exact, the content of this article is basically the basis of the foundation, but often this kind of basic class article is difficult to control in the long winded and detailed. In the article, I hope you can make more comments and corrections.
THE LAST TIME series
- 【THE LAST TIME】 Thoroughly understand JavaScript execution mechanics
This
For those of you who have used JavaScript libraries for development, this will be familiar. Although this is very, very common in development, it is not easy to really understand this. For experienced developers, it’s a good idea to stop and think about JavaScript’s scope and morphology
Misconception # 1: This refers to himself
function foo(num) {
console.log("foo:"+num);
this.count++;
}
foo.count = 0;
for(var i = 0; i<10; i++){ foo(i); }console.log(foo.count);
Copy the code
By running the code above we can see that foo is called ten times, but this.count doesn’t seem to be added to foo.count. That is, this.count in the function is not foo.count.
Misconception # 2: This refers to its scope
Another misconception about this is that it somehow refers to the scope of the function. In one sense it is correct, but in another sense it is a misconception.
To be clear, this does not in any way refer to the lexical scope of the function. The scope seems to be an object with all available identifiers as attributes. Internally, it is correct, but JavaScript code cannot access the scope “object” because it is an internal implementation of the engine
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); //undefined
Copy the code
This in the global environment
Since it is a global environment, we need to clarify the concept of a host environment. In short, a language needs an environment to run, and this environment is called the host environment. For JavaScript, the most common hosting environment is the Web browser.
As mentioned above, we also know that the environment is not unique, which means that JavaScript code can run not only in the browser, but also in other programs that provide a host environment. The other most common is Node, which also hosts its own JavaScript engine: V8.
- In the browser, at the global scale,
this
Is equivalent towindow
object - In a browser, use
var
Declaring a variable is equivalent to givingthis
orwindow
Add attributes - If you don’t use a variable when you declare it
var
orlet(ECMAScript 6)
You’re giving the big picturethis
Add or change attribute values - In the Node environment, if the
REPL
To execute the program, sothis
Is equal toglobal
- In the Node environment, if you are executing a JS script, then
this
Don’t point toglobal
butmodule.exports
for{}
- In the Node environment, globally, if you use
REPL
Executing a script file declaring a variable using var does not add the variable to this as it would in a browser - If you execute the code directly instead of executing the script file with the REPL, the results will be the same as in the browser
- in
node
In the environment, when running a script file with the REPL, if the variable is not used when declaring itvar
orlet
, this variable is automatically added toglobal
Object, but not automatically added tothis
Object. If the code is executed directly, it is added simultaneously toglobal
andthis
This piece of code is relatively simple, we do not need to speak code, instead use the figure to speak!
This in functions and methods
Many articles distinguish between functions and methods, but I think… There’s no need. We’ll just see who orders this girl
When a function is called, an active record is created, which is also the execution environment. This record contains information about where the function was called from (call-stack), how it was called, what arguments were passed, and so on. One of the attributes of this record is the this reference that will be used during the execution of the function.
Function this is variable, but the rule is constant.
You ask the function: “Oh, no, the function! Who ordered you? “
“It’s him!! “
So, this points to that guy! A little more academic, so! In general! This is not determined at compile time, but is bound to the context execution environment at run time. This has nothing to do with declarations!
function foo() {
console.log( this.a );
}
var a = 2;
foo(); / / 2
Copy the code
Remember the above, who ordered me!! Foo () => foo() = windwo.foo(), so this executes the window object, naturally printing out 2.
Note that binding global objects by default is illegal for strict mode, and this is set to undefined.
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42.foo: foo
};
var obj1 = {
a: 2.obj2: obj2
};
obj1.obj2.foo(); / / 42
Copy the code
Although this xx was ordered a lot… But, we’re only asking for his guy, which is Ojb2, so this.a prints 42.
Notice, my dot here! Not the point you think, but the runtime
Constructor This
Um… This is congliang
Again, this, we’re not going to look at where it’s defined, we’re going to look at runtime. The constructor starts with the keyword new!
Whoever gives me New, I’ll talk to whoever
In fact, the following things are done internally:
- A new object will be created
- The newly created object is connected to the prototype chain
- The newly created object is set to the this binding of the function call
- Unless the function returns another object of its own, the function called by new will automatically return a newly created object
foo = "bar";
function testThis(){
this.foo = 'foo';
}
console.log(this.foo);
new testThis();
console.log(this.foo);
console.log(new testThis().foo)// Try it yourself
Copy the code
This in call, apply, bind
Um… This is wrapped
In many books, call, apply, and bind are called strong bindings for this. To put it bluntly, whoever does the work, me and him. As for the difference and realization and principle of these three, let’s say below!
function dialogue () {
console.log (`I am The ${this.heroName}`);
}
const hero = {
heroName: 'Batman'}; dialogue.call(hero)//I am Batman
Copy the code
The above dialect.call (hero) is equivalent to dialect.apply (hero) ‘ ‘dialect.bind (hero)().
I’m explicitly specifying what this is!
This in the arrow function
The arrow function’s this is a little different from the JavaScript function. The arrow function permanently captures this value, preventing Apply or Call from changing it later.
let obj = {
name: "Nealyang".func: (a,b) = > {
console.log(this.name,a,b); }}; obj.func(1.2); / / 1. 2
let func = obj.func;
func(1.2); // 1 2
let func_ = func.bind(obj);
func_(1.2);/ / 1. 2
func(1.2);// 1 2
func.call(obj,1.2);/ / 1. 2
func.apply(obj,[1.2]);/ / 1. 2
Copy the code
The this value in the arrow function cannot be set explicitly. In addition, if you use methods like call, apply, or bind to pass values to this, the arrow function ignores them. The arrow function refers to the this value that the arrow function set when it was created.
Arrow functions cannot also be used as constructors. Therefore, we cannot attribute this within the arrow function either.
The class of this
There is still some debate about whether JavaScript is an object-oriented language. We’re not going to argue here. But as we all know, classes are a very important part of JavaScript applications.
Classes usually contain a constructor, which can point to any newly created object.
However, when used as a method, this can point to any other value if the method is called as a normal function. Like methods, classes can lose track of receivers.
class Hero {
constructor(heroName) {
this.heroName = heroName;
}
dialogue() {
console.log(`I am The ${this.heroName}`)}}const batman = new Hero("Batman");
batman.dialogue();
Copy the code
The this in the constructor points to the newly created class instance. When we call batman.dialogue(), dialogue() is called as a method, and batman is its receiver.
But if we store a reference to the Dialogue () method and call it later as a function, we lose the receiver for that method, where this points to undefined.
const say = batman.dialogue;
say();
Copy the code
The error occurs because JavaScript classes implicitly run in strict mode. We called the say() function without any automatic binding. To fix this, we need to manually bind the dialogue() function to batman using bind().
const say = batman.dialogue.bind(batman);
say();
Copy the code
The principle of this
Ahem, technical articles, let’s be serious
We all say that this refers to the environment in which the function is run. But why?
As we all know, an assignment to an object in JavaScript assigns an address to a variable. When the engine reads a variable, it essentially asks for an address and then reads the object from that address. This is also true if the properties in the object are also reference types (such as function)!
JavaScript, on the other hand, allows function bodies to reference other variables in the current environment that are provided by the runtime environment. Since functions can be executed in different environments, a mechanism is needed to provide a runtime environment for functions! And this mechanism, which is what we’re talking about in this. This is intended to be used inside functions to refer to the current runtime environment.
var f = function() { console.log(this.x); } var x = 1; var obj = { f: f, x: 2, }; // Execute f() alone // 1 // obj environment execute obj.f() // 2Copy the code
Obj.foo () finds foo through obj, so it is executed in the obj environment. Once var foo = obj.foo, the variable foo refers directly to the function itself, so foo() becomes executed in the global environment.
conclusion
- Whether the function is called in new, and if so, this binds to the newly created object
var bar = new Foo();
Copy the code
- Whether the function is called by call, apply, or some other hard call, and if so, this binds to the specified object
var bar = foo.call(obj);
Copy the code
- Whether the function is called in a context object, and if so, this binds to that context object
var bar = obj.foo();
Copy the code
- If not, use the default binding, and if in strict mode, bind to undefined. Note that this is a strict declaration inside the method. Otherwise, bind to the global object
var bar = foo();
Copy the code
A profound
var number = 2;
var obj = {
number: 4./* Anonymous function self-tuning */
fn1: (function() {
var number;
this.number *= 2; / / 4
number = number * 2; //NaN
number = 3;
return function() {
var num = this.number;
this.number *= 2; / / 6
console.log(num);
number *= 3; / / 9alert(number); }; }) (),db2: function() {
this.number *= 2; }};var fn1 = obj.fn1;
alert(number);
fn1();
obj.fn1();
alert(window.number);
alert(obj.number);
Copy the code
Leave your answers in the comments section
call & applay
Call, apply, and bind have been mentioned above, and apply is defined in MDN as follows:
The apply() method calls a function with a specified this value and arguments supplied as an array (or array-like object)
Grammar:
fun.apply(thisArg, [argsArray])
- ThisArg: This value specified when fun is run. Note that the specified this value is not necessarily the true this value when the function is executed. If the function is in non-strict mode, specifying null or undefined automatically points to the global object (window object in browsers) and the original value (number, string, This points to the auto-wrapped object of the original value.
- ArgsArray: An array or array-like object whose array elements are passed as separate arguments to fun. If the value of this parameter is null or undefined, no arguments need to be passed in. Array-like objects can be used starting with ECMAScript 5. See the bottom of this article for browser compatibility.
The concept apply is similar. The difference is that the second parameter type passed by apply and call is different.
The syntax for call is:
fun.call(thisArg[, arg1[, arg2[, ...]]])
Copy the code
Note that:
- The object that calls call must be a Function
- The first argument to call is an object. The caller of Function is going to point to this object. If not, the default is the global object Window.
- Starting with the second parameter, any parameter can be accepted. Each parameter is mapped to the Function parameter at the corresponding location. But if you pass all the parameters as an array, they are mapped as a whole to the first parameter corresponding to Function, after which the parameters are empty.
The syntax for apply is:
Function.apply(obj[,argArray])
Copy the code
Note that:
- Its caller must be a Function and take only two arguments
- The second argument, which must be an array or an array of classes, will be converted to an array of classes, passed into Function, and mapped to the corresponding parameters of Function. This is an important distinction between call and apply.
Mnemonic technique: apply, a, array, so the second argument needs to pass data.
Excuse me! What is a class array?
The core ideas
Borrow!
Yes, borrowing. For example! I don’t have a girlfriend. Uh, no, I don’t have a motorcycle 🏍, the weather was nice over the weekend and I wanted to go out and bend. But I don’t have any money! How to do, look for a friend to borrow ah ~ achieved a goal, still save expenses!
We can understand that an object does not want to use a method to achieve a function, but do not want to waste memory overhead, borrow another object with the method to borrow.
To put it bluntly, including BIND, the core idea is to borrow methods to save money.
Application scenarios
The code is pretty simple, so I won’t do it
- Convert an array of classes to an array
const arrayLike = {
0: 'qianlong'.1: 'ziqi'.2: 'qianduan'.length: 3
}
const arr = Array.prototype.slice.call(arrayLike);
Copy the code
- Find the maximum value in the array
var arr = [34.5.3.6.54.6.- 67..5.7.6.- 8 -.687];
Math.max.apply(Math, arr);
Math.max.call(Math.34.5.3.6.54.6.- 67..5.7.6.- 8 -.687);
Math.min.apply(Math, arr);
Math.min.call(Math.34.5.3.6.54.6.- 67..5.7.6.- 8 -.687);
Copy the code
- Variable type judgment
Object. The prototype. ToString fitting is used to judge types, especially for reference types.
function isArray(obj){
return Object.prototype.toString.call(obj) == '[object Array]';
}
isArray([]) // true
isArray('qianlong') // false
Copy the code
- inheritance
/ / parent class
function supFather(name) {
this.name = name;
this.colors = ['red'.'blue'.'green']; // Complex type
}
supFather.prototype.sayName = function (age) {
console.log(this.name, 'age');
};
/ / subclass
function sub(name, age) {
// Borrow methods from the parent class: modify its this pointer and assign methods and attributes from the parent constructor to the subclass
supFather.call(this, name);
this.age = age;
}
// Override prototype of the subclass and fix the constructor pointer
function inheritPrototype(sonFn, fatherFn) {
sonFn.prototype = Object.create(fatherFn.prototype); // Inherit the attributes and methods of the parent class
sonFn.prototype.constructor = sonFn; // Fix constructor to point to the inherited function
}
inheritPrototype(sub, supFather);
sub.prototype.sayAge = function () {
console.log(this.age, 'foo');
};
// instantiate a subclass. Attributes and methods can be found on the instance
const instance1 = new sub("OBKoro1".24);
const instance2 = new sub("Xiao Ming".18);
instance1.colors.push('black')
console.log(instance1) // {"name":"OBKoro1","colors":["red","blue","green","black"],"age":24}
console.log(instance2) / / {" name ":" Ming "and" colors ": [" red", "blue", "green"], "age" : 18}
Copy the code
Inheritance may also be followed by an article called THE LAST TIME. I don’t know if it’s necessary
Easy version inheritance
ar Person = function (name, age) {
this.name = name;
this.age = age;
};
var Girl = function (name) {
Person.call(this, name);
};
var Boy = function (name, age) {
Person.apply(this.arguments);
}
var g1 = new Girl ('qing');
var b1 = new Boy('qianlong'.100);
Copy the code
bind
Bind is the same as call/apply, but bind returns a new function! Not immediately executed! ** While call/apply changes this of the function and executes it immediately.
Application scenarios
- Cache parameters
The idea is to return a closure; bind, after all, returns a copy of a function
for (var i = 1; i <= 5; i++) {
// Cache parameters
setTimeout(function (i) {
console.log('bind', i) // Outputs: 1, 2, 3, 4, 5
}.bind(null, i), i * 1000);
}
Copy the code
The above code is also a classic interview question, the details will not be expanded.
- This loss problem
Bind (this); bind(this); bind(this); Of course, arrow functions are not required, as we discussed above.
Handwritten implementation
apply
The first one, let’s take it step by step
- Trigger from the definition, because it is the function caller. So you must add a method to function, and the first parameter is the future this context
Function.prototype.NealApply = function(context,args){}
Copy the code
- If context, this points to the window
Function.prototype.NealApply = function(context,args){
context = context || window;
args = args || [];
}
Copy the code
- Add a non-overwritable key to the context and bind this
Yes, we don’t have dark magic. Since we bind this, we still can’t escape the “this” mode we mentioned above
Function.prototype.NealApply = function(context,args){
context = context || window;
args = args || [];
// Add a unique attribute to the context to avoid overwriting the original attribute
const key = Symbol(a); context[key] =this;// this is the functioncontext[key](... args); }Copy the code
In fact, at this time we use it already has an effect.
- At this point we are done, and we need to return the results and clean up our garbage
Function.prototype.NealApply = function(context,args){
context = context || window;
args = args || [];
// Add a unique attribute to the context to avoid overwriting the original attribute
const key = Symbol(a); context[key] =this;// This is testFun
constresult = context[key](... args);// Take away the side effects
delete context[key];
return result;
}
var name = 'Neal'
function testFun(. args){
console.log(this.name,... args); }const testObj = {
name:'Nealyang'
}
testFun.NealApply(testObj,['Watch Together'.':'.'Full stack Front End Selection']);
Copy the code
The result is shown in the screenshot above.
- To optimize the
At first, I don’t say optimization because I want you to focus on the core, and then to trim the edges! Rome is not built in a day, see other people’s code many cattle criticism, in fact, is also a little bit of perfect out.
In fact, there is still a lot of optimization to be done, so we need to optimize the context:
// Determine the function context object correctly
if (context === null || context === undefined) {
// This with null and undefined will automatically point to the global object (window in browsers)
context = window
} else {
context = Object(context) // This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value
}
Copy the code
Other optimizations you can add a variety of user fault tolerance. For example, make a fault tolerance for the class array of the second argument
function isArrayLike(o) {
if (o && // o is not null, undefined, etc
typeof o === 'object' && O is the object
isFinite(o.length) && //. Length is a finite number
o.length >= 0 && // O.length is non-negative
o.length === Math.floor(o.length) && // O.length is an integer
o.length < 4294967296) // o.length < 2^32
return true;
else
return false;
}
Copy the code
Hold on! Really no more verbose, this article should not be this long
call
Beggar version implementation:
// Pass parameters from an array to one by one instead of... Arguments can also be used instead of extension operators
Function.prototype.NealCall = function (context, ... args) {
// You can also use es6 to set default parameters for parameters
context = context || window;
args = args ? args : [];
// Add a unique attribute to the context to avoid overwriting the original attribute
const key = Symbol(a); context[key] =this;
// Call the function with an implicit binding
constresult = context[key](... args);// Delete the added attribute
delete context[key];
// Returns the return value of the function call
return result;
}
Copy the code
bind
The implementation of bind is more difficult than the implementation of apply and call. Because you have to worry about copying the function. But it’s still relatively simple, and there are many versions on the Internet, so I won’t elaborate here. Specifically, we can discuss in the group ~
Function.prototype.myBind = function (objThis, ... params) {
const thisFn = this; // Store the source function with params(function parameters) above
// Pass the second parameter to the returned function secondParams
let fToBind = function (. secondParams) {
const isNew = this instanceof fToBind // Whether this is an instance of fToBind or whether the returned fToBind is called new
const context = isNew ? this : Object(objThis) // The new call is bound to this, otherwise it is bound to objThis passed in
returnthisFn.call(context, ... params, ... secondParams);// Call the source function with this and pass the argument, return the result of execution
};
if (thisFn.prototype) {
// Copy the source function's prototype to give fToBind some cases where the function has no prototype, such as the arrow function
fToBind.prototype = Object.create(thisFn.prototype);
}
return fToBind; // Return the copied function
};
Copy the code
Function.prototype.myBind = function (context, ... args) {
const fn = this
args = args ? args : []
return function newFn(. newFnArgs) {
if (this instanceof newFn) {
return newfn(... args, ... newFnArgs) }return fn.apply(context, [...args,...newFnArgs])
}
}
Copy the code
The last
Don’t forget the examination topic of this, classmate, it’s time to hand in your paper!
Refer to the link
- Understand JavaScript’s “this” With Clarity, and Master It
- JavaScript’s This principle
- The most comprehensive collection of this traps in JavaScript — none
- Js Basics – Interviewers want to know how well you understand call,apply,bind?
Study and communication
Pay attention to the public number: [full stack front selection] daily access to good articles recommended. You can also join groups and learn and communicate together