The problem the forerunner

  • canvasandsvgThe difference between? [HTML]
  • What are the optimizations for CSS to improve performance? “CSS”
  • What is the CSS preprocessor/post-processor? Why use them? “CSS”
  • newHow are operators created? 【 JS Basics 】
  • MapandObjectThe difference between? 【 JS Basics 】
  • WeakMapandMapWhat difference does it make? 【 JS Basics 】
  • How does Vue implement native method listening? 【 Vue 】
  • Currization function implementation
  • Ajax request implementation [handwritten code]
  • Output result (Promise related)
  • The KTH smallest element in a binary search tree

Knowledge comb

canvasandsvgThe difference between?

The Canvas API provides a way to draw graphics using JavaScript and HTML Canvas elements. It can be used for animation, game graphics, data visualization, image editing, and real-time video processing. The Canvas API focuses primarily on 2D graphics. The WebGL API, which also uses the canvas> element, is used to draw hardware-accelerated 2D and 3D graphics.

SVG, short for Scalable Vector Graphics, is a sub-language of W3C XML for marking Scalable Vector Graphics. SVG has been partially implemented in Firefox, Opera, Webkit, Internet Explorer and other browsers.

Both can be used to draw vector graphics. The difference lies in the different drawing methods. Canvas is drawn on Canvas based on scripts, but SVG is an XML standard language, that is, to draw graphics with labels, and it exists in BOM.

That is to say, SVG is based on XML elements for graphic descriptions, which are relatively independent of each other and support event processing. Due to being based on XML, DOM can be overused and rendering is slow.

Canvas, on the other hand, draws graphics based on pixels and uses WebGL to enable hardware acceleration so that rendering efficiency is relatively high. However, the whole Canvas is integrated, and if a display needs to be modified, the whole Canvas needs to be re-rendered.

SVG is suitable for drawing small images with better compatibility, while Canvas is suitable for drawing images with frequent changes.

Reference:

  • HTML 5 Canvas vs. SVG

What are the optimizations for CSS to improve performance?

  • Load performance
    • CSS compression: Reduces the CSS size and transmission bandwidth.
    • Try to use a single CSS style: for example, if you only need to change the top and bottom margins, only use itmargin-bottomandmargin-topMore efficient than unified usemarginIt’s faster.
    • To use less@importAnd uselinkBecause the former waits for the page to finish loading, while the latter is parallel loading.
  • Selector performance
    • Avoid using wildcards
    • Class selectors are preferred over element selectors
    • Use descendant selectors as little as possible, no more than three layers at most, and combine these elements with classes
    • There is no need to specify rules repeatedly for inheritable attributes, unless you do not want to use inherited values
  • Rendering performance
    • Use high performance attributes with caution, such as float and Location. Because page reflux generally requires recalculation of the exact location of these attributes
    • Minimize page backflow and redraw
    • When the attribute value is 0, no unit is added to reduce the number of operations
    • Small ICONS are loaded using picture Sprite or Base64 encoding
    • Don’t abuse web fonts, because Chinese websites may be unfamiliar with webFonts and need to download large volume of font data, which will block page rendering
  • maintainability
    • The style of the same property is extracted and usedclassTo unify control
    • Styles are separated from HTML for easy management

What is the CSS preprocessor/post-processor? Why use them?

Preprocessors such as LESS, Sass, or stylus, which are used to precompile SASS or LESS, increase the reusability of CSS code. Hierarchies, mixins, variables, loops, functions, and so on are handy for writing and developing UI components.

Postprocessors, such as postCss, usually process CSS according to the CSS specification in the finished stylesheet to make it more efficient. The most common approach is to add browser private prefixes to CSS properties to achieve cross-browser compatibility.

CSS preprocessor adds some programming features to CSS, without considering the compatibility of browsers, you can use variables, simple logic programs, functions and other basic performance in the programming language in CSS, which can make CSS more concise, increase adaptability and readability, maintainability and so on. Other CSS preprocessor languages: Sass (Scss), Less, Stylus, Turbine, Swithch CSS, CSS Cacheer, DT CSS.

