Interview tortured soul of JS, I ask you not scalp tingle?

JS data type of the question - concept papers

1.JS primitive data types are there? Reference data types are there?
In JS, there are seven kinds of the original values, respectively:

  • boolean
  • null
  • undefined
  • number
  • string
  • symbol
  • bigint
    reference data types:
    Object Object (containing ordinary objects -Object, an array of objects -Array, a regular target -RegExp, date objects -Date, math functions -Math)
    Function Function
    2. Run the following results say, explain why.
 function test(person) {
   person.age = 26
   person = {
     name: 'hzj',
     age: 18
   }
   return person
 }
 const p1 = {
   name: 'fyq',
   age: 19
 }
 const p2 = test(p1)
 console.log(p1) // -> ?
 console.log(p2) // -> ?

result:

 p1:{name: “fyq”, age: 26}
 p2:{name: “hzj”, age: 18}

Reason: when the function of transmitting the transmission parameter is the heap memory address value object, person Test argument function p1 is the memory address of the object by calling person.age = 26 actually changes the value of p1, but then person becomes another piece of memory address space, and this was in addition to a memory address space of the return in the end, assigned to the p2.
3.null is the object? why?
Conclusion: null is not an object.
Explanation: While typeof null will output object, but this is only the existence of a long JS Bug. JS used in the original version of the system is 32-bit, to consider the type of information to use low performance storage variable, however, the object 000 is representative of the beginning of an all-zero or null, the error is determined it to object.
4.'1'.toString () Why can call?
In fact, do so few things in the process of running this statement:

 var s = new String('1');
 s.toString();
 s = null; 

Step 1: Create String class instance.
Step Two: Calling an instance method.
The third step: executing the method immediately destroy this instance.
The whole process embodies the basic nature of the type of packaging, without substantially precisely the type of packaging belonging to the basic data types, including Boolean, Number, and String.

Reference: "JavaScript Advanced Programming (third edition)" P118

5.0.1 Why is not equal to 0.3 + 0.2?
0.1 and 0.2 will be an infinite loop when converted to binary, since the number of bits behind the standard limit excess bits are truncated, this time there have been a loss of precision, the floating-point decimal sum due to the limitations of the truncated binary numbers to decimal becomes .30000000000000004.

The second: Ask JS data types - The detection article

1. typeof can determine whether the correct type?
For primitive types, in addition to null can call typeof display the correct type.

 typeof 1 // 'number'
 typeof '1' // 'string'
 typeof undefined // 'undefined'
 typeof true // 'boolean'
 typeof Symbol() // 'symbol' 

However, a reference to a data type, in addition to the function will display "object".

 typeof [] // 'object'
 typeof {} // 'object'
 typeof console.log // 'function' 

So using typeof target data type is not appropriate, the use of instanceof will be better, the principle of instanceof is a query-based prototype chain, as long as in the prototype chain, the judge is always true

 const Person = function() {}
 const p1 = new Person()
 p1 instanceof Person // true

 var str1 = 'hello world'
 str1 instanceof String // false

 var str2 = new String('hello world')
 str2 instanceof String // true

2. instanceof to determine whether the basic data types?
can. In this way such as the following:

 class PrimitiveNumber {
   static [Symbol.hasInstance](x) {
     return typeof x === 'number'
   }
 }
 console.log(111 instanceof PrimitiveNumber) // true 

If you do not know the Symbol, you can see explanation on hasInstance of MDN.
It is actually a way instanceof custom behavior, where the original instanceof redefined method, typeof replaced, can be determined basic data types.

3. Can be implemented manually look instanceof function ?
Core: Look up the prototype chain.

 function myInstanceof(left, right) {
     //基本数据类型直接返回false
 if(typeof left !== 'object' || left === null) return false;
     //getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
 let proto = Object.getPrototypeOf(left);
     while(true) {
         //查找到尽头,还没找到
 if(proto == null) return false;
         //找到相同的原型对象
 if(proto == right.prototype) return true;
         proto = Object.getPrototypeof(proto);
     }
 }  

test:

 console.log(myInstanceof("111", String)); //false
 console.log(myInstanceof(new String("111"), String));//true 

