1. Implement forEach with native JS

if(!Array.prototype.forEach) {
    Array.prototype.forEach = function(fn, context) {
        var context = arguments[1];
        if(typeoffn ! = ="function") {
            throw new TypeError(fn + "is not a function");
        }

        for(var i = 0; i < this.length; i++) {
            fn.call(context, this[i], i, this); }}; }Copy the code

Let’s look at the syntax of forEach first

array.forEach(callBack(currentValue, index, arr), thisValue)
Copy the code
callback
Copy the code

A function executed for each element in the array that takes one to three arguments:

  • currentValue

    The current element in the array being processed.

  • The index of the optional

    The index of the current element being processed in the array.

  • An array of optional

    The array that the forEach() method is manipulating.

ThisArg optional

This parameter is optional. Used as the value of this when the callback function is executed.

Context, this[I], I, and this correspond to currentValue, index, and arr. Context is an optional reference to call()

2, implement the apply/bind/call method

Function.prototype.call = function (context,... args) {
    context = context?Object(context):window
    let res
    context.fn = thisres = context.fn(... args)delete context.fn
    return res
}
Copy the code
Function.prototype.apply = function (context, arr) {
  context = context ? Object(context) : window
  context.fn = this
  let res
  if(! arr) { res = context.fn() }else{ res = context.fn(... arr) }delete context.fn
  return res
}

Copy the code
Function.prototype.bind = function(context,args) {
    context = context?Object(context):window
    context.fn = this
    let res
    if(args){
        res = context.fn(args)
    }else{
        res = context
    }
}
Copy the code

3. Implement event delegation

How event delegation works:

Event delegation is implemented using the bubbling principle of events. What is event bubbling? The event starts at the deepest node and then propagates the event up. For example, there is a node tree on the page, div>ul>li>a; For example, if we add a click event to the innermost DIV, the event will be executed layer by layer in the order of A > Li >ul>div. There is such a mechanism that if we add a click event to the outermost div, ul, Li, and A will all bubble up to the outermost div. So they all fire, and this is called event delegate, and they delegate their parent to execute the event.

implementation

<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>
Copy the code
window.onload = function(){
    var oUl = document.getElementById("ul1");
   oUl.onclick = function(){
        alert(123); }}Copy the code

Here with the parent ul do event processing, due to the principle of bubble, when li was click events will bubble to the ul, click event because ul, so events can trigger, of course, here when clicking the ul also triggers, so the question becomes, ‘if I want to make the effect of the event agency like directly to the node event effect to do, For example, it will only trigger if you click on Li.

The Event object provides an attribute called target, which returns the target node of the Event, and we become the Event source. That is, target can be represented as the DOM of the current Event operation, but not the DOM of the actual operation. Of course, this is compatible. IE uses event. SrcElement to retrieve the location of the current node, but does not know what the nodeName is. Here we use nodeName to retrieve the name of the node.

window.onload = function(){var oUl = document.getElementById("ul1"); oUl.onclick =function(ev){var ev = ev || window.event;vartarget = ev.target || ev.srcElement;if(target.nodeName.toLowerCase() == 'li'{alert ()123); Alert (target. InnerHTML); }}}Copy the code

4. Use setTimeOut to implement setInterval

function myInterval(fn,time){
    let interval=() = >{
      fn()
      setTimeout(interval,time)
    }
    setTimeout(interval,time)
  }
Copy the code

5. Implement map with JS

Array.prototype.map = function (fn) {
    let arr = []
    for (let i = 0; i < this.length; i++) {
        arr. push(fn(this[i], i, this))}return arr
}
Copy the code

6. Implement reduce method with JS

Array.prototype.myReduce = function (fn, initVal) {
    let res = initVal ? initVal : 0
    for (let i = 0; i < this.length; i++) {
        res = fn(res, this[i], i, this)}return res
}
Copy the code

7. Implement the filter method with JS