Reasons for use:

  • The structure is clear and easy to expand
  • It’s easy to mask differences in browser private syntax
  • Multiple inheritance can be easily implemented
  • It’s perfectly compatibleCSSCode that can be applied to older projects

How is the new operator created?

The new operator creates an instance of an object that has a constructor.

The new keyword performs the following operations:

  1. Create an empty object

  2. Link the constructor of the object to the constructor itself, which is also equal to the constructor on the constructor prototype. Such as ({}) constructor = = = Object. The prototype. The constructor to true as a result, at the same time Object. The prototype. The constructor = = = Objec is equal to itself.

    This step can also be interpreted as having the object inherit the properties of the constructor prototype object

  3. Call the constructor with the new object as this context

  4. If the function returns no value, the new object is returned, and if it does, the value is returned directly

We can implement this ourselves:

/** * the new operator implements *@param {Function} Constructor *@param  {... any} The args parameter * /
function instanceFactory(constructor, ... args) {
    Use the constructor prototype to create a new object
    const instance = Object.create(constructor.prototype);
    // # use the new object as this context
    const res = constructor.apply(instance, args);
    return (typeof res === 'function' || typeof res === 'object')? res : instance; }Copy the code

What is the difference between Map and Object?

The Map object holds key-value pairs and can remember the original insertion order of the keys. Any value (object or raw value) can be a key or a value.

Similarly, they all allow you to press a key to access a value, delete a key, and detect if a key is bound to a value. So (and there’s no built-in alternative) we’ve always used objects as Maps. However, Maps and Objects have some important differences, and Map is a better choice in the following situations:

Map Object
The accident of key MapBy default, there are no keys. Contains only explicitly inserted keys. aObjectThere is a prototype, and the key name on the prototype chain may conflict with the key name you set on the object yourself.Note:Although the ES5 works at firstObject.create(null)To create an object without a prototype, but this is not very common.
The type of the key aMapSigma could be PIAny value, including functions, objects, or any primitive type. aObjectTheta has to be a keyStringorSymbol.
The order of the keys MapKey is ordered in. So, when you iterate, oneMapObject returns key values in the order they are inserted. aObjectThe key is unordered note: objects since the ECMAScript 2015 specificationindeedPreserves the creation order of the string and Symbol key; Therefore, iterating over an object with only string keys produces keys in insertion order.
Size MapThe number of key-value pairs can be easily passedsizeProperties for ObjectThe number of key-value pairs can only be calculated manually
The iteration Map 是 可迭代, so it can be iterated directly. One iterationObjectYou need to get its keys somehow before you can iterate.
performance This is better when key pairs are frequently added or deleted. Optimizations are not made in cases where key/value pairs are frequently added and removed.

In contrast, map is more like a real mapping table than Object and supports more functions.

Reference:

  • map – MDN

What is the difference between Map and WeakMap?

A WeakMap object is a set of key/value pairs where the keys are weakly referenced. The key must be an object, and the value can be arbitrary. The key of WeakMap can only be Object.

new WeakMap([iterable])

Iterable is an array (a binary array) or other Iterable object whose elements are key-value pairs. Each key value pair is added to the new WeakMap. Null will be treated as undefined.

In general, weekMaps and maps are not much different except that the key must be an object (that is, reference type data).

Why weak references? Let’s look at a phenomenon:

let obj = {
    name: 'Jinx'.age: 23};const map = new Map(a); map.set(obj,true);
obj = null;
console.log(obj)
Copy the code

When we set obj=null, the previously set strong reference is removed. Normally, the OBJ object would be garbage collected if it was not referenced, but since the map key references the object, it cannot be reclaimed. However, if we replace this object with a WeakMap, the key reference here is a weak type, and when garbage collection occurs, the object will be reclaimed, and the key value in the weak reference object will disappear.

In contrast, the native WeakMap holds a “weak reference” to each key object, meaning garbage collection works correctly when no other reference exists. The structure of the native WeakMap is special and efficient, and the key it uses for mapping is valid only if it is not reclaimed.

Because of such weak references, WeakMap keys are not enumerable (there is no way to give all keys). If keys were enumerable, their lists would be subject to garbage collection, resulting in inconclusive results. Therefore, if you want a list of keys for objects of this type, you should use Map.

Why to introduce WeakMap can be seen in this official explanation: Why WeakMap? .

Reference:

  • WeakMap
  • Characteristics and application scenarios of WeakMap

How to implement listening on array native methods in Vue?

In Vue, responsivity processing uses Object.defineProperty to intercept data, but this method can not listen to the internal change of array, the change of array length, the change of array interception, etc., so we need to hack these operations, that is, rewrite the native method, add responsivity processing logic. Let Vue listen for changes.

// Cache array prototype
const arrayProto = Array.prototype;
Arraymethods. __proto__ === array. prototype
export const arrayMethods = Object.create(arrayProto);
// A way to extend functionality is needed
const methodsToPatch = [
  "push"."pop"."shift"."unshift"."splice"."sort"."reverse"
];
/** * Intercept mutating methods and emit events */
methodsToPatch.forEach(function(method) {
  // Cache the native array method
  const original = arrayProto[method];
  def(arrayMethods, method, function mutator(. args) {
    // Perform and cache the native array function
    const result = original.apply(this, args);
    // Reactive processing
    const ob = this.__ob__;
    let inserted;
    switch (method) {
    // Push and unshift add indexes, so manually observer
      case "push":
      case "unshift":
        inserted = args;
        break;
      // the splice method, if passed a third parameter, will also have an index added, also manual observer.
      case "splice":
        inserted = args.slice(2);
        break;
    }
    // 
    if (inserted) ob.observeArray(inserted);// Gets the inserted value and sets reactive listening
    // notify change
    ob.dep.notify();// Notifications depend on updates
    // Returns the result of the execution of the native array method
    return result;
  });
});
Copy the code

