The problem the forerunner

  • What is the difference between defer and async properties in the Script tag? [HTML]

  • How can single-line/multi-line text overflow be handled? “CSS”

  • The difference between undefined and null? Why is the result of typeof null object? [js]

  • Vue Bidirectional binding principle

  • The KTH largest element in the array

    Finds the KTH largest element in the unsorted array. Note that you need to find the KTH largest element of the sorted array, not the KTH distinct element.Copy the code

Knowledge comb

What is the difference between defer and async properties in the Script tag?

The script tag is generally used to load JS scripts. As we know, JS scripts are loaded and executed blocking, that is, page parsing is suspended when the page is parsed to the script tag, and then page parsing is resumed after the script is loaded and executed.

The two new attributes of H5: defer and async can make the script load asynchronously, but the execution mode of the script is different. Defer means delayed, so the script will be executed after the page loads, while async means asynchronous, so the script will only load asynchronously and be executed immediately.

Page loading, script loading, and script execution are shown as follows:

To summarize, defer and the async attributes allow the Script tag to load asynchronously, but async executes immediately, whereas defer defer execution until the page has finished loading.

It is worth noting that async takes precedence when both properties exist together.

Keywords: HTML, page script loading and execution

How are single-line and multi-line text overflows handled?

The most common way to overflow text is to replace it with an ellipsis. The text overflow property is text-overflow and has three optional values:

  • Clip: Default value, clipping the overflow text, that is, the overflow text will be hidden
  • Ellipsis: Ellipsis, ellipsis, ellipsis, ellipsis, ellipsis, ellipsis, ellipsis, ellipsis
  • String: An attribute in the experiment that can overflow text with the specified character special

In addition, overflow text generally requires two attributes to work properly,

  • Overflow: Overflow processing, available values arevisible,hidden,scrollandauto. In general, overflow text needs to be hidden in order to ensure that it is correctly replaced with an ellipsis.
  • white-space: white space processing, similarly, to ensure that overflow text does not display, need to be set to no line breaks, i.enowrapValue.

Multiline text sometimes needs to overflow to ellipsis, but wire-space doesn’t work for multiline text. To do this, we need to use a few additional properties:

text-overflow: ellpsis;
overflow: hidden;

/** The display mode is set to box, and the children are arranged vertically */
display: -webkit-box;    
-webkit-box-orient: vertical;
/** The number of lines to display */
-webkit-line-clamp: 3;
Copy the code

Since Display: Box, Box-Orient, and Line-clamp are all experimental properties that are not supported by some browsers, there are compatibility issues.

Example:

<div id="app" style="width: 200px; border: solid 1px #acc; display: -webkit-box; -webkit-box-orient: vertical; text-overflow: ellipsis; -webkit-line-clamp: 3; overflow: hidden;">
    <p>This is a line of text, this is a line of text, this is a line of text, this is a line of text, this is a line of text,</p>
    <p>This is a line of text, this is a line of text, this is a line of text, this is a line of text, this is a line of text,</p>
    <p>This is a line of text, this is a line of text, this is a line of text, this is a line of text, this is a line of text,</p>
</div>
Copy the code

To sum up, text overflow processing requires the use of text-ellipsis property, which is usually set to Ellipsis, but text overflow property only takes effect when “text overflow occurs”, corresponding to a single line of text. Make single lines of text overflow with Overflow: Hidden and white-space:nowarp.

For multi-line text, you need to set the parent element to box layout and the child element arrangement to box-orient and vertical. Finally, you need to set the number of lines displayed to line-clamp so that the following lines displayed are replaced with ellipses.

CSS basics, text overflow

The difference between undefined and null?typeof nullWhy is the result ofobject?

First, by definition, undefined is an uninitialized variable and NULL is an empty object. Although both are basic data types, they are essentially different data types.

This can also be seen from the fact that typeof NULL is object. Essentially, null is stored differently than undefined.