Array.prototype.myFilter = function (fn) {
    let arr = []
    for (let i = 0; i < this.length; i++) {
        if (fn(this[i], i, this)) {
            arr.push(this[i])
        }
    }
    return arr
}
Copy the code

8. JS implements push

Array.prototype.myPush = function () {
    let args = arguments
    for (let i = 0; i < args.length; i++) {
        this[this.length] = args[i]
    }
    return this.length
}
Copy the code

9. Implement POP

Array.prototype.pop = function () {
    if(this.length === 0) return
    let val = this[this.length - 1]
    this.length -= 1
    return val
}
Copy the code

Implement unshift

Array.prototype.unshift = function () {
    let args = [...arguments]
    let len = args.length
    for (let i = this.length - 1; i >= 0; i--) {
        this[i + len] = this[i]
    }
    for (let i = 0; i < len; i++) {
        this[i] = args[i]
    }
    return this.length
}
Copy the code

Implement Shift

Array.prototype.shift = function () {
    let removeVal = this[0]
    for (let i = 0; i < this.length; i++) {
        if(i ! = =this.length - 1) {
            this[i] = this[i + 1]}}this.length -= 1
    return removeVal
}
Copy the code

11,

var n=123
function f1(){
    console.log(n)
}
function f2(){
    var n=456
    f1()
}
f2()
console.log(n)// The result is 123 123
Copy the code

12,

var length=100
function f1(){
    console.log(this.length)
}
var obj={
    x:10.f2:function(f1){
       f1()
        arguments[0]()
    }
}
obj.f2(f1,1)
Copy the code

–proto– prototype and constructor

①__proto__ and constructor attributes are unique to objects; The prototype attribute is unique to functions. But since functions in JS are also objects, they also have __proto__ and constructor attributes

The __proto__ attribute points from an object to an object, that is, to its parent object. So what does this attribute do? If the __proto__ attribute does not exist in the object, then the __proto__ attribute refers to the object. If the __proto__ attribute does not exist in the object, then the __proto__ attribute refers to the object. The __proto__ attribute of the parent object points to the parent object. If it doesn’t find the parent object, the parent object’s __proto__ attribute points to the parent object. Down to the null Prototype property at the top of the prototype chain, remember that the second of the two things we mentioned earlier is that it’s unique to functions, that it points from a function to an object. F1.proto === Foo. Prototype f1.proto == Foo. Prototype F1.proto == Foo. So what does the Prototype property do? Its job is to contain properties and methods that can be shared by all instances of a particular type, so that objects instantiated by the function can find common properties and methods. When any function is created, the default is to create its prototype object as well. The constructor property is also owned by the object. It points from an object to a function, meaning that it points to the object’s constructor. Every object has a constructor (owned or inherited from it).

Implement new manually

function myNew(constrc, ... args) {
	const obj = {}; // 1. Create an empty object
	obj.__proto__ = constrc.prototype; // 2. Point obj's [[prototype]] property to the constructor's prototype object
	SetPrototypeOf (obj, constrc.prototype)
	const result = constrc.apply(obj, args); Bind this, the context of constrc execution, to obj and execute
	return result instanceof Object ? result : obj;  //4. If the constructor returns an object, the result of the constructor execution is used. Otherwise, the newly created object is returned
}

// Examples of usage:
function Person(name, age){
	this.name = name;
	this.age = age;
}
const person1 = myNew(Person, 'Tom'.20)
console.log(person1)  // Person {name: "Tom", age: 20}
Copy the code

15. Browser event loops

Task queue

All tasks can be divided into synchronous tasks and asynchronous tasks. Synchronous tasks, as the name implies, are immediately executed tasks. Generally, synchronous tasks are directly executed in the main thread. Asynchronous tasks are asynchronously executed tasks, such as Ajax network requests and setTimeout timing functions. Asynchronous tasks are coordinated through the mechanism of task Event Queue.

