[Direct collection] 100 front-end JavaScript interview questions (final)

87. Event agent

Event proxy is event delegation

Instead of directly adding an event to the label, it is to add an event to the parent of the label. Use the event object to determine who the label object that triggered the event is. Execute different functions. The grammatical form of the program

Advantages of entrusting

Reduce memory consumption
Just imagine, if we have a list with a large number of list items, we need to respond to an event when the list item is clicked

If a function is bound to each list item one by one, it will consume a lot of memory and consume a lot of performance in terms of efficiency;

Therefore, a better way is to bind this click event to its parent layer, which is ul, and then match and judge the target element when executing the event;

Therefore, event delegation can reduce a lot of memory consumption and save efficiency.

Binding events dynamically
For example, in the above example, there are only a few list items, and we bind events to each list item;

In many cases, we need to dynamically add or remove list item elements through AJAX or user operations, so we need to re-bind events to newly added elements and unbind events to elements to be deleted every time there is a change;

If you use event delegation, there is no such trouble, because the event is bound to the parent layer, and it has nothing to do with the increase or decrease of the target element. The execution to the target element is matched in the process of actually responding to the execution of the event function ;

So using events can reduce a lot of repetitive work in the case of dynamic binding events.

88. No freeze

How to render data without getting stuck on the page, that is to say, tens of thousands of items cannot be rendered at one time, but part of the DOM should be rendered at one time, so that it can be refreshed every 16 ms through requestAnimationFrame.

<ul>控件</ul> <script>    setTimeout(() => {
   
          // 插入十万条数据      const total = 100000      // 一次插入 20 条,如果觉得性能不好就减少      const once = 20      // 渲染数据总共需要几次     const loopCount = total / once      let countOfRender = 0      let ul = document.querySelector("ul");      function add() {
   
         // 优化性能,插入不会造成回流       const fragment = document.createDocumentFragment();      for (let i = 0; i < once; i++) {
   
           const li = document.createElement("li");        li.innerText = Math.floor(Math.random() * total);        fragment.appendChild(li);      }     ul.appendChild(fragment);     countOfRender += 1;     loop();  }  function loop() {
   
         if (countOfRender < loopCount) {
   
          window.requestAnimationFrame(add);   }  }  loop();  }, 0);

89. instanceof in JavaScript

The typeof operator is often used to judge the type of variables in JavaScript, but there is a defect when using typeof, that is, when judging the reference type storage value, it returns object no matter what type of object is referenced. ECMAScript introduces another Java operator instanceof to solve this problem. The instanceof operator is similar to the typeof operator and is used to identify the type of object being processed. Unlike the typeof method, the instanceof method requires the developer to explicitly confirm that the object is of a particular type.

1. instanceof operator usage

var strObj = new String("String");
console.log(strObj instanceof String);// true


This piece of code judges whether the variable strObj is an instance of the String object, and strObj is an instance of the String object, so it is "true". Although not as flexible as the typeof method, the instanceof method is useful in cases where the typeof method returns "object".

// Determine if foo is an instance of the Foo class
function Foo(){}
var foo = new Foo();

console.log(foo instanceof Foo)


2. instanceof is used in the inheritance relationship

// Determine whether foo is an instance of the Foo class, and whether it is an instance of its parent type
function Aoo(){}
function Foo(){}
Foo.prototype = new Aoo(); //JavaScript prototype inheritance

var foo = new Foo();
console.log(foo instanceof Foo)//true
console.log(foo instanceof Aoo)//true
foo is used as an instance of the constructor Foo, because the constructor Foo prototype inherits the constructor Aoo, so it returns true. In this code, the parent class in the one-level inheritance relationship is judged. In the multi-level inheritance relationship, the instanceof operator is also applicable.

3. instanceof operator code
function instance_of(L, R) { //L means left expression, R means right expression
var O = R.prototype; // Get the display prototype of R
L = L.__proto__; // take the implicit prototype of L
while (true) {
  if (L === null)
    return false;
  if (O === L) // The important point here: when O is strictly equal to L, return true
    return true;
  L = L.__proto__;
}
}

90. await in forEach

Not sure if you have written code like this:

 function test() {
   
        let arr = [3, 2, 1]     arr.forEach(async item => {
   
         const res = await fetch(item)      console.log(res)     })     console.log('end')    }     function fetch(x) {
   
    return new Promise((resolve, reject) => {
   
     setTimeout(() => {
   
      resolve(x)  }, 500 * x) })}
test()

The print order I was expecting was

3
2
1
end
As a result, the reality made a joke with me, the printing order is actually

end
1
2
3
Why?