4. Object.is and the difference between ===?
Object is equal on the basis of strict repair mistakes in some special cases, specifically, +0 and -0, NaN and NaN. Source as follows:

 function is(x, y) {
   if (x === y) {
     //运行到1/x === 1/y的时候x和y都为0,但是1/+0 = +Infinity, 1/-0 = -Infinity, 是不一样的
 return x !== 0 || y !== 0 || 1 / x === 1 / y;
   } else {
     //NaN===NaN是false,这是不对的,我们在这里做一个拦截,x !== x,那么一定是 NaN, y 同理
 //两个都是NaN的时候返回true
 return x !== x && y !== y;
   }    

Part III: Q JS data type - The conversion articles

1. [] ==! [] What is the result? why?
Analysis:
==, the left and right sides will be converted to digital and then compared.
[] 0 is converted to digital.
! [] Is first converted to a Boolean value, since [] as a reference is converted to a Boolean type is true,
so! [] Is false, the further converted into digital, becomes zero.
0 == 0, the result is true

2. JS in the cast are there?

JS, a type conversion only three:

  • Converted into digital
  • Converted to a boolean value
  • Into a string

Conversion rules are as follows:

Note that "turn Boolean string" refers to the results of this line is an example of true to String
Interview tortured soul of JS, I ask you not scalp tingle?

And 3. == === What is the difference?

=== known exactly equal, means: not only the value to be equal to the left and right sides, also equal type, such as '1' results === 1 is false, because one side is the string, the other side is number.

=== == not as stringent, for the general case, as long as the values ​​are equal, it returns true, but it also involves some type == conversion, its conversion rule is as follows:

  • Type on both sides are the same, the same values ​​would be relatively magnitude, e.g. 1 == 2, return false
  • Judge whether it is null and undefined, it is, then it returns true
  • Determining whether the type is String and Number, are then converted into String type Number, then compared
  • Wherein one determines whether a Boolean, Boolean conversion is then put into Number, then compared
  • If one of them is Object, and the other is a String, Number or Symbol, Object will be converted to a string, and then compares
    console.log({a: 1} == true);//false
    console.log({a: 1} == "[object Object]");//true 

4. Object turn the original type is based on what processes are running?

Object transfer primitive type, will call the built [ToPrimitive] function, the function for which the following logic:

  1. If Symbol.toPrimitive () method, priority call back
  2. Call valueOf (), if converted to a primitive type, returns
  3. Call toString (), if converted to a primitive type, returns
  4. If you do not return to the original type, will complain
    var obj = {
    value: 3,
    valueOf() {
     return 4;
    },
    toString() {
     return '5'
    },
    [Symbol.toPrimitive]() {
     return 6
    }
    }
    console.log(obj + 1); // 输出7

5. How to make if (a == 1 && a == 2) conditions are met?
In fact, the application of a problem.

 var a = {
   value: 0,
   valueOf: function() {
     this.value++;
     return this.value;
   }
 };
 console.log(a == 1 && a == 2);//true 

Part IV: Tell us about your understanding of closures

What is closure?

The Red Book (P178) is defined for closure: the closure means is a function of another function has access to scope variables.

MDN definition of closure are: Closures are those functions can be accessed free variables. (Wherein the free variables, the means used in the function, but the function parameter arguments neither local variables nor variable function, in fact, is another variable function scopes.)

The reason closures produced?
We must first understand the scope chain concept is actually very simple, there are only two scopes ---- global scope and function scope in ES5, when accessing a variable, the interpreter will first find the current scope identifier, if not found, go to the parent scope to find, until you find the identifier of the variable or not in the parent scope, this is the scope chain, it is worth noting that each copy would Functions superior scope, form a scope chain. such as:

 var a = 1;
 function f1() {
   var a = 2
   function f2() {
     var a = 3;
     console.log(a);//3
   }
 } 

In this code, f1 scope points have global scope (window) and itself, and the point f2 scope global scope (window), f1 and itself. And scope is looking up from the bottom until you find the global scope window, if not globally, then it will error. It's that simple thing!
The nature of the closure is produced, the presence of the parent scope references to the current environment. Or to cite the example above:

 function f1() {
   var a = 2
   function f2() {
     console.log(a);//2
   }
   return f2;
 }
 var x = f1();
 x(); 

Where the variable x will get the role of the parent domain, output 2. Because in the current environment, contains a reference to f2, f2 just cited window, f1 and f2 scope. So f2 has access to variable scope f1.
It is not only a function that returns be considered is the creation of closures it?
Back to the nature of closures, we need to make a reference to the parent scope can exist, so we can also do the following:

 var f3;
 function f1() {
   var a = 2
   f3 = function() {
     console.log(a);
   }
 }
 f1();
 f3(); 

Let f1 execution, to f3 after the assignment, to say now f3 have a window, f1 and f3 itself access to these scopes, or look for a bottom-up, recently found a in f1, the output 2.
Here there is outside variable f3 references the parent scope, thus producing a closure, a form change, no change in nature.
What form of closure there?
After understand nature, we take a look at the real scene, exactly what areas to reflect the presence of closures?
Returns a function. It has just been exemplified.

As a function of transmission parameters

 var a = 1;
 function foo(){ 
  var a = 2;
   function baz(){
     console.log(a);
   }
   bar(baz);
 }
 function bar(fn){
   // 这就是闭包
   fn();
 }
 // 输出2,而不是1
 foo(); 

In the timer event listener, Ajax request, cross-window communication, Web Workers, or any of asynchronous, as long as the use of the callback function is actually in use closures.

The following closures are merely window and save the current scope.

 // 定时器
 setTimeout(function timeHandler(){
   console.log('111');
 },100)

  // 事件监听
 $('#app').click(function(){
   console.log('DOM Listener');
 })

IIFE (immediately execute the function expressions) creation closures, save the current scope and global scope window function, it can be a global variable.

 var a = 2;
 (function IIFE(){
   // 输出2
 console.log(a);
 })(); 

How to solve the problem the following cycle output?

 for(var i = 1; i <= 5; i ++){
   setTimeout(function timer(){
     console.log(i)
   }, 0)
 }  

Why all the output 6? How to improve, it outputs 1,2,3,4,5? (Method better)
because setTimeout macro task, because the JS in single-threaded eventLoop mechanism, before going to execute the macro task in the main thread synchronization tasks executed, so after the end of the cycle setTimeout callback was followed by the implementation, but the output i when the current scope is not, I find an up and found i, this time cycle has ended, i became 6. So will all the output 6.
Solution:
1, using IIFE (immediately execute a function expression) for each time cycle, the variable i at this time is transmitted to the timer

 for(var i = 1;i <= 5;i++){
   (function(j){
     setTimeout(function timer(){
       console.log(j)
     }, 0)
   })(i)
 } 

2, the third parameter passed to the timer as the first timer function argument function

 for(var i=1;i<=5;i++){
   setTimeout(function timer(j){
     console.log(j)
   }, 0, i)
 }

3、使用ES6中的let

 for(let i = 1; i <= 5; i++){
   setTimeout(function timer(){
     console.log(i)
   },0)
 }  

JS let make a revolutionary change, so there JS scope becomes a function block-level scope, let gone After scope chain. The scope of the code block level units in the above code example:

 // i = 1
 {
   setTimeout(function timer(){
     console.log(1)
   },0)
 }
 // i = 2
 {
   setTimeout(function timer(){
     console.log(2)
   },0)
 }
 // i = 3
 ... 

It is possible to output the correct result.

Part V: Tell us about your understanding of the prototype chain

1. What is the relationship of objects and prototype constructors?
In JavaScript, whenever a function defined data type (normal function, class) that they will naturally built a prototype property that points to the function prototype object.
When the function through the new call, this function has become a constructor, returns a new instance of an object, the object has an instance of proto attribute points to the constructor's prototype object.

![](https://s1.51cto.com/images/blog/201910/22/e8aaa00fe932ce3f40821a825274318e.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) 

2. Can you describe the prototype chain?
JavaScript object points by prototype parent class object, up until the point Object object, thus forming a chain of prototype points, i.e., the prototype chain.
Interview tortured soul of JS, I ask you not scalp tingle?

HasOwnProperty object () to check whether the object itself contains the attributes
used in checking whether the object contains a property, but if the object does not have a prototype chain, will return true

Part VI: JS how to inherit?

The first: With the call

 function Parent1(){
     this.name = 'parent1';
   }
   function Child1(){
     Parent1.call(this);
     this.type = 'child1'
   }
   console.log(new Child1); 

This time to write a subclass even though it can get a property value of the parent class, but the problem is the parent class prototype object once there is a method that subclasses can not be inherited. Then leads to the following method.

 function Parent2() {
     this.name = 'parent2';
     this.play = [1, 2, 3]
   }
   function Child2() {
     this.type = 'child2';
   }
   Child2.prototype = new Parent2();

   console.log(new Child2());

Seemed to have no problem, the parent class can access methods and properties, but in fact there is a potential shortage. for example:

 var s1 = new Child2();
 var s2 = new Child2();
 s1.play.push(4);
 console.log(s1.play, s2.play);   

You can see the console:
Interview tortured soul of JS, I ask you not scalp tingle?

Obviously I only changed the play attribute s1 and s2 followed Why change it? Very simple, because both use the same instance of a prototype object.
Why then is there a better way?
Third: The combination of the first two

 function Parent3 () {
     this.name = 'parent3';
     this.play = [1, 2, 3];
   }
   function Child3() {
     Parent3.call(this);
     this.type = 'child3';
   }
   Child3.prototype = new Parent3();
   var s3 = new Child3();
   var s4 = new Child3();
   s3.play.push(4);
   console.log(s3.play, s4.play);

You can see the console:
Interview tortured soul of JS, I ask you not scalp tingle?

之前的问题都得以解决。但是这里又徒增了一个新问题,那就是Parent3的构造函数会多执行了一次(Child3.prototype = new Parent3();)。这是我们不愿看到的。那么如何解决这个问题?
第四种: 组合继承的优化1

   function Parent4 () {
     this.name = 'parent4';
     this.play = [1, 2, 3];
   }
   function Child4() {
     Parent4.call(this);
     this.type = 'child4';
   }
   Child4.prototype = Parent4.prototype; 

这里让将父类原型对象直接给到子类,父类构造函数只执行一次,而且父类属性和方法均能访问,但是我们来测试一下:

 var s3 = new Child4();
 var s4 = new Child4();
 console.log(s3) 

Interview tortured soul of JS, I ask you not scalp tingle?

子类实例的构造函数是Parent4,显然这是不对的,应该是Child4。

第五种(最推荐使用): 组合继承的优化1

 function Parent5 () {
     this.name = 'parent5';
     this.play = [1, 2, 3];
   }
   function Child5() {
     Parent5.call(this);
     this.type = 'child5';
   }
   Child5.prototype = Object.create(Parent5.prototype);
   Child5.prototype.constructor = Child5; 

这是最推荐的一种方式,接近完美的继承,它的名字也叫做寄生组合继承。

ES6的extends被编译后的JavaScript代码
ES6的代码最后都是要在浏览器上能够跑起来的,这中间就利用了babel这个编译工具,将ES6的代码编译成ES5让一些不支持新语法的浏览器也能运行。
那最后编译成了什么样子呢?

 function _possibleConstructorReturn (self, call) {
        // ...
 return call && (typeof call === 'object' || typeof call === 'function') ? call : self;
  }
  function _inherits (subClass, superClass) {
      // ...
 //看到没有
        subClass.prototype = Object.create(superClass && superClass.prototype, {
                constructor: {
                        value: subClass,
                        enumerable: false,
                        writable: true,
                        configurable: true
                }
        });
        if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  }
   var Parent = function Parent () {
        // 验证是否是 Parent 构造出来的 this
        _classCallCheck(this, Parent);
 };
  var Child = (function (_Parent) {
        _inherits(Child, _Parent);
        function Child () {
                _classCallCheck(this, Child);
                return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments));
        }

return Child;
 }(Parent)); 

