Uncover the data and NodeValue properties in the DOM and change those things.

Source: Internet
Author: User
Tags hasownproperty

Issue raised:
Recently, in collating some of the Dom family's knowledge points, there are some things that I can't think of in some of the interface APIs of DOM. Just give an example: the text node in the DOM document model can access the textual content of the text node through the NodeValue or data property, and the NodeValue is updated immediately when the data is updated, and vice versa. Not only does data or nodevaulue have this interaction, but other types of nodes, such as the change, Document.title also change the title of the page. Trying to figure out how this was done in the JS engine, the following analysis was made:
Inheriting relationships through the prototype chain of a text node textnode.__proto__->text.prototype->characterdata.prototype->node.prototype-> Eventtarget.prototype->object.prototype. And the prototype exists on the properties and methods well understood text node calls a method or property from (inherited) where

But look at the code below.

var text=document.createtextnode (' text '); Text.hasownproperty (' data '); // falsetext.hasownproperty (' nodevalue '); // falseText.data; // "text"Text.nodevalue; // "Text"

Returning false is normal because data is a property on Characterdata.prototype, NodeValue is a property on Node.prototype. Text only inherits access to these two properties through the prototype chain. When I change the content,

 text.data= "New text" ;text.hasownproperty ( ' data '); //  Text.nodevalue; //  Text.data; //   

I wonder why I've added the Data property to the text object, but still return false??
Confusion: It is supposed that the property of the same name on the object itself is added and the prototype is accessed only by accessing the data on its own property. Even if the JS engine finally forced the delete text.data, when Text.data tried to access the data by the prototype chain to find, on the Characterdata.prototype data on the found value of "new text." However, there is a problem, because the JS engine CharacterData.prototype.data is shared only one, then if there are multiple text objects. Text1.data,text2.data ... They all access this data value on the prototype obviously and the actual situation each text node object has its own text conflict??
Puzzle Two: Change text.nodevalue when the Text.data value will also change, and vice versa, one change will cause another change. How is this JS engine implemented??
Think about it:
If you think of nodevalue and data as pointers rather than specific saved values, NodeValue and data are still on the prototype property, When you visit Text1.nodevalue, the NodeValue on the prototype points to the Text1 corresponding text value, the NodeValue on the prototype when the Text2.nodevalue is accessed to execute text2 corresponding text .... The same is true for accessing data. When you change the point of data, NodeValue also change the point.
The NodeValue value of the text node is also changed synchronously when the value of the data property of the text node is changed by asking a circle in the DOM.   The Big God answer is that Chrome has turned many of the class instances in the DOM into the accessor properties on the prototype in recent releases https://groups.google.com/a/chromium.org/forum/m/#!topic/ Blink-dev/h0mgw0jkdn4. I'm sorry I think not exactly right, I can have doubts about this thing, but the mind does not jump out of the data attributes of the space to forget that there are also accessor properties of this magical thing, so I do not know the number of times need to revisit the "Gao Cheng" P141 page, the property type familiar with the students can skip the relevant knowledge point to see analysis


Relevant knowledge points:

A graph first describes the relationship between attribute types, data properties, accessor properties, and four attributes.


(1). Data properties: A location that contains a data value that can be read or written to, and the data attribute has 4 properties that describe its behavior. Properties that are defined directly on the object, and their four attribute values default to True. A property is set by Object.defineproperty (obj, ' attr ', {}), value is undefined, and the remaining attribute is false

    • [[Configurable]]:
      A. Indicates whether the property can be redefined by Detele Delete property

      B. Ability to modify attributes of a property

      Once you set the configurable of a property to False, you cannot set it to configurable, and then call Object.defienproperty () Modifying the writable attribute will result in an error (note: The value attribute can be modified by Object.definedproperty () depending on writable, and if true before writable, then value may change, Cannot be changed if writeable is changed to False,value).
      C. Can I modify properties to accessor properties
    • [[Enumerable]]:
      A. Indicates whether the property can be returned through the for-in loop. The output "New 1" is because the chrome console basically gives each statement a return value. Because the Get function does not return a value, the Get function returns Undefiend.

    • [[Writable]]:
      A. Indicates whether the value of the property can be modified, see above. When the writable of a property is false, the property is assigned a value of strict mode error, and non-strict mode is ignored.
    • [[Value]]:
      A. The data value that contains this property, read from this location when the property is read, write the property it is worth saving the new value in this position. This attribute value defaults to undefined.