Synchronous and asynchronous tasks enter different execution environments. Synchronous tasks enter the main thread (main execution stack) and asynchronous tasks enter the Event Queue. If the task execution in the main thread is empty, the Event Queue will read the corresponding task and push the main thread to execute it. The repetition of this process is known as an Event Loop.

In the event cycle, each cycle operation is called TICK. Reading the specification, it can be seen that the task processing model of each tick is relatively complex, and its key steps can be summarized as follows:

  1. Select the oldest task from this tick and execute it if there is one.
  2. Check if Microtasks exist, and keep executing them until the Microtask Queue is cleared
  3. Update the render
  4. Repeat the steps for the main thread

What are microtasks? According to the specification, tasks are divided into two categories: Macro task and Micro task. After each Macro task is finished, all Micro tasks should be cleared. Tasks mentioned in the following articles are considered macro tasks.

(Macro) Task Description: Script, setTimeout, setInterval, I/O, UI interaction events, setImmediate(node.js)

Microtasks include: Promise, MutaionObserver, process.nexttick (node.js environment) (called before calling the task queue)

Apis such as setTimeout/Promise are task sources, and the specific execution tasks specified by them enter the task queue. Tasks from different task sources enter different task queues. Where setTimeout and setInterval are homologous.

console.log('script start');

setTimeout(function() {
  console.log('timeout1');
}, 10);

new Promise(resolve= > {
    console.log('promise1');
    resolve();
    setTimeout(() = > console.log('timeout2'), 10);
}).then(function() {
    console.log('then1')})console.log('script end');
Copy the code

First, the event loop starts from the macroTask queue. Initially, there is only one Scrip t(whole code) task in the macroTask queue. When a task source is encountered, the task is first distributed to the corresponding task queue. So, similar to the above example, console.log is encountered and script start is printed; Next, it encounters the setTimeout task source and dispatts it to the task queue, denoting timeout1. Then it encounters a promise, the code in the new Promise executes immediately, prints promise1, executes resolve, encounters setTimeout, dispatches it to the task queue, Call it timemout2, then distribute it to the microtask queue, call it then1; Then the macro task queue is checked. If the macro task queue is empty, the macro task queue is checked. Then the macro task queue is checked. Execute timeout1 and print timeout1. Then execute timeout2, which prints timeout2. At this point, all queues are cleared, and execution is complete. The output sequence is: script start, promise1, script end, then1, timeout1, timeout2

Strong cache and negotiation cache

Strong cache

What exactly is strong caching? Where is better? Actually strong means forced. When the browser requests a file, the server caches the file in the Respone header. The cache duration and cache type are controlled by the server: cache-control of respone Header. The common Settings are max-age public private no-cache no-store.

In HTTP/1.0 and HTTP/1.1, this field is different. In the early days, HTTP/1.0, Expires was used, whereas HTTP/1.1 used cache-control. Let’s look at Expires first.

Expires

Expires is a response header returned by the server that tells the browser to retrieve data directly from the cache before the expiration date, without having to request it again. Like this:

Expires: Wed, 22 Nov 2019 08:41:00GMT copy codeCopy the code

Indicates that the resource expires at 8:41 am on November 22, 2019. If the resource expires, a request must be sent to the server.

This seems fine and reasonable, but there is a potential pitfall: the server’s time and the browser’s time may not be the same, and the expiration date returned by the server may be inaccurate. So this approach was quickly abandoned in later versions of HTTP1.1.

Cache-Control

In HTTP1.1, a very critical field is adopted: cache-control. This field also exists in

The difference between Expires and Expires is that it doesn’t use a specific expiration date. Instead, it uses an expiration date to control the cache, and the corresponding field is max-age. Take this example:

Cache-Control:max-age=3600
Copy the code

This means that the cache is available within 3600 seconds, or one hour, after the response is returned.

