topic
To read more high-quality articles in the check out my 16115a17e52eae GitHub 16115a17e52eb1
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
I have encountered this classic question several times during the interview in the past few days, and specially analyzed the answer from beginning to end. The classic feature of this question is that it comprehensively examines the comprehensive ability of the interviewer’s JavaScript, including the improvement of variable definitions, This pointer points, operator precedence, prototype, inheritance, global variable pollution, object attributes and prototype attribute precedence, etc. There are some related explanations on this question on the Internet. Of course, I think some of the explanations are not correct and not clear enough. The analysis will be repeated from beginning to end. Of course, we will put the final answer at the back and make this question a little bit more difficult. The improved version is also placed at the end, so that the interviewer can have a reference when making the question.
First question
Let’s look at what we did in the first half of this question. First, we define a function called Foo, then create a static property called getName for Foo to store an anonymous function, and then create a new function called getName for the prototype object of Foo. The anonymous function. After that, a function named getName was created through the function variable expression, and finally a function named getName was declared.
The first question Foo.getName naturally to access the static properties stored on the Foo function. The answer is naturally 2. There is no need to explain too much here. Generally speaking, the first question should be for students who have a little knowledge of JS basics. There is no problem, of course we can use the following code to review the basics, first deepen our understanding
function User(name) {
var name = name; //私有属性
this.name = name; //公有属性
function getName() { //私有方法
return name;
}
}
User.prototype.getName = function() { //公有方法
return this.name;
}
User.name = 'Wscats'; //静态属性
User.getName = function() { //静态方法
return this.name;
}
var Wscat = new User('Wscats'); //实例化
Pay attention to the following points:
- To call public methods and public properties, we must first instantiate the object, that is, use the new operator to materialize the object, then the method and properties of the object can be instantiated by the constructor, and public methods cannot call private methods and static methods
- Static methods and static properties are that we can call without instantiation
- The private methods and properties of the object are not accessible from the outside
Second question
The second question is to call the getName function directly. Since it is a direct call, it is to access the function called getName in the current scope above, so the focus here should be directly on 4 and 5, which has nothing to do with 1 2 3. Of course, I asked a few of my colleagues and most of them answered 5. There are actually two pitfalls here, one is the promotion of variable declarations, and the other is the difference between function expressions and function declarations.
Let's take a look at why, please refer to (1) About Javascript function declaration and function expression (2) About JavaScript variable promotion
In Javascript, there are two types of defined functions
Function declaration
// 函数声明
function wscat(type) {
return type === "wscat";
}
Function expression
// 函数表达式
var oaoafly = function(type) {
return type === "oaoafly";
}
First look at the classic problem below. In a program, define a function named getName with both a function declaration and a function expression.
getName() //oaoafly
var getName = function() {
console.log('wscat')
}
getName() //wscat
function getName() {
console.log('oaoafly')
}
getName() //wscat
The code above looks very similar, and it doesn't feel much different. But in fact, a "trap" in Javascript functions is reflected in the two types of Javascript function definitions.
- JavaScript interpreter has a mechanism for variable declarations to be promoted, which means that function declarations will be promoted to the front of the scope, even if they are written at the end when the code is written, they will still be promoted to the forefront.
- and functions created with function expressions are assigned at runtime, and
var getName //变量被提升,此时为undefined
getName() //oaoafly 函数被提升 这里受函数声明的影响,虽然函数声明在最后可以被提升到最前面了
var getName = function() {
console.log('wscat')
} //函数表达式此时才开始覆盖函数声明的定义
getName() //wscat
function getName() {
console.log('oaoafly')
}
getName() //wscat 这里就执行了函数表达式的值
So it can be broken down into these two simple questions to see the essence of the difference clearly
var getName;
console.log(getName) //undefined
getName() //Uncaught TypeError: getName is not a function
var getName = function() {
console.log('wscat')
}
var getName;
console.log(getName) //function getName() {console.log('oaoafly')}
getName() //oaoafly
function getName() {
console.log('oaoafly')
}
This difference may seem trivial, but in some cases it is indeed an imperceptible and "fatal" trap. The essential reason for this trap is reflected in the difference in function promotion and runtime (parse time/run time) of the two types.
Of course, we give a summary: Javascript in function declarations and function expressions is the distinction, function declaration in JS parsing a function improved, so in the same scope, regardless of where the function declaration defined , The function can be called. The function expression value in JS running determined, and after expression evaluation is completed, the function can be called.
So the answer to the second question is 4, the function declaration of 5 is covered by the function expression of 4.
Third question
Foo().getName();
executes the Foo function first, and then calls the getName property function of the return value object of the Foo function.
The first sentence of the Foo function, getName = function () { alert (1); };
is a function assignment statement. Note that it has no var declaration, so first look for the getName variable in the scope of the current Foo function, no. Then go to the upper level of the current function scope, that is, the outer scope to find whether there is a getName variable, and found it, that is, the alert(4) function in the second question, and assign the value of this variable to function(){alert(1)}
.
In fact, the getName function in the outer scope is modified here.
Note: If it is still not found here, the window object will be searched upwards. If there is no getName attribute in the window object, create a getName variable in the window object.
After that, the return value of the Foo function is this, and the this problem of JS has been introduced in many articles, so I won't talk about it here.
Simply put, the direction of this is determined by the calling method of the function in which it is located. In the direct call method here, this points to the window object.
So the Foo function returns the window object, which is equivalent to executing window.getName()
, and the getName in the window has been modified to alert(1), so it will eventually output 1
Two knowledge points are examined here, one is the variable scope problem, and the other is the problem that this points to
We can use the following code to review these two knowledge points
var name = "Wscats"; //全局变量
window.name = "Wscats"; //全局变量
function getName() {
name = "Oaoafly"; //去掉var变成了全局变量
var privateName = "Stacsw";
return function() {
console.log(this); //window
return privateName
}
}
var getPrivate = getName("Hello"); //当然传参是局部变量,但函数里面我没有接受这个参数
console.log(name) //Oaoafly
console.log(getPrivate()) //Stacsw
Because JS does not have a block-level scope, but a function can generate a scope. Different methods of defining values inside the function will directly or indirectly affect global or local variables. Private variables inside the function can be obtained with closures. The function is really true. It's the first citizen~
Regarding this, the point of this cannot be determined when the function is defined. Only when the function is executed can it be determined who this points to. In fact, the final point of this is the object that calls it.
So in the third question, the window is actually calling the Foo() function, so the point of this is window
window.Foo().getName();
//->window.getName();
Fourth question
Call the getName function directly, which is equivalent to window.getName()
, because this variable has been modified when the Foo function is executed, and the result is the same as the third question, which is 1, which means that after Foo is executed, the global getName function is rewritten once, so The result is the getName function rewritten by Foo()
Fifth question
The fifth question new Foo.getName();
here is the question of JS operator priority. I think this is the soul of this question, and it is also a more difficult question.
The following is the priority table of JS operators, sorted from highest to lowest. Refer to MDN operator precedence
priority | Operation type | Relevance | Operator |
---|---|---|---|
19 | Parentheses | n/a | ( … ) |
18 | Member access | From left to right | … . … |
Member access to be counted | From left to right | … [ … ] | |
new (with parameter list) | n/a new | … ( … ) | |
17 | Function call | From left to right | … ( … ) |
new (no parameter list) | Right to left | new … | |
16 | Post-increment (operator after) | n/a | … ++ |
Post-decrement (operator after) | n/a | … -- | |
15 | Logical negation | Right to left | ! … |
Bitwise not | Right to left | ~ … | |
Unary addition | Right to left | + … | |
Unary subtraction | Right to left | - … | |
Pre-increment | Right to left | ++ … | |
Pre-decrement | Right to left | -- … | |
typeof | Right to left | typeof … | |
void | Right to left | void … | |
delete | Right to left | delete … | |
14 | multiplication | From left to right | … * … |
division | From left to right | … / … | |
Modulo | From left to right | … % … | |
13 | addition | From left to right | … + … |
Subtraction | From left to right | … - … | |
12 | Bit shift left | From left to right | … << … |
Bit shift right | From left to right | … >> … | |
Unsigned right shift | From left to right | … >>> … | |
11 | Less than | From left to right | … < … |
Less than or equal to | From left to right | … <= … | |
more than the | From left to right | … > … | |
greater or equal to | From left to right | … >= … | |
in | From left to right | … in … | |
instanceof | From left to right | … instanceof … | |
10 | equal sign | From left to right | … == … |
Non-equal sign | From left to right | … != … | |
Full equal sign | From left to right | … === … | |
Non-equal sign | From left to right | … !== … | |
9 | Bitwise and | From left to right | … & … |
8 | Bitwise exclusive or | From left to right | … ^ … |
7 | Bitwise or | From left to right | … Bitwise or… |
6 | Logical and | From left to right | … && … |
5 | Logical OR | From left to right | … Logical or… |
4 | Conditional operator | Right to left | … ? … : … |
3 | Assignment | Right to left | … = … |
… += … | |||
… -= … | |||
… *= … | |||
… /= … | |||
… %= … | |||
… <<= … | |||
… >>= … | |||
… >>>= … | |||
… &= … | |||
… ^= … | |||
… Or =… | |||
2 | yield | Right to left | yield … |
yield* | Right to left | yield* … | |
1 | Spread operator | n/a | ... … |
0 | comma | From left to right | … , … |
This question first looks at the priority of the 18th and 17th. The priority of new appears. New (with parameter list) is higher than new (with no parameter list) than function calls, and the same level as member access
The priority of new Foo.getName();
Is equivalent to:
new (Foo.getName)();
- The priority of the point (18) is higher than the priority of the new non-parameter list (17)
- When the point is calculated, because there is a bracket
()
, it becomes new with a parameter list (18), so execute new directly. Of course, some friends may have questions about why they encounter () instead of function call and then new. That's because function call (17) has lower priority than new parameter list (18)
.Member access (18) -> new has a parameter list (18)
So here, the getName function is actually executed as a constructor, and then 2 pops up.
Sixth question
The only difference between this question and the previous question is that there is an extra parenthesis in Foo. There is a parenthesis and there is no parenthesis. We also noticed that the priority is different in the fifth question.
(new Foo()).getName()
So how is it judged here? First of all, new has the parameter list (18) and the priority of the point (18) are the same level. If the same level is in the order of execution from left to right, so first execute new with the parameter list (18) and then execute the priority of the point (18) ), and finally the function call (17)
new has a parameter list (18) ->. Member access (18) -> () function call (17)
There is also a little knowledge point here, Foo has a return value as a constructor, so here we need to explain the return value of the constructor in JS.
The return value of the constructor
In traditional languages, the constructor should not have a return value, and the return value of the actual execution is the instantiated object of this constructor.
In JS, the constructor can have a return value or not.
If there is no return value, the instantiated object is returned as in other languages.
function Foo(name) { this.name = name } console.log(new Foo('wscats'))
If there is a return value, check whether the return value is a reference type. If it is a non-reference type, such as a basic type (String, Number, Boolean, Null, Undefined), it is the same as no return value, and its instantiated object is actually returned.
function Foo(name) { this.name = name return 520 } console.log(new Foo('wscats'))
- If the return value is a reference type, the actual return value is this reference type.
function Foo(name) {
this.name = name
return {
age: 16
}
}
console.log(new Foo('wscats'))
In the original question, since this is returned, and this originally represents the current instantiated object in the constructor, and finally the Foo function returns the instantiated object.
Then call the getName function of the instantiated object, because no attributes are added to the instantiated object in the Foo constructor, look for the getName function in the prototype object of the current object.
Of course, here is another digression. If the constructor and the prototype chain have the same method, such as the following code, then the public method of the constructor will be used by default instead of the prototype chain. This knowledge point is not shown in the original question. I have added the improved version.
function Foo(name) {
this.name = name
this.getName = function() {
return this.name
}
}
Foo.prototype.name = 'Oaoafly';
Foo.prototype.getName = function() {
return 'Oaoafly'
}
console.log((new Foo('Wscats')).name) //Wscats
console.log((new Foo('Wscats')).getName()) //Wscats
Seventh question
new new Foo().getName();
also an issue of operator precedence. In fact, I already think that the answer is not so important to achieve this question. The key is to check whether the interviewer really knows what the interviewer is looking at us.
The final actual execution is:
new ((new Foo()).getName)();
new has a parameter list (18) -> new has a parameter list (18)
Initialize the instantiated object of Foo first, and then use the getName function on its prototype as the constructor to new again, so the final result is 3
Answer
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//答案:
Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName();//2
new Foo().getName();//3
new new Foo().getName();//3
Follow-up
Later, I increased the difficulty of this question a little bit (attached the answer), and added a public method getName in the Foo function. For the following question, if it is used in the interview question, the pass rate may be lower, because The difficulty is a little bit more difficult, and there are two more pits, but understanding the principle of this question is equivalent to understanding all the knowledge points above.
function Foo() {
this.getName = function() {
console.log(3);
return {
getName: getName //这个就是第六问中涉及的构造函数的返回值问题
}
}; //这个就是第六问中涉及到的,JS构造函数公有方法和原型链方法的优先级
getName = function() {
console.log(1);
};
return this
}
Foo.getName = function() {
console.log(2);
};
Foo.prototype.getName = function() {
console.log(6);
};
var getName = function() {
console.log(4);
};
function getName() {
console.log(5);
} //答案:
Foo.getName(); //2
getName(); //4
console.log(Foo())
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
//多了一问
new Foo().getName().getName(); //3 1
new new Foo().getName(); //3
Finally, I actually do not recommend using these questions as the only judgement for interviewers, but as a qualified front-end engineer, we should not ignore some of our most basic basic knowledge because of impetuousness. Of course, I also wish all interviewers find An ideal job, I wish all interviewers find the Maxima in their hearts~
comminicate
If the articles and notes can bring you a hint of help or inspiration, please don't be stingy with your likes and favorites. The articles are continuously updated synchronously. You can search for "Front-end Travel" on WeChat and follow the public to facilitate your future reading. Past articles are also included in 16115a17e53bc6 https ://github.com/Wscats/articles
Welcome your attention and communication, yours is definitely my biggest motivation to move forward😁
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。