In fact, the reason is very simple, that is, forEach only supports synchronous code.

We can refer to the Polyfill version of forEach, which is similar to the pseudo code after simplification​​​​​​​​

while (index < arr.length) {
   
     callback(item, index)   //也就是我们传入的回调函数}

From the above code, we can find that forEach simply executes the callback function and does not handle asynchronous situations. And you can't end the traversal even if you use break in the callback.

How to deal with it?

Generally speaking, there are two solutions, for...of and for loop.

Can the way of using Promise.all work? The answer is: no.​​​​​​

从上述代码中我们可以发现,forEach 只是简单的执行了下回调函数而已,并不会去处理异步的情况。并且你在 callback 中即使使用 break 也并不能结束遍历。
怎么解决?
一般来说解决的办法有2种,for...of和for循环。
使用 Promise.all 的方式行不行,答案是:不行

It can be seen that the output is not as we expected.

The reason why this works is that the async function will definitely return a Promise object. After calling map, the return value is an array storing the Promise, so we can solve the problem by passing the array into Promise.all. However, this method cannot actually achieve the desired effect. If you want the internal fetch to be completed sequentially, you can choose the second method.

The first method is to use for...of​​​​​​​​

   async function test() {
   
        let arr = [3, 2, 1]     for (const item of arr) {
   
         const res = await fetch(item)      console.log(res)     }     console.log('end')    }

This method is much more concise than Promise.all, and can also achieve the output order I wanted at the beginning.

But do you have another question at this time? Why can await take effect inside for...of?

Because the internal processing mechanism of for...of is different from that of forEach, forEach calls the callback function directly, while for...of traverses through iterators. ​​​​​​​​

async function test() {
   
    let arr = [3, 2, 1] const iterator = arr[Symbol.iterator]() let res = iterator.next() while (!res.done) {
   
     const value = res.value  const res1 = await fetch(value)  console.log(res1)  res = iterator.next() } console.log('end')}

The second method is to use a for loop​​​​​​​​

async function test() {
   
     let arr = [3, 2, 1]  for (var i=0;i<arr.length;i++) {
   
       const res = await fetch(arr[i])    console.log(res)  }  console.log('end')}
function fetch(x) {
   
    return new Promise((resolve, reject) => {
   
     setTimeout(() => {
   
      resolve(x)  }, 500 * x) })}
test()

The third way is to use while loop​​​​​​​​

async function test() {
   
     let arr = [3, 2, 1]  var i=0;  while(i!==arr.length){
   
       const res = await fetch(arr[i])    console.log(res)    i++;  }  console.log('end')}
function fetch(x) {
   
    return new Promise((resolve, reject) => {
   
     setTimeout(() => {
   
      resolve(x)  }, 500 * x) })}
test()

To use async await in a loop, use for...of or for loop, while loop

forEach supports async awaitforEach. Under normal circumstances, writing like the following is definitely not synchronous. The program will not wait for the asynchronous completion of a loop before proceeding to the next loop. The reason is obvious. In the above simulation, the while loop simply executes the callback, so although await is used in the callback, it only affects the inside of the callback. ​​​​​​​​

arr.myforeach(async v => {
   
       await fetch(v);});

To support the above writing method, just change it a little bit​​​​​​​​

Array.prototype.myforeach = async function (fn, context = null) {
   
       let index = 0;    let arr = this;    if (typeof fn !== 'function') {
   
           throw new TypeError(fn + ' is not a function');    }    while (index < arr.length) {
   
           if (index in arr) {
   
               try {
   
                   await fn.call(context, arr[index], index, arr);            } catch (e) {
   
                   console.log(e);            }        }        index ++;    }};

91. src and href

Both src and href are used to introduce external resources, such as images, CSS files, HTML files, and other web pages, etc. So what are the differences between src and href?

1. Different resource types are requested
(1) href is the abbreviation of Hypertext Reference, which means hypertext reference. Used to establish a link between the current element and the document. The commonly used ones are: link, a.
(2) When the src resource is requested, the resource it points to will be downloaded and applied to the document, commonly used are script, img, iframe;

2. Different results
(1) href is used to establish a link between the current document and the referenced resource;

(2) src is used to replace the current content;

3. The browser parses differently
(1) If href is added to the document, the browser will recognize the document as a CSS file, and will download resources in parallel without stopping the processing of the current document. This is why it is recommended to use link to load CSS instead of @import.

(2) When the browser resolves to src, it will suspend the download and processing of other resources until the resource is loaded, compiled, and executed. The same is true for pictures and frames, similar to applying the pointed resource to the current content. This is why it is recommended to put the js script at the bottom instead of the head.

92. Method of event binding in JavaScript

In the learning of JavaScript, we often encounter the event mechanism of JavaScript, for example, event binding, event listening, event delegation (event proxy), etc. What do these nouns mean and what are their functions?

1. Event binding To make JavaScript respond to user operations, it is first necessary to bind event handlers to DOM elements. The so-called event processing function is a function that handles user operations, and different operations correspond to different names.

In JavaScript, there are three commonly used methods of binding events:

Bind directly in DOM elements; bind in JavaScript code; bind event listener functions.

1. Directly bind events in DOM​​​​​​​​

我们可以在DOM元素上绑定onclick、onmouseover、onmouseout、onmousedown、onmouseup、ondblclick、onkeydown、onkeypress、onkeyup等。好多不一一列出了。如果想知道更多事件类型请查看, DOM事件 。
<input type="button" value="click me" onclick="hello()">

<script>function hello(){
   
    alert("hello world!");}

2. Bind events in JavaScript code​​​​​​​​

在 JS 代码中(即 script 标签内)绑定事件可以使 JS 代码与HTML标签分离,文档结构清晰,便于管理和开发。
<input type="button" value="click me" id="btn">
<script>document.getElementById("btn").onclick = function(){
   
    alert("hello world!");}

3. Use event monitoring to bind events

Another way to bind events is to use addEventListener() or attachEvent() to bind event listener functions. The following details, event monitoring.

1) Event monitoring​​​​​​