Max-age indicates that the cache time is 315360000 seconds (10 years). Public indicates that it can be cached by browsers and proxy servers. Proxy servers are usually used by Nginx. Immutable means that a resource is never mutable, but it is not. It is set to immutable so that users do not call the server when refreshing the page! What you mean? That is, if you only set cahe-control:max-age=315360000,public this is a strong cache. Every time the user opens the page normally, the browser will determine whether the cache has expired, and if it has not, it will read the data from the cache. But there are some “smart” users will click on the top left corner of the browser refresh button to refresh the page, then even if the resource has not expired not so quickly (10 years), the browser will go directly to request to the server, this is the request of the additional cost, then go quite so the negotiation process of cache (as below). If cahe-control:max-age=315360000,public = immutable, the browser does not request service even if the user updates the page, the browser reads the cache directly from the local disk or memory and returns 200 status. See the red box above (from Memory Cache). This is what the Facebook team suggested to the IETF working group that developed the HTTP standard in 2015: They wanted HTTP to add a property field to the cache-Control response header to indicate that the resource would never expire, so that browsers would no longer have to make conditional requests for those resources.

Strong cache summary

  1. Cache-control: max-age= XXXX, public client and proxy server can cache this resource. If the client has a request for the resource within XXX seconds, it directly reads the cache,statu code:200, and sends an HTTP request to the server if the user refreshes the resource

  2. Cache-control: max-age= XXXX, private allows only the client to cache the resource. The proxy server does not cache the client directly reads the cache in XXX seconds,statu code:200

  3. Cache-control: max-age= XXXX, immutable The client reads the cache directly if it has requests for this resource within XXX seconds. Statu code:200 Does not send HTTP requests to the server even after the user flusher the cache

  4. Cache-control: no-cache Skips setting strong cache, but does not prevent setting negotiated cache; If you have a strong cache, you will only use the negotiation cache if the strong cache fails. If you set no-cache, you will not use the negotiation cache.

  5. Cache-control: no-store cache. This will cause the client and server to not cache, so there is no so-called strong cache, negotiation cache.

  6. S-maxage: This is similar to max-age, but the difference is that s-maxage is the cache time for the proxy server.

    Note that cache-control takes precedence when both Expires and Cache-Control are present.

Negotiation cache

The above mentioned strong caching is to set an expiration time for the resource. Each time the client requests the resource, it will check whether the expiration time is correct. The server is only asked if it has expired. So, strong caching is meant to be self-sustaining for clients. However, when the client requests the resource and finds that it is expired, it will request the server, and then the process of requesting the server can set up the negotiation cache. In this case, the negotiation cache requires interaction between the client and the server.

Etag: each file has a hash, which is unique to each file, just like when webpack is used, each resource will have this thing, for example, app.js will become app.c20abbde.js after package, add a unique hash, also to solve the cache problem.

Last-modified: Indicates the modification time of a file, accurate to the second

That is, eTag and Last-Modified from the Response header are returned with each request, and the next request is put in the Request header. The server compares the tag that you brought with it to determine if the resource has changed. If it changes, it returns the new resource directly and updates the etag and last-Modified labels of the corresponding Response header. If the resource has not been modified, then the eTAG, last-modified, and last-modified cache will be negotiated for each request from the client.

Send a request -> see if the resource is expired -> Expired -> Request server -> server comparison resource is really expired -> Not expired -> Return 304 status code -> client with the cache of the old resource.

This is a complete cache negotiation process.

Of course, when the server discovers that the resource is truly expired, it does the following:

Send a request -> See if the resource is expired -> Expired -> Request server -> Server compare whether the resource is really expired -> Expired -> Return 200 status code -> If the client receives the resource for the first time, Write down max-age, etag, last-Modified, and so on in its cache-control.

Therefore, the negotiation cache steps are summarized as follows:

When requesting a resource, the local ETAG of the resource is sent to the server for comparison with the latest resource. If the resource has not changed, 304 is returned and the browser reads the local cache. If the resource has changed, return 200 to return the latest resource.

