1, typeof judgment
Does Typeof correctly determine the type? How does instanceof correctly identify objects
- For primitive types, the correct type can be displayed except for null
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol(a)// 'symbol'
Copy the code
Typeof displays objects except functions, so typeof does not accurately determine what type a variable is
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
Copy the code
If we want to determine the correct type of an object, we can consider using Instanceof because the internal mechanism is to determine through the prototype chain
const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true
var str = 'hello world'
str instanceof String // false
var str1 = new String('hello world')
str1 instanceof String // true
Copy the code
For primitive types, you can’t use instanceof to determine the type directly
2. Type conversion
There are only three types of cast in JS. They are:
- Convert to a Boolean value
- Convert to number
- Convert to string
Turns a Boolean
In a condition, all values except undefined, null, false, NaN, “, 0, -0 are converted to true, including all objects
Object to primitive type
For the built-in ToPrimitive function that is called when an object converts its type, the algorithmic logic is generally as follows
- If it is already a primitive type, there is no need to cast
- The call x.valueof () returns the converted value if converted to the underlying type
- Call x.tostring () and return the converted value if converted to the base type
- If neither returns the original type, an error will be reported
Of course, you can override Symbol. ToPrimitive, which has the highest priority to call when converting to the original type.
var a = {
valueOf() {
return 0;
},
toString() {
return '1'; },Symbol.toPrimitive]() {
return 2; }};console.log(1 + a); / / 3
var b = {
valueOf() {
return 11;
},
toString() {
return 10; }};console.log(1 + b); / / 12
var c = {
toString() {
return 5; }};console.log(1 + c); / / 6
Copy the code
Four operators
It has the following characteristics:
- If one side of the operation is a string, then the other side will be converted to a string
- If a side is not a string or a number, it is converted to either
1 + '1' / / '11'
true + true / / 2
4 + [1.2.3] 41, 2, 3 "/ /"
Copy the code
- For the first line of code, it fires feature one, so it converts the number 1 to a string, resulting in ’11’
- For the second line of code, feature two fires, so convert true to the number 1
- In the case of the third line of code, characteristic two is triggered, so the array is toString into strings 1,2,3, resulting in 41,2,3
The other thing to notice about addition is this expression ‘a’ + + ‘b’.
'a' + + 'b' // -> "aNaN"
Copy the code
- Because +’ b’ equals NaN, the result is “aNaN”, and you may also see the form +’1′ in some code to quickly get the number type.
- So for operators other than addition, as long as one side is a number, then the other side is converted to a number
4 * '3' / / 12
4 * [] / / 0
4 * [1.2] // NaN
Copy the code
Comparison operator
- If it is an object, the object is converted through toPrimitive
- If it is a string, the comparison is made using the Unicode character index
let a = {
valueOf() {
return 0;
},
toString() {
return '1'; }}; a > -1; // true
Copy the code
In the above code, because A is an object, the value is converted to the original type by valueOf and then compared.
3, This
Let’s start with a few function call scenarios
function foo() {
console.log(this.a);
}
var a = 1;
foo();
const obj = {
a: 2.foo: foo,
};
obj.foo();
const c = new foo();
Copy the code
- For calling foo directly, no matter where foo is placed, this must be window, right
- For obj.foo(), we just need to remember that whoever called the function is this, so this in the foo function is an obj object in this scenario
- In the case of new, this is always bound to C and will not be changed in any way
So with that said, in a lot of code this should be fine, so let’s look at this in the arrow function, okay
function a() {
return () = > {
return () = > {
console.log(this);
};
};
}
console.log(a()()());
Copy the code
- First of all, there is no “this” in the arrow function. The “this” in the arrow function depends only on the “this” in the first normal function that wraps the arrow function. In this case, because the first normal function wrapping the arrow function is a, this is window. In addition, using a function such as bind for the arrow function is invalid.
- The last case is a context-changing API like bind. For these functions, this depends on the first argument. If the first argument is null, it is window.
- So speaking of bind, have you thought about, if you bind a function multiple times, what’s the context?
let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() / / = >?
Copy the code
If you think the output is a, you’re wrong. We can actually convert this code to another form
/ / fn. The bind (). The bind is equal to (a)
let fn2 = function fn1() {
return function () {
return fn.apply();
}.apply(a);
};
fn2();
Copy the code
As you can see from the code above, no matter how many times we bind the function, this in fn is always determined by the first bind, so the result is always window
let a = { name: 'poetries' };
function foo() {
console.log(this.name);
}
foo.bind(a)(); // => 'poetries'
Copy the code
This is the rule for this, but it is possible for more than one rule to occur at the same time. In this case, the different rules will determine where this ends according to the highest priority.
First, new takes precedence, then bind, then obj.foo(), and finally foo. Once this is bound to the arrow function, it is not changed in any way.
What’s the difference between == and ===
In the case of ==, a cast is performed if the two types of the comparison are different
If we need to compare whether x and y are the same, the following judgment process occurs
- First, it determines whether the two types are the same. The same is the ratio size
- If the type is not the same, then the cast will be performed
- It will first determine whether null and undefined are being compared, and if so, it will return true
- Determine if the two types are string and number, and if so, convert the string to number
1= ='1' -> 1= =1
Copy the code
- Determine whether one of the parties is Boolean, and if it is, convert Boolean to number
'1'= =true -> '1'= =1 -> 1= =1
Copy the code
- If one of them is an object and the other is a string, number, or symbol, then convert the object to its original type
'1'= = {name: 'yck'} - >'1'= ='[object Object]'
Copy the code
It’s much simpler for ===, which is to determine whether the two types and values are the same
5, closures
The definition of A closure is simple: function A has A function B inside it, and function B can access variables in function A, so function B is A closure
function A() {
let a = 1;
window.B = function () {
console.log(a);
};
}
A();
B(); / / 1
Copy the code
Closures exist to give you indirect access to variables inside a function
Use closures in a loop to solve the problem of var defining functions
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
Copy the code
First of all, since setTimeout is an asynchronous function, it will execute the entire loop first, and then I will be 6, so it will print a bunch of 6’s
There are three solutions
- The first is using closures
for (var i = 1; i <= 5; i++) {
(function (j) {
setTimeout(function timer() {
console.log(j);
}, j * 1000);
})(i);
}
Copy the code
In the above code, we first use the immediate execution function to pass I into the function. At this time, the value is fixed on the parameter j and will not change. The next time the timer closure is executed, we can use the variable j of the external function to achieve this goal
- The second is to use the third argument of setTimeout, which is passed in as an argument to the timer function
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j);
},
i * 1000,
i
);
}
Copy the code
- The third way is to use let definition I to solve the problem, and this is the most recommended way
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
Copy the code
6, deep copy
Shallow copy
The first way to solve this problem is with object.assign, which many people think is for deep copies. No, object. assign will only copy all property values to the new Object. If the property value is an Object, it will copy the address, so it is not a deep copy
let a = {
age: 1};let b = Object.assign({}, a);
a.age = 2;
console.log(b.age); / / 1
Copy the code
We can also use the expansion operator… To implement a shallow copy
let a = {
age: 1};letb = { ... a }; a.age =2;
console.log(b.age); / / 1
Copy the code
In general, shallow copies will solve most problems, but deep copies may be needed in the following situations
let a = {
age: 1.jobs: {
first: 'FE',}};letb = { ... a }; a.jobs.first ='native';
console.log(b.jobs.first); // native
Copy the code
The shallow copy only solves the first layer of the problem, and if the next value has an object in it, then it’s back to the beginning, both of which have the same address. To solve this problem, we need to use deep copy.
Deep copy
This problem can usually be solved with json.parse (json.stringify (object)).
let a = {
age: 1.jobs: {
first: 'FE',}};let b = JSON.parse(JSON.stringify(a));
a.jobs.first = 'native';
console.log(b.jobs.first); // FE
Copy the code
But there are limitations to this approach:
- Ignores undefined
- Ignore the symbol
- Cannot serialize function
- Objects with circular references cannot be resolved
7, the prototype
A prototype chain is a chain of objects that are __proto__ together. Why does Obj have access to valueOf, because obj found valueOf through the prototype chain
Object
Is the father of all objects, all objects can pass__proto__
To find itFunction
It’s the father of all functions, and all functions can pass__proto__
To find it- Function of the
prototype
It’s an object - The object’s
__proto__
Property points to the stereotype,__proto__
Connecting objects and stereotypes forms a prototype chain
Var, let, and const differences
What is promotion? What is a temporary dead zone? Var, let, and const?
- Function promotion takes precedence over variable promotion. Function promotion moves the entire function to the top of the scope. Variable promotion only moves the declaration to the top of the scope
var
There are enhancements that we can use before the declaration.let const
Cannot be used before declaration due to temporary dead zonevar
Declaring a variable in the global scope causes it to be mounted on window. The other two do notlet
和const
Basically the same effect, but the latter declaration variable cannot be assigned again