关于事件监听,W3C规范中定义了3个事件阶段,依次是捕获阶段、目标阶段、冒泡阶段。
起初Netscape制定了JavaScript的一套事件驱动机制(即事件捕获)。随即IE也推出了自己的一套事件驱动机制(即事件冒泡)。最后W3C规范了两种事件机制,分为捕获阶段、目标阶段、冒泡阶段。IE8以前IE一直坚持自己的事件机制(前端人员一直头痛的兼容性问题),IE9以后IE也支持了W3C规范。
W3C规范
element.addEventListener(event, function, useCapture)event : (必需)事件名,支持所有 DOM事件 。function:(必需)指定要事件触发时执行的函数。useCapture:(可选)指定事件是否在捕获或冒泡阶段执行。true,捕获。false,冒泡。默认false。注:IE8以下不支持。
<input type="button" value="click me" id="btn1">
<script>document.getElementById("btn1").addEventListener("click",hello);function hello(){
   
    alert("hello world!");}IE标准
element.attachEvent(event, function)event:(必需)事件类型。需加“on“,例如:onclick。function:(必需)指定要事件触发时执行的函数。<input type="button" value="click me" id="btn2"><script>document.getElementById("btn2").attachEvent("onclick",hello);function hello(){
   
    alert("hello world!");}​​​​​​
<script>document.getElementById("btn2").attachEvent("onclick",hello);function hello(){
   
    alert("hello world!");}

2) Advantages of event monitoring

1. You can bind multiple events; conventional event binding only executes the last bound event. ​​​​​​​​

<input type="button" value="click me" id="btn3">  <input type="button" value="click me" id="btn4">
var btn3 = document.getElementById("btn3");btn3.onclick = function(){                  //动态绑定事件 alert("hello 1");           //不执行}btn3.onclick = function(){
   
    alert("hello 2");           //执行}
var btn4 = document.getElementById("btn4");btn4.addEventListener("click",hello1);      //添加事件监听器btn4.addEventListener("click",hello2);
function hello1(){
   
    alert("hello 1");        //执行}function hello2(){
   
    alert("hello 2");        //执行 (顺序执行)}

2. The corresponding binding can be released​​​​​​​​

<input type="button" value="click me" id="btn5">
<script>var btn5 = document.getElementById("btn5");btn5.addEventListener("click",hello1);//执行了btn5.addEventListener("click",hello2);//不执行btn5.removeEventListener("click",hello2);
function hello1(){
   
    alert("hello 1");}function hello2(){
   
    alert("hello 2");}

3) Encapsulate event monitoring​​​​​​

<input type="button" value="click me" id="btn5">
//绑定监听事件function addEventHandler(target,type,fn){
   
    if(target.addEventListener){
   
    target.addEventListener(type,fn); }else{
   
    target.attachEvent("on"+type,fn); }}
//移除监听事件function removeEventHandler(target,type,fn){
   
    if(target.removeEventListener){
   
    target.removeEventListener(type,fn); }else{
   
    target.detachEvent("on"+type,fn); }}

93. Common branches of git

git branch naming convention
In order to standardize development, keep code submission records and git branch structure clear, and facilitate subsequent maintenance, the related operations of git are now regulated.

Two main points:

git branch naming convention

git commit record specification

1. Git branch naming convention
  The git branch is divided into integration branch, function branch and repair branch, which are named develop, feature and hotfix respectively, all of which are singular. Bad names such as features, future, hotfixes, hotfixes, etc. cannot be used.

master (master branch, always available stable version, cannot develop directly on this branch)
develop (development main branch, all new functions use this branch to create their own development branch, this branch only does merge operations, and cannot be directly developed on this branch)
feature-xxx (feature development branch, create a branch on develop, named after the self-developed function module, and merge it into the develop branch after the function test is normal)
feature-xxx-fix (feature bug fix branch, find bugs after merging feature branch, create a branch fix on develop, and then merge back to develop branch. PS: After applying for merging, the feature branch can still submit code before it is merged, so feature can continue to fix bugs on the original branch before merging)
hotfix-xxx (emergency bug modification branch, created on the master branch, merged into master after the repair is completed)
Precautions:

Try to develop one functional module in one branch, and do not develop multiple functional modules on one branch.
Before applying for the merge of the feature branch, it is best to pull the main develop branch first to see if there are any conflicts. If there are any conflicts, first resolve the conflicts and then apply for the merge.

94. Front-end engine template

JavaScript is becoming more and more popular with web developers and designers as various magical and practical function libraries, such as jQuery, MooTools, Prototype, etc., are becoming more and more popular.

1) Jade

Jade is a JavaScript template engine with a complete API and amazing features. Write HTML pages using whitespace and indentation sensitive code formatting. Based on Node.js, running on the server side.

2) Mustache

Mustache is a logic-less (no logic or light logic) grammar template. Can be used to organize anything from HTML, configuration files, source code. Mustache uses the value of a JavaScript object to expand brace tags in template code.

3) Transparency

Transparency is a powerful client-side template engine used to bind data to the BOM structure of Web pages. Its templates require no special formatting and are fully HTML compliant. Use JavaScript logic directly, no need to learn a special "template language". Compatible with IE9+, Chrome, Fx, iOS, Android and other browsers.

4) Underscore.js

Underscore.js is a JavaScript library that provides a series of useful utility functions (helpers). Underscore.js only works independently as an additional utility function, and does not augment (pollute) any JavaScript built-in objects themselves.

5) Embeddedjs

EJS helps developers effectively separate JavaScript and HTML in the form of JS/HTML that is similar to PHP through label mixing.

6) DoTjs

The fastest and cleanest JavaScript templating engine for both Node.js and the browser.

7) Handlebarsjs

A set of semantic template engines. Compatible with Mustache.

8) T.js

A template engine that uses simple JavaScript data structures to render and represent html/xml content.

9) Dustjs

An asynchronous templating engine that works both in the browser and in Node.js.

10) Nunjucks

Nunjucks is a feature-rich template engine. The template language is powerful and supports functions such as block inheritance, automatic escaping, macros, and asynchronous control.

What kind of template engine is suitable for the front end

The front-end template engine needs to have transparency during development. I think that any front-end framework and tools must have transparency for development, and the template engine is no exception. The so-called transparency means that after I set up the development environment, I can write the code and refresh the browser to see the latest effect, without the need to execute any additional commands or have any waiting process, so everything depends on the template engine of the compilation process It is not suitable for front-end use. Compilation can only be a feature of the template engine, not a prerequisite for use. Strictly speaking, using FileWatch and other means to detect file changes and automatically compile is not within the scope of my consideration, because it will Causes extra waiting, people like me with extremely fast hands may not be able to keep up with the compilation speed, so it can be launched. The front-end template engine should have the ability to be parsed and used in a pure front-end environment

The front-end template engine must have good runtime debugging capabilities

The front end is not like the back end, any error can have strict logging and call stack for analysis. Due to the uncertainty of user behavior, the uncertainty of the execution environment, the impact of various third-party scripts, etc., it is difficult for the front-end to complete error handling and tracking, which also leads to the fact that the front-end inevitably needs to directly troubleshoot problems online And when the problem occurs at the template engine level, the template engine needs to provide good debugging capabilities It does not have readability and breakpoint tracking. Therefore, at this point, a template engine for front-end use should be able to switch from "execute the compiled function to obtain HTML" back to "parse the original template and then execute the function to obtain HTML" " mode, that is, it should support switching between the two modes or better, the function generated by a powerful front-end template engine can be directly mapped back to the original template fragment using Source Map or other custom means, but there is no What template engine implements this functionality

The front-end template engine should be friendly to file merging

Before the popularization of HTTP/2, file merging was still an important method in front-end performance optimization. Templates, as part of files, still needed to be merged.