In the first version of JS design, 32 bits were used as the storage unit and the lower three bits (1-3 bits) were used to indicate the type of the value:

  • 000: Object. The following bits are used to store references to objects, while the last 31 bits of NULL are all zeros, indicating no references, or empty objects.

  • 1: indicates an int. Subsequent bits store a 31 – bit signed integer.

  • 010: double. Subsequent bits store a double – precision floating – point number.

  • 100: string.

  • 110: Boolean value.

    Undefined is represented by the integer -2^30, which means it takes 32 bits to represent the number, which is outside the scope of an int. (in spite of this, I still don’t know, this is what the difference between undefined and null, because – 2 ^ 30 with a 32-bit binary representation of 11000000000000000000000000000000, the same low three to 000, if only in accordance with the lower three as the judgment standard, Undefined is also defined as object. If the number is equal to -2^30, then return undefined. If the number is not equal, then use the lower three digits to determine the data type.

For more details: The History of “Typeof Null”

Keyword: JS data type

Vue bidirectional binding implementation principle

The bidirectional binding principle of Vue is simply to detect data changes when they occur and then respond to them. The getters and setters of object.defineProperty in JS are used to listen to read data, and Vue is also based on these two apis to realize data listening, and then realize instant response.

The implementation of bidirectional binding has two processes:

  • Data hijacking (Observer) : A definition of a data listener that uses getters and setters of Object.defineProperty to implement data hijacking (Vue3.0 already uses Proxy Proxy objects to implement data hijacking).
  • View update logic: When data changes, it is heard by the Observer as an Observer and sends a message to the Dep, who acts as a broker and sends the message to all subscribers, who then trigger the view update: re-render, so data changes can trigger the view update.
  • Bidirectional binding: In other words, the view changes can also update the data. The view is Dom, so we can monitor the view changes through the events of the native Dom, and then trigger the data changes, and the changes of the data changes cause the changes of the view layer, so as to achieve the effect of bidirectional binding.

Key words: Vue foundation

The KTH largest element in the array

Finds the KTH largest element in the unsorted array. Note that you need to find the KTH largest element of the sorted array, not the KTH distinct element.

So it’s pretty clear that if you want to find the KTH largest element, the easiest way to do that is to sort it, and then you can locate the KTH largest element by subscript. This can be done, but we need to think about whether there is room for optimization.

They’re asking us to find the KTH largest number, and in fact, if we can identify the KTH largest element without having to completely sort it, we don’t have to waste any more sorting.

There are two main sorting algorithms: fast sorting and heap sorting.

Quicksort, or quicksort algorithm, uses the idea of divide and conquer,

  • Select a cardinality from the sequence
  • Put the smaller number on the left and the larger number on the right
  • Repeat the above steps for the left and right intervals until there is only one interval, and the sorting is finished
/** * find the KTH number * in the sequence@param {number[]} nums 
 * @param {number} k 
 * @returns {number}* /
function findKthLargest(nums, k) {
    return quickSort(nums)[nums.length - k];
};

/** * quicksort *@param {number[]} Nums Array to be sorted *@param {number? } The left pointer of the left interval *@param {number? } The right pointer of the right interval is *@returns {number[]}* /
function quickSort(nums, left, right) {
    if(Object.is(left, undefined)) {
        left = 0;
    }
    if(Object.is(right, undefined)) {
        right = nums.length - 1;
    }
    if(left >= right) {
        return nums;
    }
    let baseIndex = left; // Base pointer
    const base = nums[baseIndex];
    for(leti = left; i < right +1; i++) {
        // Put it to the left of the base. The base is "squeezed" one bit to the right
        if(nums[i] < base) {
            / / exchange
            nums[baseIndex] = nums[i]; // less
            baseIndex++;
            nums[i] = nums[baseIndex]; // more
            nums[baseIndex] = base; // base
        }
        // Greater than the cardinality itself is on the right side, no need to move
    }
    quickSort(nums, left, baseIndex-1);
    quickSort(nums, baseIndex+1, right);
    return nums;
}
Copy the code

There are a number of different place exchange scheme, calendar, using the above once swept from left to right, with smaller than base on the left, it is important to note that since it is put forward, need to base and right interval number ward, right interval (one-way only need to move the base to the front right interval (base behind the number) moved to the right range (traverse pointer) finally end, We can move the base one place.

In addition, there is a collision two-pointer method, the left pointer refers to the left of the left interval, the right pointer points to the left of the right interval. So we start with left and right Pointers on the left and right sides of the array range. First, you start iterating from the left, you need to find the larger value, you need to find where you want to move to the right, that is, to the right pointer, and at the same time, you need to adjust the base position to the left pointer. And then you start walking the right hand side, looking for smaller values, finding where you want to put it on the left, which is the I pointer, and at the same time, adjusting the base position to the right. Repeat until the left and right Pointers collide, indicating that both sides are found.

function quickSort(nums, left, right) {
    if(Object.is(left, undefined)) {
        left = 0;
    }
    if(Object.is(right, undefined)) {
        right = nums.length - 1;
    }
    if(left >= right) {
        return nums;
    }
    let i = left,
        j = right;
    const base = nums[j];
    while(i < j) {
        // Look for values on the left that are larger than the cardinality
        while(i < j && nums[i] <= base) {
            i++;
        }
        nums[j] = nums[i];
        nums[i] = base;
        // Look for values on the right that are smaller than the cardinality
        while(j > i && nums[j] >= base) {
            j--;
        }
        nums[i] = nums[j];
        nums[j] = base;
    }
    quickSort(nums, left, j-1);
    quickSort(nums, j+1, right);
    return nums;
}
Copy the code

Fast selection based on quicksort

We know that quicksort is divide-and-conquer, step by step, and one thing that’s clear is the position of the cardinality. And every time we do that, we can figure out where the base is, if we’re to the left of K, we just need to sort through the left interval, if we’re to the right, we just need to sort through the right interval, until the base is the number we’re looking for.

In fact, we only need to modify the quicksort function slightly:

/** * Fast lookup based on quicksort *@param {number[]} Nums Array to be sorted *@param {number} k
 * @param {number? } The left pointer of the left interval *@param {number? } The right pointer of the right interval is *@returns {number}* /
function findKthLargest(nums, k, left, right) {
    if(Object.is(left, undefined)) {
        left = 0;
    }
    if(Object.is(right, undefined)) {
        right = nums.length - 1;
    }
    if(left >= right) {
        return nums[right];
    }
    let i = left,
        j = right;
    const base = nums[j];
    while(i < j) {
        // Look for values on the left that are larger than the cardinality
        while(i < j && nums[i] <= base) {
            i++;
        }
        nums[j] = nums[i];
        nums[i] = base;
        // Look for values on the right that are smaller than the cardinality
        while(j > i && nums[j] >= base) {
            j--;
        }
        nums[i] = nums[j];
        nums[j] = base;
    }
    const d = j - (nums.length - k);
    if(d == 0) {
        return base;
    } else if(d > 0) {
        return findKthLargest(nums, k, left, j-1);
    } else {
        return findKthLargest(nums, k, j+1, right); }}Copy the code

Because of the K value information and the characteristics of fast sorting, we only need to fast sort the left interval or the right interval to find the answer, rather than the entire array is completely sorted.

Heap sort

Heap sort takes advantage of the heap data structure:

A heap is a complete binary tree with the property that each node has values greater than or equal to the values of its left and right children, called a big top heap (big root heap). Or if the value of each node is less than or equal to the value of its left and right children, it is called the small top heap (the small root heap).

According to the characteristics of the heap, we know that the large root heap guarantees the maximum root element, and the small root heap guarantees the minimum root element, so we keep building the heap structure and shrinking the size of the heap, and when the size of the heap is 1, the sorting ends. That’s the logic of heap sort.

  • Build an unordered sequence into a large (ascending) or small (descending) root heap
  • Place the top of the heap element at the end of the sequence
  • Repeat the above steps until the sequence length is 1, ending the sorting process.

See heap sort a little bit of bubble sort, looking for the maximum, and then save, at the same time narrowing down the search sequence. However, heap sort, like quicksort, is only O(nlogn). This is because of the optimization effect of the heap structure: when you build the heap the first time, you only adjust the changes brought about by the first swap, without rebuilding the heap as you did in the first step. Reconstruction heap and adjust the heap is a big difference: reconstruction of chaotic sequence, need to start from the last leaf node adjustment, adjust from down to up, but after the adjustment of the pile due to changed only the root element, and other non leaf node is the heap structure, so only need to adjust down on, until not a leaf node into a heap of structure.

In fact, heap sort can also be in place, because it is a complete binary tree, the relationship between non-leaf nodes and left and right children is very clear, without the use of redundant heap structure.

/** * find the KTH number * in the sequence@param {number[]} nums 
 * @param {number} k 
 * @returns {number}* /
function findKthLargest(nums, k) {
    // 1. Build a large root heap
    let level = nums.length;
    buildHeap(nums, level);
    // 2. Swap the first and last elements to shrink the heap level and maintain the heap, repeating Step 2 until the heap level is 1
    while(level > 1) {
        // Swap heads and tails
        level--;
        const root = nums[0];
        nums[0] = nums[level];
        nums[level] = root;
        // Re-maintain the heap
        adjustHeap(nums, level, 0);
    }
    // return the KTH largest number
    return nums[nums.length - k];
};

/** * Build a large root heap *@param {number[]} * nums sequence@param {number} Level build level | | length range: [0 ~ * / level)
function buildHeap(nums, level) {
    // Node (I) => (left child)2* I + 1, (right child)2* I + 2
    // There is at least one left child => 2* I + 1 <= len-1 => I <= len/ 2-1
    const lastNodeIndex = Math.ceil(level/2 - 1);
    for(let i = lastNodeIndex; i >= 0; i--) { adjustHeap(nums, level, i); }}/** * Adjust the large root heap *@param {number[]} * nums sequence@param {number} Level build level | | length range: [0 ~ * level)@param {number} I subscript the current node */
function adjustHeap(nums, level, i) {
    const lastNodeIndex = Math.ceil(level/2 - 1);
    if(i <= lastNodeIndex) {
        let nodeVal = nums[i];
        // Swap the maximum value of the child node
        const left = 2 * i + 1;
        const right = 2 * i + 2;
        let sweapIndex = i; // Swap the coordinates of the nodes
        if(left < level && nums[left] > nodeVal) {
            sweapIndex = left;
            nodeVal = nums[left];
        }
        if(right < level && nums[right] > nodeVal) {
            sweapIndex = right;
            nodeVal = nums[right];
        }
        / / exchange
        if(sweapIndex ! = i) { nums[sweapIndex] = nums[i]; nums[i] = nodeVal; adjustHeap(nums, level, sweapIndex); }}}Copy the code