2021 JavaScript interview summary, error checking and review

Introduction: I have been reviewing interview questions recently, understanding the scope and frequency of the questions, reviewing some error-prone points, and sharing them at the same time. I hope to help you a little bit!

1、点(.) VS  = operator precedence

    let a = {n : 1};
    let b = a;
    a.x = a = {n: 2};    
    console.log(a.x);
    console.log(b.x);

What is the output?

undefined
{n: 2}

The priority of dot (.) and = is involved here, because dot (.) has a higher priority than =, this code can also be written like this:

    let a = {n : 1};
    let b = a;
    //a.x = a = {n: 2};    
    a.x ={n: 2}
    a = {n: 2};
    console.log(a.x);
    console.log(b.x);

Summary: Because of priority issues, ax needs to be assigned first, and then a is assigned. In daily development, this type of continuity is also very common. Generally, a=b=c=0 is written like this, of course. There will be no ambiguities and faults in writing, but like the case in the above example, there will be unexpected effects (it is estimated that this effect is not what we want), and the code should be as simple and clear as possible, so use and wait Be careful when you use it, it’s easy to use, and it’s not abused.

 

2. Scope


    var a = 0,b = 0;
    function A(a) {
        A = function (b) {
            console.log(a + b++)
        }
        console.log(a++)
    }
    A(1)
    A(2)

Parsing

var a = 0,b = 0;
    function A(a) {
        //A(1)调用运行情况:
        /*
            1.A函数被重置为:
                function (b) {
                    console.log(a + b++)
                }
            2.执行打印console.log(a++),因为此时的a是形参,形参的值是传入的1,所以打印1,而且形参a执行了++,此时值为2
        */
        A = function (b) {
            //A(2)调用情况
            /*
                这个a是采用的闭包,所以是2,b是形参也是2,所以打印的是4
            */
            console.log(a + b++)
        }
        console.log(a++)
    }
    A(1)//A(1)调用
    A(2)//A(2)调用

 

3. Variable promotion

Topic 1:

var a = 10;
(function a(){
    console.log(a)
    a = 20;
    console.log(window.a)
    var a = 30
    console.log(a)
})()

Equivalent to

var a = 10;
(function a(){
	//内部作用域
	var a;//var a = 30 中的声明被提升
    console.log(a)//undefined
    a = 20;//内部作用域的a赋值20
    console.log(window.a)//window中的a依然是10 
    a = 30//内部作用域的a赋值30
    console.log(a)//打印内部作用域中的a 30
})()

Topic 2:

var name = '张三';
function fn() {
    if (typeof name === 'undefined') {
        var name = '李四';
        console.log('辛苦了: ' + name);
    } else {
        console.log('吃饭没: ' + name);
    }
}
fn();

Equivalent to

var name = '张三';
function fn() {
	var name ;// var name = '李四'; 中的声明被提前到函数作用域里面
    if (typeof name === 'undefined') {//此时成立
        name = '李四';
        console.log('辛苦了: ' + name);
    } else {
        console.log('吃饭没: ' + name);
    }
}
fn();

Question 3:

var a=10,b=20,c=30;
function fn(a){
	a=1;
	var b=2;
	c=3
}
fn(100)
console.log(a,b,c);

Equivalent to

var a=10,b=20,c=30;
function fn(a){
	//函数调用,形成局部作用域,形参a在内部局部作用域 a=100
	var b;//b在内部变量提升
	a=1;//内部局部作用域 a设置为1
	b=2;//内部局部作用域 b设置为2
	c=3;//全局作用与c被设置值3
}
fn(100)
//可以看到只有c被局部作用域修改了
console.log(a,b,c);//10 20 3

Question 4:

if(!("a" in window)){
	var a = 10
}
console.log(a);

Equivalent to

var a;//变量提升了
if(!("a" in window)){//"a" in window 成立了,所以这个条件不执行
	a = 10
}
console.log(a);//'undefined

Question 5:

var fn = 'hello';
(function(fn){
	console.log(fn);
	var fn=fn||'world';
	console.log(fn)
})(fn)
console.log(fn);

