• JavaScript高级程序设计(第3版)学习笔记15——DOM基础


      从这篇笔记开始整理JavaScript的第三部分:文档对象模型DOMDocument Object Model)。DOM是针对HTMLXML文档的一个API,脱胎于DHTML,由W3C负责制定相关标准,现在已经成为表现和操作页面标记的真正的跨平台、语言中立的一种标准,除了JavaScript外,其它一些语言比如SVGMathML等也不同程度的实现了各自的DOM

    1、DOM组成和级别

      DOM分为三个组成部分和三个级别:

    组成部分 说明
    核心DOM 用于任何结构化文档的标准模型
    XML DOM 用于XML文档的标准模型,定义了所有XML元素的对象和属性,以及访问它们的方法(接口),换句话说,XML DOM是用于获取、更改、添加或删除XML元素的标准
    HMTL DOM 用于HTML文档的标准模型,定义了所有HTML元素的对象和属性,以及访问它们的方法(接口)
    DOM级别 浏览器支持情况 功能模块 说明
    0级 IE4、Netscape4 W3C标准中,是没有0级的,通常所谓DOM 0级指的就是在DOM 1级规范之前的在IE4Netscape Navigator4中支持的DHTML
    1级 几乎所有现代浏览器 DOM核心(DOM Core) 规定的是如何映射基于XML的文档结构,以便简化对文档中任意部分的访问和操作
    DOM HTML DOM核心基础上加以扩展,添加了针对HTML的对象和方法
    2级 IE 9+
    Opera 7-9.9
    (部分支持),OPera 10+
    Safari 2+
    (部分支持)
    Chrome 1+
    (部分支持)
    Firefox 1+
    (几乎全部)
    DOM视图(DOM Views) 定义了跟踪不同文档(例如,应用CSS前后的文档)视图的接口
    DOM事件(DOM Events) 定义了事件和事件处理的接口
    DOM样式(DOM Style) 定义了基于CSS为元素应用样式的接口
    DOM遍历和范围(DOM Traversal and Range) 定义了遍历和操作文档树的接口
    DOM核心和HTML扩展 开始支持XML命名空间等
    3级 IE9+
    Opera9+
    (部分支持)
    Firefox1+
    (部分支持)
    DOM加载和保存(DOM Load and Save 引入了以统一方式加载和保存文档的方法
    DOM验证(DOM Validation 验证文档的方法
    DOM核心和HTML扩展 开始支持XML1.0规范,涉及XML InfosetXPathXMLBase

    2、文档映射

    DOMHTMLXML文档映射成一个由不同节点组成的树型机构,每种节点都对应于文档中的信息或标记,节点有自己的属性和方法,并和其他节点存在某种关系,节点之间的关系构成了节点层次,例如:

    <html>
        <head>
            <title>标题</title>
        </head>
        <body>
            <p>测试</p>
        </body>
    </html>

    1)文档节点Document是每个文档的根节点
    2)文档元素:如右图,文档节点只有一个子节点,即<html>元素,称之为文档元素。文档元素是文档的最外层元素,其他所有元素都包含在文档元素中,每个文档只能有一个文档元素。在HTML中,文档元素永远是<html>元素,但是在XML中,没有预定义元素,因此任何元素都可能成为文档元素。
    3)文档中每一段标记都通过树中的一个节点来表示,比如HTML元素通过元素节点表示,特性(attribute)通过特性节点表示,文档类型通过文档类型节点表示,而注释则通过注释节点表示。在DOM1中,总共有12种节点类型,他们都继承自一个基类型。

     

    3、Node接口

    类别 属性/方法 /类型/返回类型 说明
    属性 nodeName String 节点名字,根据节点类型定义。对于元素节点,就是原始的标签名
    nodeValue String 节点值,根据节点类型定义。对于元素节点为null
    nodeType Number 节点类型,返回12种节点类型值之一
    ownerDocument Document 指向这个节点所属的文档,可以利用它直接访问文档节点,而不用层层回溯
    parentNode Node 文档树中的父节点
    childNodes NodeList 所有直接子节点组成的列表,不同浏览器对空白字符和<html>外的注释有不同处理,会导致childNodes不一致
    firstChild Node 第一个直接子节点,没有子节点返回null
    lastChild Node 最后一个直接子节点,没有子节点返回null
    previousSibiling Node 前一个兄弟节点,没有则返回null
    nextSibiling Node 后一个兄弟节点,没有则返回null
    方法 hasChildNodes() Boolean 有子节点时返回true,否则返回false
    appendChild(node) Node 在末尾添加子节点,返回新添加的节点。如果传入参数已经是文档中一部分,结果将是将该节点从原来的位置移向新位置(任何DOM节点不能同时出现在多个位置上)
    removeChild(node) Node 移除节点并返回这个节点。删除后,节点仍然属于原来的文档,只是没有了位置
    replaceChild(node,node) Node 传入新添加的节点和被替换的节点,返回被替换的节点。被替换的节点仍然属于原来的文档,只是没有了位置
    insertBefore(node,node) Node 传入新添加的节点和参照节点,新添加节点会成为参照节点的前一个兄弟节点,如果参照节点为null,则插入到最后,相当于appendChild()。返回新添加的节点
    cloneNode(boolean) Node 复制节点,参数为true时,复制节点及其所有子节点树,为false时,只复制节点本身。返回的节点属于文档所有,但没有指定父节点
    normalize()   处理文档树中的文本节点,删除空文档节点或合并两个相邻的文本节点等

    说明:

    (1)关于节点类型nodeType,在DOM1中定义了12种常量,是作为Node类型构造函数的属性定义的(静态属性),它们对应于各自的节点类型:

    节点 节点类型(nodeTypeNode的静态属性) 节点名称(nodeName 节点值(nodeValue 父节点(parentNode 子节点(childNodes 说明
    Document Node.DOCUMENT_NODE(9) #document null null DocumentType(最多一个)|Element(最多一个)
    |Comment|ProcessingInstruction
    下有进一步叙述
    Element Node.ELEMENT_NODE(1) 元素的标签名 null Document|Element Element|Text|Comment|
    ProcessingInstruction|
    CDATASection|EntityReference
    Text Node.TEXT_NODE(3) #text 节点所包含的文本 Element (不支持)没有子节点
    Comment Node.COMMENT_NODE(8) #comment 注释的内容 Document|Element (不支持)没有子节点 和Text继承自相同基类,拥有除splitText()外所有字符串方法,可通过nodeValue或data属性获取注释内容
    CDATASection Node.CDATA_SECTION_NODE(4) #cdata-section CDATA区域的内容 Document|Element (不支持)没有子节点 继承自Text类型,拥有除splitText()外所有字符串方法
    DocumentType Node.DOCUMENT_TYPE_NODE(10) doctype的名称 null Document (不支持)没有子节点 在DOM1中不能动态创建,DocumentType对象的3个属性:
    name表示文档类型名称
    entities表示描述文档类型实体的NamedNodeMap
    notations表示文档类型描述的符号的NamedNodeMap
    DocumentFragment Node.DOCUMENT_FRAGMENT_NODE(11) #document-fragment null null 同Element类型 下有进一步叙述
    Attr Node.ATTRIBUTE_NODE(2) 特性名称 特性值 null HTML中不支持,XML中可以是TextEntityReference 有3个自己的属性:
    name等于nodeName
    value等于nodeValue
    specified表示是否为默认设置
    EntityReference Node.ENTITY_REFERENCE_NODE(5) 引用的实体名称 null     实体引用节点
    Entity Node.ENTITY_NODE(6) entity name null     实体节点
    ProcessingInstruction  Node.PROCESSING_INSTRUCTION_NODE(7) ProcessingInstruction.target 相同 ProcessingInstruction.data 相同     处理指令
    Notation  Node.NOTATION_NODE(12) notation name null     DTD中定义的符号

    这些节点类型都实现了Node接口,因此都可以访问Node类型的属性和方法(不支持子节点的节点类型上调用appendChild()、insertBefore()、replaceChild()、removeChild()等方法时会抛出异常)。经过测试在IE9中已经可以直接访问Node类型中定义的常量值,并且IE9中这些值不能改变,而在Firefox15的版本中仍然可以修改,这应该是Firefox实现的一个Bug(原书说IE中不可访问Node的论述似有不妥)。

    Node.DOCUMENT_NODE = 2;
    console.info(Node.DOCUMENT_NODE);//IE9输出9,而Firefox15输出的是2

    2)关于NodeList类型,它也是一个类数组类型,有length属性,也可以通过方括号语法访问,还可以通过item()方法访问,但并不是Array的实例,如果要对其使用数组方法,必须像转换arguments对象一样来转换NodeList对象:

    var node = document;//任意一个节点,这里使用文档根节点测试
    console.info(node.childNodes.length);//2,直接子节点
    console.info(node.childNodes[0].nodeName);//通过方括号语法访问第1个元素,索引从0开始
    console.info(node.childNodes.item(1).nodeName);//通过item()方法访问第2个元素,索引从0开始
    var arr = Array.prototype.slice.call(node.childNodes,0);//转换为真正的数组
    console.info(arr.join(','));//可以使用数组方法了

    需要特别注意的是,NodeList对象类型是一个有生命、有呼吸的对象,它的属性和元素是跟随文档变化而变化的:

    var node = document;
    var nodeList = node.childNodes;
    var src = nodeList.length;
    node.removeChild(nodeList[0]);//修改文档,会同时修改NodeList对象的属性和元素,即便是已经将其保存为另外一个变量
    console.info(nodeList.length == src - 1);//true

    类似NodeList这种动态变化的对象对象还有HTMLCollectionNamedNodeMap等,其中HTMLCollection还有一个namedItem()方法,可以通过元素的name获取集合中的项。

    4、Document类型

      在JavaScript中,通过Document类型表示文档,而我们通常使用的document对象则是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。同时,docuemnt对象也是window对象的一个属性,可以作为全局对象来访问。document对象的主要属性和方法有:

    类别 属性/方法 说明 备注
    属性 documentElement 指向HTML页面中的<html>元素 作为document的子节点,<html>元素还可以通过document.firstChilddocument.childNodes[0]等方式访问
    body 直接指向HTML页面中的<body>元素 document.body的使用频率非常高
    doctype 表示<!DOCTYPE>相关信息,不同浏览器差异比较大,因此用处不大 有些浏览器会把<!DOCTYPE>作为注释处理
    title 包含<title>元素中的文本,显示在浏览器窗口的标题和标签页上,可以通过它修改标题 修改title属性的值不会改变<title>元素
    URL 页面完整的URL,即地址栏中显示的URL 这些信息都存在于请求的HTTP头部,这些属性是为了方便在JavaScript中访问,domain的设置需要注意:
    1
    、不能将domain设置为URL不包含的域,如URL='p2p.wrox.com'domain可以设置为wrox.com,但不能设置为ncz.net
    2
    、不能将domain由松散的设置为紧绷的,如原来domain='wrox.com',不能设置为domain='p2p.wrox.com' 
    domain 页面的域名,可以设置
    referrer 链接到当前页面的那个页面的URL,没有来源页面时,为空字符串
    implementation 提供浏览器对DOM实现情况的描述对象,在DOM1只有一个hasFeature()方法 hasFeature()接受两个参数:DOM功能和版本号。由于浏览器实现问题,这个方法并非百分百准确
    文档元素集合 anchors 包含文档中所有带name特性的<a>元素 这些集合都是HTMLCollection对象,集合中的项会随文档的变化而更新
    applets 包含文档中所有<applet>元素,已经不推荐使用
    forms 包含文档中所有<form>元素,相当于document.getElementsByTagName('form')
    images 包含文档中所有<img>元素,相当于document.getElementsByTagName('img')
    links 包含文档中所有带href特性的<a>元素
    元素获取方法 getElementById() 接受一个参数:要获取元素的ID,匹配大小写,找不到时返回null,若有多个相同ID元素,返回第一个 IE8-中不区分大小写,并且表单元素(如<input>)中的name也被作为ID去查询
    getElementsByTagName() 接受一个参数:要获取元素的标签名,返回包含0或多个元素的NodeList,在HTML中,返回HTMLCollection 传入*号时,则返回文档中所有元素,可以通过索引(调用item())、字符串(调用namedItem())、以及直接使用item()方法访问结果集
    getElementsByName() HTMLDocument特有方法,返回name特性为传入参数值的元素集合,返回HTMLCollection对象 使用namedItem()时,只返回第一项,因为是按name获取的集合,集合中的name全部相同
    文档写入方法 write() 输出文本,接受一个参数:写入到输出流的文本 write()writeln()被用来向页面动态地输入内容,需要注意的是:
    1
    、如果输出内容中含'</script>',为了防止做为标签解析而发生错误,需要特殊处理,可以写成'<\/script>'或拆分
    2
    、如果是页面加载完成之后调用,会重写整个页面
    writeln() 输出文本,并添加换行符(\n),接受一个参数:写入到输出流的文本
    open() 打开网页输出流
    close() 关闭网页输出流
    创建方法 createElement() 创建新元素,并设置ownerDocument属性,接受一个参数:要创建元素的标签名(在HTML中不分大小写,在XML中区分) IE中还可以传入包含属性的完整的元素标签来创建新元素
    createTextNode() 创建文本节点,接受一个参数:要插入节点中的文本(会按HTML/XML格式编码),会同时设置ownerDocument 除非把新节点添加到文档中,否则不会显示新节点
    createComment() 创建新注释,接受注释文本 浏览器不会识别<html>后代注释,如要访问注释节点,需要保证是<html>元素的后代 
    createCDATASection() 在XML文件中创建CDATA区域,传入节点内容 CDATA区域只会出现XML文档中,因此多数浏览器会报CDATA区域错误地解析为Comment或Element
    createDocumentFragment() 创建文档片段  
    createAttribute() 创建新特性节点  

    说明:

    (1)一般情况下,不用在document对象上调用appendChild()insertBefore()removeChild()replaceChild()等方法,因为文档类型(如果存在的话)是只读的,而且它只能有一个元素子节点。

    (2)document对象的创建方法,是实现动态加载脚本或样式的基础,从而可以进一步对代码模块化,实现按需加载,提升性能,这在Ext4库中已经很好地应用了。动态加载的一般方法:

    function loadScript(url){//动态加载外部js文件,也可以类似的加载js代码
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = url;
        document.body.appendChild(script);
    }
    
    function loadStyle(url){//动态加载外部css文件,也可以类似的通过style元素加载css代码
        var link = document.createElement("link");
        link.rel = "stylesheet";
        link.type = "text/css";
        link.href = url;
        var head = document.getElementsByTagName("head")[0];
        head.appendChild(link);
    }

    5、Element类型

      Element类型用于表现XMLHTML元素,提供了对元素标签名、子节点以及特性的访问。在HTML中,元素由HTMLElement类型(或其子类型)表示,HTMLElement继承了Element类型,并添加了对应每个HTML元素都存在的标准特性的属性。HTMLElement类型常用属性和方法总结如下:

    类别 属性/特性 说明
    Element类型属性 tagName 这个属性是所有Element都有的,值和nodeName相同,表示元素标签名,在HTML中,返回的标签名始终大写,在XML中,和源代码一致
    HTML元素标准特性 id id 元素在文档中的唯一标识符
    title title 有关元素的附件说明信息,一般通过工具条显示出来
    lang lang 元素内容的语言代码,很少使用
    dir dir 语言的方向,ltr:从左至右,rtl:从右至左,也很少使用
    className class 元素的CSS类,因为class是保留字,所以属性命名为className
    特性属性 attributes 这个是属性只有Element类型使用,是一个NamedNodeMap值,属于一个动态集合
    特性方法 getAttribute() 参数:特性名。参数必须与实际的特性名相同,因此是class而不是className,给定特性不存在时,返回null,这个方法也可以获取自定义特性,特性名不区分大小写
    setAttribute() 参数:特性名和特性值。如果已存在该特性,替换现有的值,如果不存在,就创建并赋值。通过这个方法设置特性时,会把特性名转换为小写形式
    removeAttribute() 参数:特性名。彻底删除元素的特性,不仅会清除特性的值,也会从元素对象中删除特性
    特性节点方法 getAttributeNode() 返回对应特性的Attr节点,element.getAttributeNode('align')相当于element.attributes['align']
    setAttributeNode() 设置元素的Attr节点
    其它方法 getElementsByTagName() 在当前元素的后代节点中搜索,其它用法和Document类型的同名方法一样

    说明:

    (1)特殊特性:

      A、class特性,其对应DOM对象的属性为className,在特性方法操作中参数需要传入class,而对象属性操作需要设置className属性。

      B、style特性,在通过getAttribute()访问时,返回的是style特性值中包含的CSS文本,而通过属性来访问它则会返回一个对象。

      C、事件处理特性,比如onclick,当在元素上使用时,onclick特性中包含的是JavaScript代码,如果通过getAttribute()访问,返回相应代码的字符串,而在访问onclick属性时,会返回一个JavaScript函数(没有相应特性时返回null)。

    (2)关于自定义特性和属性,在HTML5规范中,需加上“data-”前缀。

      A、给节点设置特性值时,会同步修改相应DOM对象的属性值,但是设置自定义特性的值时,一般浏览器是不会为相应的DOM对象添加属性的,然而IE也会为自定义特性创建属性。

      B、直接给DOM对象设置属性值时,会同步修改相应节点元素的特性值,但是一般浏览器中自定义属性不会成为元素的特性,使用getArrtibute()时会返回null,然而IE会为自定义属性创建特性。

    (3)HTMLElement类型是HTML中元素的基类型,对应不同的标签,还有很多更加具体的子类型,这些子类型也有与之相关的特性和方法,比如对应<body>标签有HTMLBodyElement类型、对应<table>标签有HTMLTableElement类型等。

    (4)元素的子节点,可以有任意数目的子节点和后代节点,childNodes属性则包括了所有直接子节点,但是由于不同浏览器在处理注释、文本等节点的不同,会使得childNodes也不同,因此,如果需要遍历元素子节点的话,需要添加节点类型判断:

    for(var i=0,l=element.childNodes.length; i < l; i++){
        if(element.childNodes[i].nodeType === 1)//过滤元素子节点
        {
        //对元素子节点做操作
        }
    }

    (5)元素的每一个特性都由一个Attr节点表示,每个节点都保存在一个NamedNodeMap对象中,这个对象和NodeList与HTMLCollection类似,是一个“动态”的,随文档变化而变化,它有下面的一些方法:

    方法 说明
    getNamesItem(name) 返回nodeName等于name的节点
    removeNamedItem(name) 从列表中移除nodeName等于那么的节点,调用removeNamedItem()与在元素上调用removeAttribute()效果相同,只是前者返回被移除的Attr节点
    setNamedItem(node) 向列表中添加节点,以节点的nodeName属性为索引
    item(pos) 返回位于数字pos位置处的节点

    元素的attributes属性返回的就是一个NamedNodeMap对象,其中包含一系列Attr节点,每个节点的nodeName就是特性名称,nodeValue就是特性值,可以使用attributes属性来遍历元素的特性(原书第268页):

    function outputAttributes(element){
        var pairs = new Array(),
            attr;
        for(var i=0, len=element.attributes.length; i < len; i++){
            attr = element.attributes[i];
            if(attr.specified){//specified属性表示特性值是设置还是默认的,为true表示设置的
                pairs.push(attr.nodeName + "=\"" +attr.nodeValue + "\"");
            }    
        }
        return pairs.join(" ");//以空格连接各个特性并返回
    }

    6、Text类型

      文本节点由Text类型表示,包含的是可以按字面解释的纯文本内容,可以包含转义后的HTML字符,但不能包含HTML代码,Text类型的主要属性和方法有:

    类别 属性/方法 说明
    属性 length 文本字符数
    data 文本字符,和nodeValue值相同
    方法 appendData(text) 将text添加到文本节点末尾
    deleteData(offset,count) 从offset指定的位置开始删除count个字符
    insertData(offset,text) 从offset指定的位置插入text
    replaceData(offset,count,text) 用text替换从offset指定的位置开始到offset+count为止处的文本
    splitText(offset) 从offset指定的位置将当前文本节点分成两个文本节点
    substringData(offset,count) 提取从offset指定的位置开始到offset+count为止处的字符串

    说明:

    (1)在Node类型中定义了一个normalize()方法,用于将相邻的两个或多个文本子节点合并,需要注意的是这个方法需要在文本节点的父节点上调用;与之相反的是,Text类型提供了splitText()方法,它会将原文本节点分成两个文本节点,原文本节点将包含从开始到指定位置之前的内容,新文本节点包含剩下的内容,最终返回新文本节点。

    (2)默认情况下,每个可以包含内容的元素最多只能有一个文本子节点,而且文本不能为空。通过DOM脚本操作时,可能存在有多个文件子节点的情况。

    (3)设置文本节点时需要注意,字符串会经过HTML或XML编码。

    7、DocumentFragment类型

      在所有节点类型中,只有DocumentFragment是没有对应的标记的,DOM规定DocumentFragment是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。DocumentFragment类型可以作为一个容器来使用,可以把后面要对文档进行的添加、修改、移除等操作先对DocumentFragment进行,然后再将DocumentFragment添加至文档中,从而避免DOM视图的多次渲染。例如:

    function addItems(){        
        var fragment = document.createDocumentFragment();//创建一个文档片段作为容器
        var ul = document.getElementById("myList");
        var li = null;
        
        for (var i=0; i < 3; i++){
            li = document.createElement("li");//创建列表元素
            li.appendChild(document.createTextNode("Item " + (i+1)));//在列表元素中添加文本
            fragment.appendChild(li);//将列表元素添加中容器中,此时不会渲染页面
        }
        
        ul.appendChild(fragment); //将容器中的节点添加到文档中(但容器本身不会添加至文档树),这样只需要渲染一次页面    
    }

    需要注意的是,在DocumentFragment中的节点不属于文档,如果将文本中的节点添加至DocumentFragment中,该节点将会从文档树中移除。

    8、操作表格

      <table>元素是HTML中最复杂的结构之一,在HTML DOM中还为<table>、<tbody>、<tr>等元素添加了一些属性和方法(请注意table、tbody、tr及td之间的关系):

    元素 类别 属性/方法 说明
    <table> 属性 caption 保存着对<caption>元素(如果有)的指针
    tBodies 是一个<tbody>元素的HTMLCollection
    tHead 保存着对<thead>元素(如果有)的指针
    tFoot 保存着对<tfoot>元素(如果有)的指针
    rows 表格中所有行的HTMLCollection
    方法 createTHead() 创建<thead>元素,将其放到表格中,返回引用
    createTFoot() 创建<tfoot>元素,将其放到表格中,返回引用
    createCaption() 创建<caption>元素,将其放到表格中,返回引用
    deleteTHead() 删除<thead>元素
    deleteTFoot() 删除<tfoot>元素
    deleteCaption() 删除<caption>元素
    deleteRow(pos) 删除指定位置的行
    insertRow(pos) 向rows集合中的指定位置插入一行
    <tbody> 属性 rows 保存着<tbody>元素中行的HTMLCollection
    方法 deleteRow(pos) 删除指定位置的行
    insertRow(pos) 向rows集合中的指定位置插入一行,返回新插入行的引用
    <tr> 属性 cells 保存着<tr>元素中单元格的HTMLCollection
    方法 deleteCell(pos) 删除指定位置的单元格
    insertCell(pos) 想cells集合中的指定位置插入一个单元格,返回新插入单元格的引用

    注意,上表中列出的是原书中提及元素的属性和方法,并不全面,比如对应<td>元素还有HTMLTableCellElement对象,而<tr>对应对象还有rowIndex属性等,实际使用时可以查阅相关的DOM参考手册。这里旨在通过主要的一些属性和方法明确概念,而非参考大全。

    拣尽寒枝不肯栖,寂寞沙洲冷。
    郴江幸自绕郴山,为谁流下潇湘去?
    欲将心事付瑶琴,知音少,弦断有谁听?
    倩何人,唤取红巾翠袖,揾英雄泪!
    零落成泥碾作尘,只有香如故!
  • 相关阅读:
    [转]多个ajax请求时控制执行顺序或全部执行后的操作
    [转]微擎目录结构介绍
    [书目20180702]互联网思维的企业
    [转]Oracle密码过期, 报:ORA-01017: 用户名/口令无效; 登录被拒绝
    [转]VR原理讲解及开发入门
    [转]JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐
    [转]bootstrapValidator.js 做表单验证
    [转]Build beautiful, responsive sites with Bootstrap and ASP.NET Core
    [转]C# Bootstrap table之 分页
    [转]Bootstrap table 分页 In asp.net MVC
  • 原文地址:https://www.cnblogs.com/linjisong/p/2733723.html
Copyright © 2020-2023  润新知