Note: IE8 can only use the Object.defineproperty () method on Dom objects, and only accessor properties can be created, because implementations do not thoroughly recommend or do not use the method in IE8.

(2). Accessor properties: Does not contain data values, they contain a pair of getter and setter functions (though not required). The Getter function is called when the accessor property is read, the function returns a valid value, the setter function is called when the accessor property is written, and the new value can be passed in, which is responsible for deciding what to do with the data. The default value for the Configurable,enumerable attribute is true for properties that are defined directly on the object.

    • [[Configurable]]:
      A. Can I redefine a property by Detele delete the property

      B. Ability to modify attributes of a property

      C. Can I modify a property to a data property

      Note You cannot set data properties and accessor properties at the same time to error.
    • [[Enumerable]]:
      A. Whether the property can be returned through a for-in loop.
    • [[Get]]: The function that the engine calls automatically when the property is read, non-strict mode does not set the default value to Undefiend.
    • [[Set]]: The function that the engine calls automatically when the property is written, the default value is Undefiend.

Accessor properties cannot be defined directly and must be defined using Object.defineproperty (). It is not necessary to specify both getter and setter at the same time, specifying get only means that the property cannot be written, the attempt to write the property is ignored, and the error in strict mode. Only specifies that the property of the setter function cannot be read, the attempt to read the property is returned undefiend in the non-strict mode, and the test strict mode does not appear to have an error



Analysis and discussion

In fact, this association phenomenon has historical reasons, it is necessary to read FQ Lu Xiaofe recommended this article  intent-to-ship:moving DOM attributes To prototype chains . Let me briefly explain my understanding (English slag Please crossing forgive me) ~
traced to small science :
Around 13, these properties of the DOM interface in Chrome's JS engine are still defined on the DOM instance object, Take the id attribute of the element node as an example, which is the data property instead of the current accessor property. The chromium team has realized that this performance does not conform to the WEBIDL specification, after all, when IE and Firefox attributes were first implemented on the prototype chain, the chromium team felt that they should also move the DOM attribute from the instance to the prototype chain and then expose the getter/ Setter accesses these properties, since it is a shared property on the prototype chain, the accessor property is a good choice because there is only one value that does not satisfy multiple instance objects to access their respective IDs, because it is essentially a function, and it is good to do the corresponding processing within the function. What are the benefits of the

property moving to the prototype chain? ?
This enables developers to hook the Getter/setter method of DOM properties, which improves the programmability of DOM operations;
because it is the getter/setter of the prototype that handles JavaScript logic, For developers to manipulate the JavaScript library and the default DOM attribute representation, developers can override the default Getter/setter method provided to implement custom logic, and
from the bottom, it is also convenient for C + + to implement the JS engine provided by this operation;
can also reduce memory consumption (otherwise how to say V8 engine so fast ~ ~), improve performance, after all, the prototype chain properties are all instances shared;
As for compatibility, the risk is very low after all, IE and FF have achieved this change;
They also surveyed which properties suggest moving, is their goal

My understanding :


Yes, that's good! The attribute values of these properties are set to true, configurable enumerable, gives the developer the flexibility to use, I can redefine my gettr/setter function logic, let the ID be added to text, Text.hasownproperty (' ID ') returns TRUE.
Start by destroying the relationship between data and NodeValue.