Equivalent to

var fn = 'hello';
(function(fn){
	//内部作用域
	//形参先被定义 所以fn=hello
	//var fn;//var fn=fn||'world';变量提升到这里,但是会被忽略,因为形参的fn先被定义
	console.log(fn);//打印hello
	fn=fn||'world';//这句fn还是等于hello
	console.log(fn)//打印hello
})(fn)//hello作为实参传入
console.log(fn);//打印hello
//最终结果是:hello hello hello

Question 6:

var n=10;
function fn(n){
     console.log(n);
     var n= 20;
     console.log(n);
     function n(){
     	
     }
     console.log(n);
}
 fn(n);
 console.log(n);

Equivalent to

var n=10;
function fn(n){
	//局部作用域
	//n根据形参首先定义 n=10
	//函数会提升,覆盖了n,此时n为函数
	 function n(){
     	
     }
     //var n;//var n =20中的var n 被提升到此处,但是会被忽略
     console.log(n);//打印函数
      n= 20;//n被赋值为20
     console.log(n);//打印20
    	//函数已经提升
     console.log(n);//打印20
}
 fn(n);//实参n=10传入调用
 console.log(n);//打印10

Question 7:

 function fn(fn){
     console.log(fn);
     var fn = 20;
     console.log(fn);
     function fn(){
     	console.log(fn);
     }
}
fn(30);
var fn = 10;
console.log(fn);

Equivalent to

 function fn(fn){
 	//局部作用域 fn被形参设置 fn为30
 	 function fn(){//函数被提升到此次,并且fn设置为函数
     	console.log(fn);
     }
     var fn ;//var fn = 20;中的var变量提升到这里,但是会被忽略
     console.log(fn);//打印函数
     fn = 20;//赋值20
     console.log(fn);//打印20
}
var fn;//var fn = 10;这句中的var变量提升到这里,但是会被忽略,因为已经存在函数 fn 了
fn(30);//执行函数
fn = 10;//赋值fn为10
console.log(fn);//打印10

Eventually it will be printed out in turn: fn function 20 10

4. Closure related

Topic 1: Return value (most commonly used)

	function fn(){
			var name="hello";
			return function(){
				return name;
			}
		}
		var fnc = fn();
		console.log(fnc())//hello

This is easy to understand is to return name in the form of a closure.

Topic 2: Function assignment

 var fn2;
		function fn(){
			var name="hello";
			//将函数赋值给fn2
			fn2 = function(){
				return name;
			}
		}
		fn()//要先执行进行赋值,
		console.log(fn2())//执行输出fn2

Set the value of the fn2 function in the closure. The name attribute is memorized in the form of the closure, and the execution will output hello.

Topic 3: Function parameters

        function fn(){
			var name="hello";
			return function callback(){
				return name;
			}
		}
		var fn1 = fn()//执行函数将返回值(callback函数)赋值给fn1,
		
		function fn2(f){
			//将函数作为参数传入
			console.log(f());//执行函数,并输出
		}
		fn2(fn1)//执行输出fn2

Use a closure to return a function, use this function as a parameter of another function, execute this function in another function, and finally output hello

Topic 4: IIFE (self-executing function)

        (function(){
			var name="hello";
			var fn1= function(){
				return name;
			}
			//直接在自执行函数里面调用fn2,将fn1作为参数传入
			fn2(fn1);
		})()
		function fn2(f){
			//将函数作为参数传入
			console.log(f());//执行函数,并输出
		}

Pass the encapsulated function fn1 to fn2 directly in the self-executing function, and call it as a parameter to get the result hello

Topic 5: Loop assignment

//每秒执行1次,分别输出1-10
	for(var i=1;i<=10;i++){
		(function(j){
			//j来接收
			setTimeout(function(){
				console.log(j);
			},j*1000);
		})(i)//i作为实参传入
	}