Why eTag? You might think that using Last-Modified is enough to let the browser know if the local cached copy is new enough, so why etag? The introduction of eTag in HTTP1.1 (that is, eTAG was added to address the previous if-Modified defect) was primarily intended to address several difficult last-Modified problems:

  1. Some files may change periodically, but their contents do not change (just change the modification time). At this point we do not want the client to think that the file has been modified and get again;

  2. Some files are modified very frequently, such as if they are modified in seconds or less (say N times in 1s), and if-modified-since the granularity that can be checked is in seconds, so the modification cannot be determined (or the UNIX record MTIME is only accurate to seconds).

  3. Some servers do not know exactly when a file was last modified.

3. Location of cache

As mentioned earlier, we fetch resources directly from the cache when a strong cache hit or a negotiated cache returns 304. So where exactly do these resources cache?

There are four cache locations in the browser, in descending order of priority:

  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache

Service Worker

The Service Worker borrows from the idea of the Web Worker, that is, JS runs outside the main thread and cannot access the DOM directly because it is out of the browser’s window. That said, it still helps us with a lot of useful features, such as offline caching, push notifications, and network proxies. The offline Cache is the Service Worker Cache.

Service Worker is also an important implementation mechanism of PWA. We will introduce its details and features in PWA sharing later.

Memory Cache and Disk Cache

Memory Cache refers to the Memory Cache, which is the fastest in terms of efficiency. But it is the shortest in terms of lifetime, and when the rendering process is finished, the memory cache is gone.

A Disk Cache is a Cache stored on Disk. It is slower than a memory Cache in terms of access efficiency, but its advantages lie in storage capacity and storage duration. A little bit computer based should make a lot of sense, but I won’t expand it.

Ok, now the question is, since each has its pros and cons, how does the browser decide whether to put resources in memory or hard disk? The main strategies are as follows:

  • Large JS and CSS files will be directly thrown into the disk, otherwise thrown into the memory
  • When the memory usage is high, files are preferentially transferred to disks

Push Cache

Push caching, the last line of defense for browser caching. It is part of HTTP/2, and although it is not widely used today, it is becoming more and more widely used as HTTP/2 becomes more and more popular.

17. Implement deep copy

let deepCopy = (obj) = > {
    if(! objinstanceof Object) {
        throw new Error('not a object')}let newObj = Array.isArray(obj)? [] : {}for (let key in obj) {
        newObj[key] = obj[key] instanceof Object? deepObj(obj[key]):obj[key] }return newObj
}
Copy the code

NodeValue, value, and innerHTML

DOM has a total of 12 nodes, among which the common ones are:

1. Document node (document, a document can only have one document element (in HTML documents, it is))

2. Element nodes (div, P, etc.)

3. Attribute nodes (class, ID, SRC, etc.)

4. Text nodes (inserted in div, P, etc.)

5. Comment the node

NodeValue, which is the value of a node, where attribute nodes and text nodes have values and element nodes have no values.

InnerHTML returns all the child nodes of the node and their values as a string

Value is the value that gets the input tag value

19. Differences between arrow functions and ordinary functions

1. Arrow functions are all anonymous

2. The direction of this in the arrow function is different, which is determined when the function this is defined.

Arrow functions do not have arguments objects

20, MVVM

MVVM is short for model-view-viewModel. Model-view-viewmodel. Model refers to the data passed by the back end. View refers to the page seen. 【 Viewmodel 】 The core of the MVVM pattern, which is the bridge between view and Model. It has two directions: one is to convert the [model] into the [view], which is to convert the data passed by the back end into the page to be seen. This is done by data binding. The second is to transform [view] into [model], that is, to see the page into back-end data. This is done by DOM event listening. Both directions are implemented, which we call bi-directional data binding. Summary: Views and models cannot communicate directly under the MVVM framework. They communicate through the ViewModel. The ViewModel usually implements an observer, and when the data changes, the ViewModel can listen for changes in the data and notify the corresponding view for automatic updates. And when the user manipulates the view, the ViewModel can also listen for changes in the view. It then notifies the data that it has changed, effectively enabling two-way binding of the data. And views and ViewModels in MVVM can communicate with each other.