In the template engine that provides compilation function, we can use compilation to convert the template into JavaScript source code, and then merge files based on JavaScript

But if we want to keep the original template fragments for reasons such as the debugging ability mentioned above, then the template engine itself needs to support the merging of template fragments into one file

Most engines that only support parsing an input string as a template do not have this ability. They are not inherently able to split an entire string into multiple template fragments, so they cannot support file merging at the template fragment level

Need to implement support for file merging, the best way is to make the syntax of the template based on "fragments"

The front-end template engine should be responsible for XSS prevention

In terms of security, the front-end has strict requirements on the control of XSS

Will I have more security issues with single-page (SPA) development than multi-page? - As mentioned in Zhang Lili's answer, the more appropriate way for the front-end to prevent XSS is to use the "default escape" whitelist strategy

Based on this, a reasonable template engine must support default escaping, that is, the output of all data is processed by escape logic by default, and key symbols are converted into corresponding HTML entity symbols, so as to eliminate the XSS intrusion path from the root.

Of course, not all content must be escaped. In the system, there is inevitably a need for users to input rich text, so it is necessary to support specific syntax to generate unescaped output, but always pay attention to unescaped output is a special case , which must be escaped by default

The front-end template engine must support the reuse of fragments

This is not a requirement of the front-end template engine. In fact, any template engine should support the reuse of fragments. The back-end such as Velocity, Smarty, etc. all have this function.

The so-called fragment multiplexing should have the following levels of application:

A fragment can be imported into another place, which is equivalent to the effect of using a variable everywhere

When a fragment is introduced, different data can be passed to it, which is equivalent to the effect of using a function everywhere

A fragment can be replaced externally, but if the fragment is not provided externally, keep a default content, similar to the strategy pattern in the design pattern

There are many template engines that satisfy the first and second points, but the front-end template engines that meet the third point are rare, and the back-end Razor, Smarty, etc. all have this function

Speaking of which, when I was designing the third version of our own template engine, I came up with the concept of block to realize the third point. After nearly half a year of delivery, someone told me that Smarty had this concept. There is a feeling of being overwhelmed by not knowing whether to be happy or sad. Fortunately, he didn't suspect that I directly copied other people's functions, otherwise it would be wrong

The front-end template engine needs to support data output processing

The so-called data output processing means that a piece of data needs to be converted during output, the most common such as the trim operation of strings, and the more technical ones such as markdown conversion, etc.

It is true that the data conversion can be processed through JavaScript logic before the data is handed over to the template engine, but this will lead to a lot of ugly and redundant code, which will also have a negative impact on the reusability of the logic itself

Usually, the template engine will use the form of filter to perform additional processing on data, similar to the logic of the pipeline in bash. The implementation and registration of filters will also have different designs. For example, mustache actually registers the filter factory, while other template engines will directly register the filter itself. Different designs have different considerations. It is difficult for us to say who is good and who is bad.

However, after the template engine supports data output processing, it will cause us a new entanglement in the coding process, that is, which data processing should be implemented by the filter of the template engine, and which should be handled by our own logic before being handed over to the template engine accomplish. This topic will be a long discussion, if it has nothing to do with the current topic, just skip it

The front-end template engine must support dynamic data

In the development process, there are actually many data that are not static. For example, EmberJS provides the concept of Computed Property, Angular also has similar things, and Backbone can realize it in disguise by rewriting the get method of Model.

Although ES5 directly provides getter support at the language level, we still don't use this language feature in most scenarios of front-end development, but choose to encapsulate dynamic data into methods such as get of some object and template The engine should also pay attention to this point in the process of converting data into HTML fragments, and has good support for these dynamically calculated data

To put it more clearly, the template engine should not only accept plain objects (Plain Object) as input, but should be more open to accept dynamic data like get method

A more reasonable logic is that if an object has a get method (the template engine determines this interface), the data is obtained through this method. In other cases, the input object is regarded as a plain object (Plain Object), and the standard property acquisition logic is used.

The front-end template engine should be closely integrated with the asynchronous process

A big feature of the front end is that it is full of asynchronous processes. Due to the single-threaded execution of JavaScript in the engine provided by the browser, the fact that most IO-related APIs are exposed as asynchronous, and the fact that the dynamic acquisition of templates in most module definition specifications is asynchronous, we are destined to be unable to The world is seen as completely synchronized

A very common example is that we have an AMD module that stores globally used constants that the template engine needs to use. Of course, we can let JavaScript get this module asynchronously before using the template engine, and then pass the constant as data to the template engine, but this is a way of relatively coupling business and view. I don’t think it’s obsessive-compulsive. A nice design, so we want to write this directly in the template:

{
   
   {$globals.ICP_SERIAL}}

This is my hypothetical syntax. You can use AMD Loader to get the globals module through $globals, and then get the ICP_SERIAL attribute output in it.

Asynchronous template engine support is a challenging topic, and my plan is to try to implement it in the next version of our own template engine. This involves many technical points, such as:

The output of the template itself has become an asynchronous method, instead of directly returning a string as it is now

Analyze the template's dependence on asynchronous operations, the splicing logic of the entire string is interrupted into multiple asynchronous

Asynchrony needs to wait, and the waiting is unknown. From the perspective of performance, whether it is necessary to consider Stream-style output in order to complete a section and provide a section

Whether to provide built-in fixed asynchronous logic, or to support any custom asynchronous logic based on Promise, to strike a balance between complexity and practicability

So far, I haven't fully clarified the way and interface of template and asynchronous combination, and I can't continue to discuss this topic further.

The front-end template engine should support different development modes

Since the development of the front end, there are many different development models, such as:

For the most common HTML pages, use events such as DOMContentLoaded to add logic, and partially refresh the page under specific interactions

Single-page development using the traditional MVC model

Use the MVVM method to develop with data as the core and binding data and view direction

Development of data comparison Diff to DOM update based on Immutable Data (there may be the introduction of Virtual DOM)

It is a very big challenge for a template engine to support so many different modes, especially the support for two-way binding. So far, almost all development frameworks that support two-way binding have their own dedicated template engines, because two-way binding has two requirements for templates:

Able to extract the meta-information of "which data this template depends on" from the template

It is possible to know which part of the template a data change engine is in, without refreshing the whole

However, general template engines rarely provide these two features, so there is no way to fully support different front-end development models.

In terms of the implementation of the template engine itself, one method is to directly expose the AST-like structure after template parsing for reasonable processing by other frameworks, and at the same time provide a partial refresh function for the template (it can also be used with the aforementioned template Fragments together), but most template engines will not parse out AST-like grammatical structures for performance and other considerations

The front-end template engine must have isolation between instances

In large-scale front-end projects, especially single-page projects, there will be a completely unknown number of template fragments at the same time. If these fragments have names (for reuse considerations), it is easy to cause name confusion. For logic at the same level (for example, everyone is code in the business layer, or everyone is code in the control layer), name conflicts can be resolved through some development-time conventions. But between different layers, due to the requirements of encapsulation, the outside should not know the names of some fragments that are only used internally. At this time, if unfortunately there are name conflicts with other layers, the situation will become more troublesome. Not easy to track and often results in a lot of wasted effort and time

Therefore, a good template engine should have multiple instances, and different instances should be isolated from each other, so that such unpredictable conflicts will not occur

If you study this topic further, you will find that simple isolation is not enough. In addition to the non-conflicting requirements between different layers, there are also fragment reuse requirements. We will also need to open some fixed templates between different template instances. Fragments are shared, so the relationship between each instance of the template engine is a combined dependency but with basic encapsulation and isolation

95. Datalist usage​​​​​​​​

<input list="browsers"><datalist id="browsers">  <option value="Internet Explorer">  <option value="Firefox">  <option value="Chrome">  <option value="Opera">  <option value="Safari"></datalist>

Note here that the id of the datalist is bound to the list attribute of the input, so that a list will appear under the input input box

96. The difference between ajax synchronous and asynchronous

When using ajax to request data, we usually treat async:true as the default, making our request an asynchronous request. But in some cases, we need to set async:false to false, so that we can observe the direction and where of the data. What is the difference between synchronous and asynchronous?

Synchronous request async: false​​​​​​​​