Topic 6: Getters and setters

    function fn(){
		var name='hello'
		setName=function(n){
			name = n;
		}
		getName=function(){
			return name;
		}
		
		//将setName,getName作为对象的属性返回
		return {
			setName:setName,
			getName:getName
		}
	}
	var fn1 = fn();//返回对象,属性setName和getName是两个函数
	console.log(fn1.getName());//getter
		fn1.setName('world');//setter修改闭包里面的name
	console.log(fn1.getName());//getter

When outputting hello for the first time, use setter to output world. This can be encapsulated into a public method to prevent undesired properties and functions from being exposed to the outside.

Topic 7: Iterator (execute a function to take a value down)

        var arr =['aa','bb','cc'];
		function incre(arr){
			var i=0;
			return function(){
				//这个函数每次被执行都返回数组arr中 i下标对应的元素
				 return arr[i++] || '数组值已经遍历完';
			}
		}
		var next = incre(arr);
		console.log(next());//aa
		console.log(next());//bb
		console.log(next());//cc
		console.log(next());//数组值已经遍历完

Topic 8: Distinguish for the first time (the same parameter, the function will not be executed repeatedly)

         var fn = (function(){
				var arr=[];//用来缓存的数组
					return function(val){
						if(arr.indexOf(val)==-1){//缓存中没有则表示需要执行
							arr.push(val);//将参数push到缓存数组中
							console.log('函数被执行了',arr);
							//这里写想要执行的函数
						}else{
							console.log('此次函数不需要执行');
						}
						console.log('函数调用完打印一下,方便查看已缓存的数组:',arr);
					}
				})();
		
		fn(10);
		fn(10);
		fn(1000);
		fn(200);
		fn(1000);

Results of the

It can be clearly seen that the first execution will be saved, and the execution will be directly fetched again.

Topic 9: Caching

//比如求和操作,如果没有缓存,每次调用都要重复计算,采用缓存已经执行过的去查找,查找到了就直接返回,不需要重新计算
	 
	 var fn=(function(){
	 	var cache={};//缓存对象
	 	var calc=function(arr){//计算函数
	 		var sum=0;
	 		//求和
	 		for(var i=0;i<arr.length;i++){
		 		sum+=arr[i];
		 	}
		 	return sum;
	 	}
	 	
	 	return function(){
	 		var args = Array.prototype.slice.call(arguments,0);//arguments转换成数组
	 		var key=args.join(",");//将args用逗号连接成字符串
		 	var result , tSum = cache[key];
		 	if(tSum){//如果缓存有	
		 		console.log('从缓存中取:',cache)//打印方便查看
		 		result = tSum;
		 	}else{
		 		//重新计算,并存入缓存同时赋值给result
			 	result = cache[key]=calc(args);
			 	console.log('存入缓存:',cache)//打印方便查看
		 	}
		 	return result;
	 	}
	 })();
	fn(1,2,3,4,5);
	fn(1,2,3,4,5);
	fn(1,2,3,4,5,6);
	fn(1,2,3,4,5,8);
	fn(1,2,3,4,5,6);

Output result:

Topic 10: Throttling function

The function of the throttling function is to execute the function only once within a limited time, such as:

1. Button submit (can avoid repeated submission, of course, not only this method, it is also possible to set the button to be unavailable).

2. When the trigger frequency of scroll, mousehover, mousemove is high.

The main principle is to set a flag in the closure. The flag is set to true within a limited time, and the function will not be executed if it is clicked again. After the setTimeout function is executed, the flag is set to flase, and the execution can continue. 

 
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=GBK">
        <style>
        *{
        	margin:0;padding:0;
        }
        </style>
        </head>
        <body>
       		<div class="box" id="div_box" >
       			<button onclick="fn1()">test</button>
       		</div>
        </body>
        
<script>
//节流函数
function throttle(fn,delay){
	var flag=false;
	var timer=null;
	return function(){
		var args=[].slice.call(arguments,0);//将参数转成数组
		var context=this;
		if(flag){//如果在限定的时间内 flag是true 则直接返回,不让执行
			return;
		}
		flag=true; //函数正在控制中
		//执行函数
		fn.apply(context,args);
		clearTimeout(timer);//清除定时器
		timer =setTimeout(function(){
			flag=false;//延时时间过了以后,放开函数控制
		},delay)
		
	}	
}
function fn(){
	console.log(123);
}
 