核心是_inherits函数,可以看到它采用的依然也是第五种方式————寄生组合继承方式,同时证明了这种方式的成功。不过这里加了一个Object.setPrototypeOf(subClass, superClass),这是用来干啥的呢?
答案是用来继承父类的静态方法。这也是原来的继承方式疏忽掉的地方。

追问: 面向对象的设计一定是好的设计吗?

不一定。从继承的角度说,这一设计是存在巨大隐患的。
从设计思想上谈谈继承本身的问题
假如现在有不同品牌的车,每辆车都有drive、music、addOil这三个方法。

 class Car{
   constructor(id)
 {
     this.id = id;
   }
   drive(){
     console.log("wuwuwu!");
   }
   music(){
     console.log("lalala!")
   }
   addOil(){
     console.log("哦哟!")
   }
 }
 class otherCar extends Car{} 

We can now realize the function of the car, and in this way to expand a different car.
But the question is, is the car of new energy vehicles, but it does not require addOil (refueling).
If you let the class of new energy vehicles inherit Car, then, it is also problematic, commonly known as "gorillas and bananas" issue. Gorilla hands are bananas, but obviously now I just need to bananas, but got a gorilla. That is fueling this method, I am now not needed, but because of inheritance, but also to the subclass of.

Inherited biggest question is: Can not decide which property is inherited, all the properties had inherited.

Of course, you might say, you can then create a parent class ah, to refuel method to get rid of, but it is also a problem, one parent is unable to describe the details of all subclasses of the situation, in order to go to different subclasses characteristics add different parent class, the code is bound to be a lot of duplication, on the other hand are subject to change once the subclass, the parent should be updated accordingly, the coupling of the code is too high, poor maintenance.
How then to solve the many problems it inherited?
With the combination, which is the trend of today's programming syntax, such as golang is completely oriented design in combination.
As the name suggests, it is to design for a combination of a series of parts, and these parts will be assembled to form different class or instance.

 function drive(){
   console.log("wuwuwu!");
 }
 function music(){
   console.log("lalala!")
 }
 function addOil(){
   console.log("哦哟!")
 }
 let car = compose(drive, music, addOil);
 let newEnergyCar = compose(drive, music);

Code is clean and reuse is also very good. This is a combination of design-oriented way.

Guess you like

Origin blog.51cto.com/14534823/2444558