// overwrite the Set/get function, break the default Set/get to implement the logic associated with NodeValue Object.defineproperty (Characterdata.prototype, ' data ', {  configurable:true,  Enumerable:true,  set:function() {},  get:function (){}});
 var  text=document.createtextnode (' text ' 
 text.data= ' new text ' ;text.hasownproperty ( ' data '); //  Text.data; //  undefined  text.nodevalue; //  "text"  /*   or return false, it appears that handling delete Text.data does not implement  in the SET function. */ 
 delete  CharacterData.prototype.data; //  CharacterData.prototype.hasOwnProperty (' data '); //  text.data= ' new new text '; //  Text.nodevalue; //  text.hasownproperty (' data '); //  /*   */ 

The default Setter/getter function how to implement the association logic, provide my simple idea, certainly not the default implementation is good, we can brainstorm a bit of mutual discussion ~ ~
I guess there's always a while (true) loop in the engine that listens to Text.hasprototype (' data '); when text.data= "text", you might do the following
Get the Data property value that the developer added to the text object, Change the value of the CharacterData.prototype.data (the set method that calls the data accessor property by default) and the value of Node.prototype.nodeValue (the set method that calls the data accessor property by default), delete the object that should be on the text The Data property.

With the use of the JS engine default implementation of the delete off the text instance on the Data property, I use the composite inheritance mode to implement the
(1) The assignment operation of data updates the value on the prototype property and does not add the processing logic to its own property
The code is as follows

//in order not to conflict with the native interface name in the engine, the naming takes a similar view only as a demonstration of understanding.//Combining Inheritancefunctionobjectt () {//...}functionEventtargett () {//...}//NodefunctionNodeE (mydata) {if( This instanceoftextt) {CharacterDataa.prototype.data=MyData; Eventtargett.call ( This, MyData); //... You need to add a property to text.  }  Else{object.defineproperties ( This, {data:{configurable:true, Enumerable:true, set:function(protodata) {MyData=Protodata; }, get:function(){          returnMyData; }}, length:{configurable:true, Enumerable:true, set:undefined, get:function() {}}, previouselementsibling:{configurable:true, Enumerable:true, set:undefined, get:function() {}}, nextelementsibling:{configurable:true, Enumerable:true, set:undefined, get:function(){}      }    }); /*This.substringdata=substringdata;    This.appenddata=appenddata;    This.insertdata=insertdata;    This.deletedata=deletedata;    This.replacedata=replacedata; This.remove=remove;*/}}nodee.prototype=NewEventtargett (); Nodee.prototype.constructor=NodeE; nodee.prototype.__proto__=Eventtargett.prototype;//CharacterdatafunctionCharacterdataa (mydata) {//adding properties to the text node itself  if( This instanceoftextt) {Nodee.call ( This, MyData); //... You need to add a property to text.  }  //properties on a prototype  Else{object.defineproperties ( This, {wholetext:{configurable:true, Enumerable:true, set:undefined, get:function(){}    }  }); /*This.splittext=splittext; this.getdestinationinsertionpoints=getdestinationinsertionpoints;*/}}characterdataa.prototype=NewNodeE (); CharacterDataa.prototype.constructor=Characterdataa; characterdataa.prototype.__proto__=Nodee.prototype;//TextfunctionTextt (MyData) {Characterdataa.call ( This, MyData); //... If you need to add a property to the text node itself.}textt.prototype=NewCharacterdataa (); Textt.prototype.constructor=Textt; textt.prototype.__proto__=Characterdataa.prototype;//Export InterfaceDocument.prototype.createtextnode=function(MyData) {if(arguments.length==0){    return' Error '; }  return NewTextt (arguments[0].tostring ());}//UsevarText1=document.createtextnode (' Hello '); text1.data;//"Hello"Text1.hasownproperty (' data ');//false;CharacterDataa.prototype.hasOwnProperty (' data ');//true
View Code

In the implementation process also encountered some problems and there are some limitations, as follows
1.character.prototype needs to execute the node function once the prototype object is initialized, and new Text (Arguments[0].tostring ()) Executes the node function again. This causes the get in the node function to be used as a mydata in the scope chain of the closure package but not the one that really wants it, that is, the get in the scope chain created by the prototype object wants new Text (Arguments[0].tostring ()) Creates a mydata in the scope chain. Then my solution was to set the characterdata.prototype.data=mydata when I created the new Text (Argumetns[0].tostring ()) ; The accessor property is better implemented because it is called the set () function by default, and I save MyData in the Set function to update the MyData value in the scope chain of Character.prototype initialization prototype Object!
2. Without the use of the JS engine default delete operation, I can only create a good text object in the code beforehand, and temporarily did not dynamically create the node's code processing ideas.

var text=document.createtextnode (' text ');  while (true) {  if(text.hasownproperty (' Data ')) {    CharacterData.prototype.data =text.data;     Delete text.data;}  }

3. Can not satisfy more than one text instance, Set/get logic also need to strengthen otherwise the data is really shared value, really need to use the pointer point??
4. Have not realized and NodeValue Association, Dom API system is too large, I have to study slowly, first of all I put the code on GitHub, follow-up if there is interest can be concerned about ~ if you have any ideas welcome to explore, I also want to learn more learning!




Uncover the data and NodeValue properties in the DOM and change those things.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.