var fn1 = throttle(fn,2000);//绑定节流函数 
 
 
</script>
</html>

The click time of the button is limited here. Only the first time will be executed within 2000 milliseconds, and it will be invalid if it is clicked multiple times.

Summary: Closures can be said to be ubiquitous, so these scenarios of closures are still worth studying.

5. Prototype and prototype chain

Topic 1:

function F() {
  this.a = 1;
}
var obj = new F();
console.log(obj.prototype);//打印undefined   对象只有 __proto__属性没有prototype属性,函数才有

Topic 2:

Object.prototype.a = 1;
var obj = {
    b: 2
};
for(var i in obj) {
    console.log(i); //能迭代出原型链里面的属性,所以会打印出 b和a
}

Question 3:

Object.prototype.a = 1; 
var obj = {
    b: undefined
};
 
console.log(obj.a);//1
console.log('b' in obj);//true  //能找到处于原型链里面的bar属性
 
console.log(obj.hasOwnProperty('a'));//false 不是自身的属性
console.log(obj.hasOwnProperty('b'));//true  是自身的属性

Question 4:

function A(){
}
function B(a){
  this.a = a;
}
function C(a){
  if(a){
		this.a = a;
  }
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
 //实例对象都能访问构造函数的原型对象,可以明确的是以上3个都是能拿到a的
 
console.log(new A().a);//1   拿到的是原型上的a=1
console.log(new B().a);//undefined  this.a = a;将new B()这个实例对象的a属性这是为undefined,所以拿到的是undefined
console.log(new C(2).a);//2   将new C(2)这个实例对象的a属性这是为2,所以拿到的是2

Question 5:

function A () {
}
A.prototype.n = 1;
 
var b = new A();//b实例对象已经建立原型连接
 
//原型对象指向被改变,不会切断b实例对象的
A.prototype = {
    n: 2,
    m: 3
}
var c = new A();//c实例对象将根据新的原型建立连接
 
console.log(b.n, b.m); //1 undefined  这里拿到是改变prototype之前的
console.log(c.n, c.m); //2 3		这里拿到是改变prototype之后的

Question 6:

var F = function(){};
Object.prototype.a = function(){
    console.log('a')
}
Function.prototype.b = function(){
    console.log('b')
}
var f = new F();
 
F.a();  //打印a  对象都能访问Object.prototype中的属性和方法
F.b();  //打印b  F是函数,原型链  F => F.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype
 
f.a();  //打印a  对象都能访问Object.prototype中的属性和方法
f.b();  //报错f.b is not a function   因f是F实例对象,原型链 f=>f.__proto__=>F.prototype=>F.prototype.__proto__ => Object.prototype

Summary: In JavaScript, objects have __proto__ attributes (implicit prototypes), which point to the prototype of the constructor function that constructs the object. Function is special. In addition to having __proto__ attributes like other objects, it also has its own The unique attribute----prototype is called the prototype object, and the prototype object has a constructor attribute, which points back to the function.

 

6, the understanding of this

Example 1 (new):

function person(name){
	this.name=name;
}
 
var p = new person("张三")
	console.log(p);
var p1 = new person("李四")
	console.log(p1);

For objects created by new, look at their this, and you can see from the figure below that new objects have been created.

Example 2 (hard binding call, apply)

	var person = {
		name:'person',
		sayHi:function(){
			console.log(this)
		}
	}
	var o={name:'o1'};
	person.sayHi.call(o);

Look at the this print in the figure below, this this is bound to the object o

 

Example 3 (context object call)

	var person = {
		name:'person',
		sayHi:function(){
			console.log(this)
		}
	}
	var o={name:'o1'};
	person.sayHi();	

this is printed as shown in the figure below and is directly bound to the person object

Example 4 (using the default binding)

	var name='person';
	function sayHi(){
		console.log(this)
	}
	sayHi();

The printed this is window

exception

	var person = {
		name:'person',
		sayHi:function(){
			console.log(this)
		}
	}
	var o={name:'o1'};
	person.sayHi.call(null);

When null or undefined is passed as the first parameter of call and apply, the default binding rule will be used, and this will point to window.

to sum up:

  • Is the function called with new? If so, this binds the newly created object. var p = new Person();
  • Is the function called by hard binding through call and apply? If it is, this binds to the specified object. var p = sayHi.call(obj);
  • Is the function called by a context object? If it is, this is bound to this context object. var p = obj.sayHi();
  • If none of the above is true, the default binding is used. var p = sayHi();
     

This of the arrow function

The arrow function is picked up separately for a reason, it is determined according to the outer scope  this , and the binding of the arrow function cannot be modified.

var a = 2;
var foo = () => {
  console.log(this.a)
}
var obj = {
  a: 4,
  foo: foo
}
obj.foo() // 2

The arrow function of the above code  is not affected by the implicit binding rules, so the result printed in the console is 2.

 

7. Array de-duplication

Way 1:

  var arr=[1,2,3,4,5,3,6,1,0];
   var arr2 = [];  
    for(let i = 0; i < arr.length; i++) {  
        if(arr2.indexOf(arr[i]) == -1) { //不包含某个值则返回-1  
            arr2.push(arr[i]);  
        }  
    }  
    console.log(arr2); 

This method is probably the easiest for everyone to think of.

Way 2:

    var arr=[1,2,3,4,5,3,6,1,0];
    var arrSort = arr.sort();  
    var arr2 = [];  
    for(let i = 0; i< arrSort.length; i++) {  
        if(arrSort[i] != arrSort[i+1]) {  
            arr2.push(arrSort[i]);  
        }  
    }  
    console.log(arr2); 

This method will cause the array to be sorted instead of maintaining the original order.

Use splice:

    var arr=[1,2,3,4,5,3,6,1,0];
    var len = arr.length;  
    for(var i = 0; i < len; i++) {  
        for(var j = i + 1; j < len; j++) {  
            if(arr[i] === arr[j]) {  
                arr.splice(i,1);  
                len--;  
                j--;  
            }  
        }  
    }  
    console.log(arr); 

ES6 Set

    var arr=[1,2,3,4,5,3,6,1,0];
    let sortSet = new Set(arr);  
    console.log(Array.from(sortSet));

 

8. Event Agent

Check out this article

 

9. Variable promotion and function promotion

Variable promotion

a=2;
var a;
console.log(a);

What will the console print in this code? It may be undefined, but it will actually print 2. Because this code will actually be processed like this:

var a;
a=2;
console.log(a);

Among them: var a; is to compile, and the following two sentences are executed.

Let's look at another paragraph:

console.log(a);
var a=2;

It will be processed like this:

var a;
console.log(a);
a=2;

The same var a; is compiled, and the next two sentences are executed, because the assignment of a=2 is after printing, so this code will output undefined..

From the above two pieces of code, we can know that a variable declared as var will be moved to the top of the current scope in the compilation phase wherever it is placed.

Function promotion

Function declarations will be promoted in the same way as variable declarations. Let’s look at an example of a function:

hello();//执行
function hello(){//函数声明,这里可以看到声明在后,执行在前,能正常执行吗?
	console.log('hello');
}

When we run this code, we can see that the output is normal, that is, the function declaration is promoted.

What if we use var to declare a function?

hello();//执行
var hello = function (){
	console.log('hello');
}

The execution will report an error:

why? Because var hello = function (){     console.log('hello'); } in this way, var hello is a variable declaration, and the following is an assignment operation, the variable is promoted, and the assignment operation is not promoted. In fact, this paragraph is equivalent In this way:

var hello;
hello();//执行
hello = function(){
	console.log('hello');
}

When executing, hello is only declared, and its value is undefined, so TypeError will appear when called with (). Change it to the following to work normally. If you want to write in this way, you need to put it in the function execution front:

var hello= function(){
	console.log('hello');
}
hello();//执行

Function first

hello();//执行
 
var hello;
 
function hello(){
	console.log('log hello');
}
 
hello = function(){
	console.log('log hello1');
}

This code will print out: log hello

From the results, we can see that log hello is printed when the function is executed. After the function is executed, the hello variable will be replaced by an assignment statement with a function that prints log hello1. Why? The actual code will be processed like this

function hello(){
    console.log('log hello');
}
 
hello();//执行
//var hello这句被忽略了
hello = function(){
    console.log('log hello1');
}


The function is first promoted to the top. At this time, the declaration var hello will be ignored, because hello already exists at this time, and it is a function, then of course the next execution of the function hello will print out the previous result, and then be replaced.
 

Function declaration coverage

hello();//执行
 
function hello(){
	console.log('log hello');
}
 
function hello(){
	console.log('log hello2');
}
 
 
function hello(){
	console.log('log hello3');
}

It prints log hello3, and the following function declarations overwrite the previous function declarations.

to sum up:

  1. Both function declarations and variable declarations will be promoted to the top of the current scope, and function first, that is, wherever we place the declaration statement.
  2. In order to make the code good-looking, easy to maintain, to avoid repeated declarations, and to avoid unnecessary risks, it is better to put the declarations first.

10、typeof vs instanceof

typeof 

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof 对于对象来说,除了函数都会显示 object
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'

instanceof

instanceof is to detect whether the prototype chain of the object points to the prototype object of the constructor

var arr = [1,2,3]; 
console.log(arr instanceof Array);   // true
function B(){}
function A(){}
A.prototype = new B();  // 原型继承

var a = new A();
console.log(a instanceof A)  //true
console.log(a instanceof B)  //true
console.log(a instanceof Object)//true

All checks with instanceof on the prototype chain return true

 

11. Callback function (Callback)

Such as jquery's ajax

$.ajax(url, function(data) {

// Processing logic

})

The callback function has a fatal weakness, that is, it is easy to write callback hell, as follows:

$.ajax(url, function(data) {
    // 处理逻辑 
    $.ajax(url_1, function(data) {
        // 处理逻辑 
        $.ajax(url_2, function(data) {
            // 处理逻辑 
            $.ajax(url_3, function(data) {
                // 处理逻辑 
            })
        })
    })
})

Although the callback function is easy to use, it also has this problem. It is recommended to use Promise to handle it.

 

12、Promise 

Promise has three states, namely:

1. Waiting (pending)

2. Completed (resolved)

3. Rejected

Once you change from the waiting state to another state, you can never change the state

new Promise((resolve,reject) => {
    resolve('success')
    // 无效
    reject('reject')
})

Promise also solves the problem of callback hell. The following code:

let p = new Promise((resolve, reject) => {
    setTimeout(resolve, 2000, 'success');
});
p.then(
    res => {
        console.log(res);
        return `${res} again`;
    }
)
    .then(
        res => console.log(res)
    );

// success
// success again

 

13, call, apply and bind functions

Similarities:

Change the point of this in the function body.


       the difference:

The difference between call and apply: the way of accepting parameters is different.

bind: Do not execute immediately. While apply and call are executed immediately.

function Person(name){
  this.name = name;
}
Person.prototype.getName=function(){
    console.log(this.name);
}

var p = new Person('张三');
p.getName();//直接执行

var p1={name:'李四'};

p.getName.call(p1);//this被改变,获取的是p1中的name
p.getName.apply(p1);//this被改变,获取的是p1中的name
p.getName.bind(p1)();//this被改变,获取的是p1中的name,不会立即执行,需要()来执行

 Output:

 

14. The difference between == and ===

Simply put: == means the same, === means strictly the same; 

 When doing == comparison: first check the data types of the two operands, if they are the same, then perform === comparison, if they are different, then perform type conversion, and then perform comparison after conversion to the same type, and === comparison, If the types are different, it is directly false, and there will be no conversion.

 

I have included these for the time being, I hope it will be helpful to everyone, and give me three companies to support me, brothers! !

 

Guess you like

Origin blog.csdn.net/dkm123456/article/details/114939677