$.ajax({         async:false,        type:"POST",        url:"Venue.aspx?act=init",        dataType:"html",        success:function(result){  //function1()            f1();            f2();         }         failure:function (result) {             alert('我在弹');         }        }function2();

Analysis at this time, after the ajax block sends a request, it will wait at the place of function1(), and will not execute function2() until the part of function1() is executed. Asynchronous request async:true​​​​​​​​

$.ajax({         async: true, //默认为 true        type:"POST",        url:"./xxx/xxx/a/b.html",        dataType:"html",        success:function(result){  //function1()             f1();             f2();         }        failure:function (result) {               alert('我弹');         },        }function2();

Analysis When the ajax block sends a request, it will stay in function1(), waiting for the result to be returned, but at the same time (during this waiting process), function2() can run. To sum up (the difference between the two) when synchronizing requests, the codes are like queuing up and must be executed one by one. If the previous ones are not finished, the following codes are in a blocked state. When executing asynchronously, other code statements can also be executed synchronously while requesting data. For example, when requesting data, due to some willingness, the request result needs to be returned slowly. At this time, the bandwidth is very idle. Then, The code will not wait until the previous data is completely requested to return before the subsequent code can start running.

97. JavaScript pseudo-array

array

Definition: An array is a list-like object, and its prototype provides related operations for traversing and modifying elements. The length and element type of JavaScript arrays are not fixed. Only integers can be used to index array elements, not strings. Objects have no index, which is the basic characteristic of arrays. ​​​​​​​​

var obj = {};var arr = [];
obj[2] = 'a';arr[2] = 'a';
console.log(obj[2]); // => aconsole.log(arr[2]); // => aconsole.log(obj.length); // => undefinedconsole.log(arr.length); // => 3

Obj[2] outputs 'a' because the object is a common key-value pair to access data, but arr[2] outputs 'a' differently. The array accesses data through indexes, and arr[2] outputs ' a', because the position of index 2 of the array arr has stored data obj.length does not have the characteristics of an array, and obj does not save the attribute length, then it will naturally output undefined. For arrays, length is a built-in array Attribute, the array will change the value of length according to the index length. Why does arr.length output 3 instead of 1? When adding elements to the array, it is not added according to the continuous index, so the index of the array is not continuous, which leads to the index length. greater than the number of elements

pseudo array

definition:

The pseudo-array is an object (Object), and the real array is an array (Array) that has a length property and must be of type number. Other properties (index) are strings that do not have the methods that arrays have, forEach(), etc. However, there is an Object method where the length of the pseudo-array is immutable, and the length of the real array can be changed, which can be traversed through for in​​​​​​​​

var fakeArray = {
   
       length: 3,    "0": "first",    "1": "second",    "2": "third"}var arr = [1, 2, 3, 4]
// 真数组的方法来自Array.prototypeconsole.log(fakeArray instanceof Array) //falseconsole.log(arr instanceof Array) // true
Array.isArray(fakeArray) // false;Array.isArray(arr) // true;
console.log(arr.__proto__ === Array.prototype) // trueconsole.log(fakeArray.__proto__ === Array.prototype) // falseconsole.log(fakeArray.__proto__ === Object.prototype) // true
arr.forEach(x => console.log(x)) // 1 2 3 4fakeArray.forEach(x => console.log(x)) // fakeArray.forEach is not a function
Object.keys(fakeArray) //  ["0", "1", "2", "length"]

Common pseudo-arrays are:

The list of argumentsDOM objects inside the function (such as the list obtained by document.getElementsByTags) and jQuery objects (such as $("div") ) pseudo-array is an Object, while the real array is an Array. The significance of the existence of pseudo-arrays is that ordinary objects can also use many methods of arrays, such as:​​​​​​​​

使用Array.prototype.slice.call();var arr = Array.prototype.slice.call(arguments);
Array.prototype.forEach.call(arguments, function(v) {
   
     // 循环arguments对象});
// push// some// every// filter// map// ...

使用[].slice.call()var fakeArray = {
   
       length: 3,    "0": "first",    "1": "second",    "2": "third"}var arr = [].slice.call(fakeArray) console.log(arr) // ["first", "second", "third"]
使用ES6中的Array.from方法var fakeArray = {
   
     length: 3,    "0": "first",    "1": "second",    "2": "third"  }var arr = Array.from(fakeArray)console.log(arr) // ["first", "second", "third"]
使用扩展运算符,也是ES6的语法var fakeArray = document.querySelectorAll('div')var newArr= [...fakeArray]console.log(newArr.__proto__ === Array.prototype) // true
伪数组转换为真数组原理Array.prototype.slice = function (start, end) {
   
     start = start || 0  end = start || this.length  const arr = []  for (var i = start; i < end; i++) {
   
       arr.push(this[i])  }  return arr}

Conclusion The object does not have the property value of the array Array.prototype, the type is Object, and the array type is Array The array is implemented based on the index, the length will be automatically updated, and the object is a key-value pair Using the object can create a pseudo-array, and the pseudo-array can be used normally Most methods of arrays

98. Same Origin Policy

What is homology?
The domain name, protocol, and port are exactly the same as the same source.

www.juejin.com and juejin.com
Different origin, because the domain name is different

www.bilibili.tv and http://www.bilibili.com
Different origin, because the domain name is different

http://localhost:3000 和 http://localhost:3001
different source because port is different

qq.com and https://qq.com
different source because the protocol is different

www.pixiv.net and www.pixiv.net/manage/illu…
The same origin, because the domain name, protocol, and port are all the same

What is strategy?
The strategy mainly limits the ability of js
1. Unable to read the content of non-same origin cookie, Storage, indexDB
2. Unable to read non-homologous DOM
3. Unable to send non-same-origin AJAX, more precisely, the request should be sent but blocked by the browser.
Why is there a same-origin policy?

In order to protect the security of user data

1. In order to prevent malicious webpages from obtaining local data of other websites.
2. In order to prevent malicious websites from obtaining data when iframe other websites.
3. In order to prevent malicious websites from having the right to access other websites on their own website, so as not to avoid logging in and get data through cookies.
cross-domain issues
The front-end and back-end are separated, and when using service provider data, the front-end page address and the back-end API are not of the same origin. For example, the front-end address is baidu.com, and the back-end API is api.baidu.com. Direct access to the API will trigger the same-origin policy, so we need to find a way to cross it.
Principles of common cross-domain methods
1.CORS
•CORS (Cross-Origin Resource Sharing) uses a dedicated HTTP header, and the server (api.baidu.com) tells the browser that the ajax request for a specific URL (baidu.com) can be used directly without activating the same-origin policy.
2.JSONP
• This solution is equivalent to black magic, because js calls (actually all <\script>, <\img>, <\iframe> with src attributes) will not go through the same-origin policy, for example, baidu.com references CDN jquery. So I get JSON data from the server to bypass the same-origin policy by calling the js script.
3. nginx reverse proxy
• When you visit baidu.com/api/login, the nginx server on baidu.com will recognize that you are a resource under the api, and will automatically proxy to api.baidu.com/login. The browser itself does not know me In fact, the data of the accessed api.baidu.com has the same origin as the front-end resources, so the same-origin policy of the browser will not be triggered.

99. Get the second largest number

Method 1 Sort the array from largest to smallest and then find the second method. Of course, there is a sort() method in JS to sort arrays​​​​​​​​

var arr=[5,2,10,8,0,4,7,11,9,1];function array1(){
   
       var max,min;    if(arr[0]<arr[1]){
   
             max=arr[1];          min=arr[0];    }         else    {
   
            max=arr[0];         min=arr[1];    }         for(i=2;i<arr.length;i++)    {
   
           if(arr[i]>min)        {
   
               if(arr[i]>max)            {                   min=max;                max=arr[i];            }            else                  min=arr[i];        }    }    alert(min);}array1();

Method Two

Define two variables max min loop traversal to store the current largest and second largest number respectively and then output the second largest number min;​​​​​​​​

var arr=[5,2,10,8,0,4,7,11,9,1];function array2(){
   
       var temp,min;    for(var i=0;i<arr.length-1;i++){
   
           min=i;        for(var j=i+1;j<arr.length;j++){
   
               if(arr[j]>arr[i]){                  temp= arr[i];                arr[i] = arr[j];                arr[j] = temp;            }        }    }    alert(arr[1]);}array2();

100. The difference between forin and Object.keys

Using for in to traverse the object will also print out the methods or properties extended on the prototype​​​​​​​​

// 递归写法Object.prototype.clone = function(){
   
       let o = this.constructor === Array ? [] : {};    for(let e in this){
   
           o[e] = typeof this[e] === "object" ? this[e].clone() : this[e];    }    return o; }let obj = {
   
       a : 1,    b : {
   
           c: 2    }}let obj2 = obj.clone();console.log(obj2);// { a: 1, b: { c: 2, clone: [Function] }, clone: [Function] }

The solution can be to add hasOwnProperty to each traversal. The specific function of hasOwnProperty is to judge whether the property belongs to the object itself.

// recursive writing​​​​​​

Object.prototype.clone = function(){
   
       let o = this.constructor === Array ? [] : {};    for(let e in this){
   
           if(this.hasOwnProperty(e)){
   
               o[e] = typeof this[e] === "object" ? this[e].clone() : this[e];        }    }    return o; }let obj = {
   
       a : 1,    b : {
   
           c: 2    }}let obj2 = obj.clone();console.log(obj2); // { a: 1, b: { c: 2 } }
也可以使用Object.keys()方式完成遍历操作
// 递归写法Object.prototype.clone = function(){
   
       let o = this.constructor === Array ? [] : {};
    Object.keys(this).forEach(item => {
   
           o[item] = typeof this[item] === "object" ? this[item].clone() : this[item]    })    return o; 
}let obj = {
   
       a : 1,    b : {
   
           c: 2    }}let obj2 = obj.clone();console.log(obj2);// { a: 1, b: { c: 2 } }

For more interview questions, please see the special interview collection

Guess you like

Origin blog.csdn.net/shi15926lei/article/details/132108065
Recommended