Understanding and Use of Class Array

Keywords: Attribute JSON Javascript IE

First, what are class arrays?

1 Definition:

JavaScript typed arrays are array-like objects and provide a mechanism for accessing raw binary data.

2 Specific manifestations:

     var arr = ['Hello', 'json', 'array']
     var obj = {
        0: 'Hello',
        1: 'json',
        2: 'array',
        length: 3
     }

It is not difficult to see from the above code that obj also has a length attribute, and access attributes can also be accessed by obj[0], so that objects can be called class arrays. But it's not data, because it can't use array push and other methods. But we can also call it through the following methods.

   arr.push('New elements');
   [].push.call(obj, 'New elements');
   console.log(arr, obj)

Note that obj's length attribute automatically becomes 4. Class arrays can use the array api. So what are the benefits of using class arrays? Why bother to write an array of classes and then call it in the way above? Then look at the following...

Benefits of Class II arrays?

Look at the printed results of the following code series

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="myDiv1" class="box1">Hello</div>
    <span id="mySpan1" class="box1 box2">Singleton pattern</span>
    <div id="myDiv2" class="box1 box2 box3"></div>
    <a id="myA1" class=""></a>

</body>
    <script>
        console.log(myDiv1.classList);
        console.log(mySpan1.classList);
        console.log(myDiv2.classList);

    </script>
</html>

The element itself has a classList attribute, which can be seen clearly on the results stored. This attribute is in the form of an array of classes.

In addition, there are some APIs on this property.

    <script>
        console.log(myDiv1.classList);
        console.log(mySpan1.classList);
        console.log(myDiv2.classList);
        var div1 = myDiv1.classList;
        var span1 = mySpan1.classList;
        var div2 = myDiv2.classList;
        div1.add('box2');
        span1.remove('box2'); // Add the class name
        div2.toggle('box3'); // Delete the class name
        span1.toggle('box3'); // Add the class name if you have it, and delete the class name if you have nothing.
    </script>

Look what happened to our body?

As you can see, the operation of classList is the operation of classes, we can add classes, delete them. Or delete if you have, add if you don't. What does classList have to do with classes? Because classList attributes are not compatible with IE10 browsers, with problems, let's do the following compatibility, write a classList attributes, expand some of the required api s.

Let's first look at what elements are.

console.log(myDiv1.constructor)
console.log(mySpan1.constructor)
console.log(document.body.constructor)

It can be said that all tags are created by constructors, and tag elements are instances of their own constructors. So how do we add an attribute to all elements?

We can't add this attribute to the constructors of all elements. There are many kinds of element labels, which is obviously unscientific. Let's do an experiment.

HTMLElement.prototype.say = function() {
   console.log(this + 'Called me')
}
myDiv1.say() // [object HTML DivElement] called me
mySpan1.say() // [object HTML SpanElement] called me
document.body.say() // [object HTML Body Element] called me

Add a method to the object prototype of HTMLElement, then each element calls the method, and finds that the function executes. We can speculate boldly that the constructor of each element may inherit the constructor of HTMLElement. So the function can be executed. We seem to have found their common parent so that we can operate on HTMLElement.

But the question is, we can add sharing methods to the prototype, so can we add sharing attributes to the prototype?

HTMLElement.prototype.newClassList = []
myDiv1.newClassList.push('classA')
console.log(myDiv1.newClassList); // ["classA"]
console.log(mySpan1.newClassList);// ["classA"]

We add an element to the new ClassList attribute of div1, and the new ClassList attribute on span will also change, so the attribute cannot be shared. Let's add a listening attribute to HTMLElement.prototype

        Object.defineProperty(HTMLElement.prototype, 'newClassList', {
            get: function() {
                console.log(this); // html element
                if(!this._dtl_) {
                    this._dtl_ = this.className.split(' ');
                }
                return this._dtl_
            },
        })
        var div1 = myDiv1.newClassList;
        var span1 = mySpan1.newClassList;
        var div2 = myDiv2.newClassList;
        console.log(div1) // 
        console.log(span1)
        console.log(div2)

Here is the print result of this.

Here is the print result of div1 span 1 div2

Here we locate each html element, where this points to the html element that calls it, that is, who accesses this attribute, this points to whom. Now we can operate on this. Now our newClassList property is still an array. Since this property is not only an attribute, but also has some methods above, how can we add methods to an array? Add to the prototype of the array? In this way, we will pollute the prototype of the array. We only need these APIs (add, remove, etc.) on the newClassList array, so it must be unreasonable for us to add them to the prototype of the array. At this point, we need to use our class array.

 function myDOMTokenList(dom) {

        }
        myDOMTokenList.prototype = {
            constructor: myDOMTokenList,
            contains: function(val) {
            },
            add: function(val) {
                
            },
            remove: function(val) {
                
            },
            toggle: function(val) {
               
            }
        }
        Object.defineProperty(HTMLElement.prototype, 'newClassList', {
            get: function() {
                console.log(this); // html element
                if(!this._dtl_) {
                    this._dtl_ = this.className.split(' ');
                    console.log(this._dtl_.__proto__); // Here we point to the prototype of Array.
                    // Reconstructing Prototype Orientation
                    this._dtl_.__proto__ = new myDOMTokenList(this);
                    console.log(this._dtl_.__proto__) // myDOMTokenList {}
                    console.log(this._dtl_.add === this._dtl_.__proto__.__proto__.add) // true
                }
                return this._dtl_
            },
        })

Define a constructor, add some methods to the prototype, and then change the prototype pointing of this._dtl_that is, the new ClassList to point to an instance of the myDOMTokenList constructor, so that we can turn the array into an array of classes. Then we will improve these methods.

     function myDOMTokenList(dom) {
             // this is each instance, myDiv1.newClassList. dom is an html element
            console.log(this, dom)
            Object.defineProperty(this, 'value', {
                set: function(val) {
                    dom.className = val;
                }
            })

        }
        myDOMTokenList.prototype = {
            constructor: myDOMTokenList,
            contains: function(val) {
                for(var i=0; i< this.elngth; i++) {
                  return this[i] === val
                }
            },
            add: function(val) {
                [].push.call(this, val);
                this.value = [].join.call(this, ' ');
                return this;
            },
            remove: function(val) {
                for(var i=0; i<this.elngth; i++) {
                    if(this[i] === val) {
                        [].slice.call(this, i, 1)
                    }
                }
                return this;
            },
            toggle: function(val) {
                this.contains(val) ? this.remove(val) : 
                this.add(val);
                return this;
            }
        }
     

The parameter in the myDOMTokenList constructor is the dom element, and this points to each instance, myDiv1.newClassList, mySpan1.newClassList.

Add a listener attribute value to this to change DOM when value changes. Note the data flow. The data is generated in dom, and the method parameters on the prototype are passed in. After changing, they enter into dom.

 

Posted by tomdude48 on Sat, 27 Jul 2019 01:58:17 -0700