Currization function implementation

In computer science, Currying is a technique that converts a function that takes multiple arguments into a function that takes a single argument (the first argument of the original function), and returns a new function that takes the remaining arguments and returns a result. Function coriolization is the basic concept of functional programming.

The key to corritization is to determine the number of arguments passed in. If it is greater than or equal to the number of function arguments, the function is called; otherwise, the passed arguments are cached and a function is returned that receives the rest.

/** * the function is currified@param {Function} fn 
 * @param  {... any} arg 
 * @returns * * /
function curry(fn, ... arg) {
    return arg.length >= fn.length ? fn.apply(this, arg) : curry.bind(this, fn, ... arg); }Copy the code

Implementation of Ajax requests

AJAX is an acronym for Asynchronous JavaScript and XML, which refers to the Asynchronous communication of JavaScript to fetch data from an XML document from a server and then update the corresponding portion of the current web page without refreshing the entire page.

Ajax is not a technology, but a term, a collection of existing technologies: asynchronous, JavaScript, XML, and XMLHTTPRequest, with the XMLHTTPRequest object at its core. The XMLHTTPRequest object is used to communicate with the server, and the key thing is that the communication is asynchronous, so the page doesn’t have to wait for the result of the communication, and the content of the communication is the content of the communication that we need, XML and so on. In fact, JSON is one of the most common and most common forms of Ajax interaction data, and when you get that data asynchronously, you update the page, The user doesn’t have to wait for this process and the interactive experience is good.

To sum up:

  1. XMLHTTPRequest makes an asynchronous request like a server: usesopenAfter the request parameters are set, thesendInitiate a request.
  2. The browser main thread continues to perform other tasks
  3. The server returns the request data,onreadystatechange The function receives the state change information and performs the request callback
  4. If the request fails, run theonerror The function can listen for the corresponding exception information

Using this logic we can write Ajax request logic:

const url = '/server';
const xhr = new XMLHTTPRequest();
// Initialize the request information: set the request method, request path, and whether asynchronous
xhr.open('GET', url, true);
// Set the response type and request header information
xhr.responseType = 'json';
xhr.setRequestHeader("Accept"."application/json");
// Listen for state changes
xhr.onreadystatechange = function(){
    if(this.readSate ! = =4) {return;  
    }
    if (this.status === 200) {
    	console.log('responseData:'.this.response);
  	} else {
    	console.error(this.statusText); }}// Set the request failure callback
xhr.onerror = function() {
   console.error(this.statusText);
};
// Send an Http request
xhr.send(null);
Copy the code

By extracting these variables, you can encapsulate more convenient Ajax functions with Promise objects.

Reference:

  • Ajax – MDN
  • Ajax working principle and implementation steps – Zhihu

Output results (Promise related)

Code snippet:

Promise.resolve('1') // Promise1
  .then(res= > {
    console.log(res)
  })
  .finally(() = > {
    console.log('finally')})Promise.resolve('2') // Promise2
  .finally(() = > {
    console.log('finally2')
  	return 'I'm the value returned by finally2'
  })
  .then(res= > {
    console.log('Then function after Finally2', res)
  })
Copy the code

The execution logic of promise.finally is called regardless of whether the state of the Promise is successful or not, and the new Promise object is returned that inherits the state and value of the previous Promise.

  1. Promise1 ofthenThe callback joins the microtask queue
  2. The Promise2finallyThe callback joins the microtask queue
  3. Perform microtasks: Print callbacks for step 1 or 2
  4. Promise1. ThenfinallyThe callback joins the microtask queue
  5. Promise2. FinallythenThe callback joins the microtask queue
  6. Perform microtasks: print callbacks of 4,5
Finally2 Finally finally2 then function 2Copy the code

Code snippet:

Promise.resolve('1')
  .finally(() = > {
    console.log('finally1')
    throw new Error('I'm an exception thrown in finally')
  })
  .then(res= > {
    console.log('Then function after finally', res)
  })
  .catch(err= > {
    console.log('Catch error', err)
  })
Copy the code

The Promise. Finally callback does not inherit the state and value of the previous Promise, but returns a rejected Promise.

Finally1 caught an errorError: I am afinallyError thrown byCopy the code

The KTH smallest element in the binary search tree

Given the root node of a binary search tree, root, and an integer k, design an algorithm to find the KTH smallest element (counting from 1). Input: root = [3,1,4,null,2], k = 1 output: 1 input: root = [5,3,6,2,4,null,null,1], k = 3Copy the code

Look for the KTH largest element, and the general way to do that is to sort it out, and then you get the answer straight away. In this case, we can use the optimized sorting algorithm, can not complete sorting end, can get the answer, such as: fast sorting (dichotomy), heap sort (using the size of the root heap).

Tree traversal has two kinds of depth first and breadth first, but the tree in this question is not ordinary tree, is a binary search tree (BST), BST is a tree that satisfies this property:

  1. All nodes in the left subtree are smaller than the root node
  2. All nodes in the subtree are larger than the root node
  3. The left and right subtrees are also BST trees

Note: Unlike the heap structure, the heap structure is a complete binary tree, and the nodes of the heap are compared to the left and right children, not to all the nodes of the left and right subtrees.

One of the things about this tree structure is that middle traversal is an ascending sort, so when we do middle traversal we get an ascending array.

Sequential traversal in recursion:

let kthSmallest = function(root, k) {
    let res = null
    let inOrderTraverseNode = function(node) {
        if(node ! = =null && k > 0) {
            // Iterate through the left subtree
            inOrderTraverseNode(node.left)
            // Then the root node
            if(--k === 0) {
                res = node.val
                return
            }
            // Walk through the right subtree again
            inOrderTraverseNode(node.right)
        }
    }
    inOrderTraverseNode(root)
    return res
}
Copy the code

Recursion involves traversing the entire tree, and in fact, traversing is an ascending process, and when the length of the ascending array becomes K, this is actually the number we need, and everything that follows is larger than this number, so we don’t need to continue traversing.

Sequential traversal in iteration:

let kthSmallest = function(root, k) {
    let stack = []
    let node = root
    
    while(node || stack.length) {
        // Iterate over the left subtree
        while(node) {
            stack.push(node)
            node = node.left
        }
      
        node = stack.pop()
        if(--k === 0) {
            return node.val
        }
        node = node.right
    }
    return null
}
Copy the code