归属不相同的范围

property 和
attribute极度轻易混淆,七个单词的普通话翻译也都不行相仿(property:属性,attribute:本性),但实则,二者是莫衷一是的事物,归于不相同的层面。

  • property是DOM中的属性,是JavaScript里的靶子;
  • attribute是HTML标签上的特征,它的值只可以够是字符串;

基于JavaScript分析property 和 attribute

html中有这么风姿洒脱段代码:

<input id="in_1" value="1" sth="whatever">

简易的在html页面上成立一个input输入栏(注意在那一个标签中增加了一个DOM中不设有的习性“sth”),那时在JS试行如下语句

var in1 = document.getElementById('in_1');

实行语句

console.log(in1);

从console的打印结果,能够见见in1含有三个名字为“attributes”的性质,它的门类是NamedNodeMap,同临时候还或者有“id”和“value”两此中央的特性,但未曾“sth”这几个自定义的品质。

attributes: NamedNodeMap
value: "1"
id: "in_1"

多少console也许不会打印in1上的习性,那么能够实行以下命令打字与印刷要寓指标属性:

console.log(in1.id);       // 'in_1'
console.log(in1.value);     // 1
console.log(in1.sth);       // undefined

能够窥见,标签中的五天性格,独有“id”和“value”会在in1上成立,而“sth”不会被创制。这是出于,每多个DOM对象都会有它暗中同意的中坚属性,而在创造的时候,它只会创制那个宗旨脾性,我们在TAG标签中自定义的性质是不会直接放到DOM中的。

大家做三个附加的测验,创设另八个input标签,并履行相似的操作:

html:

<input id="in_2">

JS:

var in2 = document.getElementById('in_2');
console.log(in2);

从打字与印刷新闻中能够看见:

id: "in_2"
value: null

就算大家从未在TAG中定义“value”,但鉴于它是DOM暗中同意的主干质量,在DOM初阶化的时候它依旧会被创建。由此大家能够得出结论:

  • DOM有其暗许的主导属性,而那一个属性便是所谓的“property”,无论如何,它们都会在初叶化的时候再DOM对象上创造。
  • 要是在TAG对那么些属性实行赋值,那么那么些值就能够作为早先值赋给DOM的同名property。

近日赶回第七个input(“#in_1”),大家就能够问,“sth”去哪个地方了?别急,我们把attributes这几个性格打字与印刷出来看看

console.log(in2);

上边有几个性子:

0: id
1: value
2: sth
length: 3
__proto__: NamedNodeMap

原来“sth”被放置了attributes这么些目标里面,这么些指标按顺序记录了作者们在TAG中定义的习性和天性的多寡。那时,尽管再将首个input标签的attributes打字与印刷出来,就能够发觉唯有多少个“id”属性,“length”为1。

从此未来处就能够看看,attributes是归于property的三个子集,它保存了HTML标签上定义属性。如果再进一步研究attitudes中的每多天质量,会开掘它们实际不是大致的靶子,它是叁个Attr类型的靶子,具有NodeType、NodeName等天性。关于那或多或少,稍后再研讨。注意,打字与印刷attribute属性不会一向获得指标的值,而是获取多个含有属性名和值的字符串,如:

console.log(in1.attibutes.sth);        // 'sth="whatever"'

透过能够得出:

  • HTML标签中定义的属性和值会保存该DOM对象的attributes属性里面;
  • 那些attribute属性的JavaScript中的类型是Attr,而不只是保存属性名和值这么轻松;

那么,假设大家转移property和attribute的值会师世什么成效啊?实践如下语句:

in1.value = 'new value of prop';
console.log(in1.value);             // 'new value of prop'
console.log(in1.attributes.value);  // 'value="1"'

那时候,页面中的输入栏的值形成了“new value of
prop”,而propety中的value也改成了新的值,但attributes却如故是“1”。从今未来间能够想见,property和attribute的同名属性的值实际不是双向绑定的。

若果反过来,设置attitudes中的值,效果会什么呢?

in1.attributes.value.value = 'new value of attr';
console.log(in1.value);             // 'new value of attr'
console.log(in1.attributes.value);  // 'new value of attr'

此刻,页面中的输入栏拿到更新,property中的value也时有发生了转移。别的,施行下边语句也会得到后生可畏致的结果

in1.attributes.value.nodeValue = 'new value of attr';

透过,可得出结论:

  • property能够从attribute中赢得同步
  • attribute不会联合property上的值
  • attribute和property之间的多寡绑定是单向的,attribute->property;
  • 变动property和attribute上的放肆值,都会将立异反映到HTML页面中;

基于jQuery分析attribute和property

那么jQuery中的attr和prop方法是哪些的啊?

先是选择jQuery.prop来测验

$(in1).prop('value', 'new prop form $');

console.log(in1.value);             // 'new prop form $'
console.log(in1.attributes.value);  // '1'

输入栏的值更新了,但attribute并未有更新。

然后用jQuery.attr来测试

$(in1).attr('value', 'new attr form $');

console.log(in1.value);             // 'new attr form $'
console.log(in1.attributes.value);  // 'new attr form $'

输入栏的值更新了,同有时候property和attribute都更新了。

从上述测量试验的现象能够算计,jQuery.attr和jQuery.prop基本和原生的操作方法效果雷同,property会从attribute中收获同步,但是attribute不会从property中拿到同步。那么jQuery到底是什么完结的呢?

上面,大家来看看jQuery.attr和jQuery.prop的源码。

jQuery源码

$().prop源码

jQuery.fn.extend({
    prop: function( name, value ) {
        return access( this, jQuery.prop, name, value, arguments.length > 1 );
    },
    ... // removeProp方法
});

$().attr源码

jQuery.fn.extend({
    attr: function( name, value ) {
        return access( this, jQuery.attr, name, value, arguments.length > 1 );
    },
    ... // removeAttr方法
});

无论attr还是prop,都会调用access方法来对DOM对象的成分实行访问,因而要研究出越多内容,就非得去阅读access的得以完毕源码。

jQuery.access

// 这是一个多功能的函数,能够用来获取或设置一个集合的值
// 如果这个“值”是一个函数,那么这个函数会被执行

// @param elems, 元素集合
// @param fn, 对元素进行处理的方法
// @param key, 元素名
// @param value, 新的值
// @param chainable, 是否进行链式调用
// @param emptyGet,
// @param raw, 元素是否一个非function对象
var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
    var i = 0,                      // 迭代计数
        length = elems.length,      // 元素长度
        bulk = key == null;         // 判断是否有特定的键(属性名)

    // 如果存在多个属性,递归调用来逐个访问这些值
    if ( jQuery.type( key ) === "object" ) {
        chainable = true;
        for ( i in key ) {
            jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
        }

    // 设置一个值
    } else if ( value !== undefined ) {
        chainable = true;

        if ( !jQuery.isFunction( value ) ) {    // 如果值不是一个function
            raw = true;
        }

        if ( bulk ) {
            // Bulk operations run against the entire set
            // 如果属性名为空且属性名不是一个function,则利用外部处理方法fn和value来执行操作
            if ( raw ) {
                fn.call( elems, value );
                fn = null;

            // ...except when executing function values
            // 如果value是一个function,那么就重新构造处理方法fn
            // 这个新的fn会将value function作为回调函数传递给到老的处理方法
            } else {
                bulk = fn;
                fn = function( elem, key, value ) {
                    return bulk.call( jQuery( elem ), value );
                };
            }
        }

        if ( fn ) { // 利用处理方法fn对元素集合中每个元素进行处理
            for ( ; i < length; i++ ) {
                fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
                // 如果value是一个funciton,那么首先利用这个函数返回一个值并传入fn
            }
        }
    }

    return chainable ?
        elems :         // 如果是链式调用,就返回元素集合

        // Gets
        bulk ?
            fn.call( elems ) :
            length ? fn( elems[0], key ) : emptyGet;
};

access方法即使非常短,然则丰硕绕,要完全读懂并不简单,因而得以针对jQuery.fn.attr的调用来简化access。

jQuery.fn.attr/ jQuery.fn.prop 中的access调用

$(State of Qatar.attr的调用情势:

  • $(卡塔尔.attr( propertyName 卡塔尔国 // 获取单个属性
  • $(卡塔尔国.attr( propertyName, value 卡塔尔国 // 设置单个属性
  • $(卡塔尔.attr( properties 卡塔尔国 // 设置四个属性
  • $(卡塔尔(قطر‎.attr( propertyName, function 卡塔尔 // 对品质调用回调函数

prop的调用方式与attr是风流倜傥律的,在这里就不重复列举。为了轻松起见,在那地只对第生龙活虎和第几种调用形式开展切磋。

调用语句:

access( this, jQuery.attr, name, value, arguments.length > 1 );

简化的access:

// elems 当前的jQuery对象,可能包含多个DOM对象
// fn jQuery.attr方法
// name 属性名
// value 属性的值
// chainable 如果value为空,则chainable为false,否则chainable为true

var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {

    var i = 0,                      // 迭代计数
        length = elems.length,      // 属性数量
        bulk = false;                // key != null

    if ( value !== undefined ) {    // 如果value不为空,则为设置新值,否则返回该属性的值
        chainable = true;
        raw = true;             // value不是function

        if ( fn ) { // fn为jQuery.attr
            for ( ; i < length; i++ ) {
                fn( elems[i], key, value);      // jQuery.attr(elems, key, value);
            }
        }
    }

    if(chainable) {         // value不为空,表示是get
        return elems;        // 返回元素实现链式调用
    } else {
        if(length) {        // 如果元素集合长度不为零,则返回第一个元素的属性值
            return fn(elems[0], key);   // jQuery.attr(elems[0], key);
        } else {
            return emptyGet;     // 返回一个默认值,在这里是undefined
        }
    }
};

透过简化代码,能够精晓,access的效应就是遍历上二个$调用拿到的因素会集,对其调用fn函数。在jQuery.attr和jQuery.prop里面,就是接受access来遍历成分集合併对其达成对attribute和property的垄断。access的源码里面有多段条件转移代码,看起来头昏眼花,其最后指标就是能够落到实处对成分集合的变量并完结分歧的操作,复杂的代码让jQuery的接口变得更为简便易行,能急大幅度增加加代码重用性,意味着收缩了代码量,进步代码的密度进而使JS文件大小得到裁减。

那么些都以题外话了,将来回到$(卡塔尔.attr和$(卡塔尔(قطر‎.prop的兑现。总的说,那三个原型方法都使用access对元素集实行变量,并对各样元素调用jQuery.prop和jQuery.attr方法。要留意,这里的jQuery.prop和jQuery.attr并不是原型链上的法子,而是jQuery那些指标自己的办法,它是应用jQuery.extend进行方式扩大的(jQuery.fn.prop和jQuery.fn.attr是利用jQuery.fn.extend进行格局增加的)。

上面看看那八个办法的源码。

jQury.attr

jQuery.extend({
    attr: function( elem, name, value ) {
        var hooks, ret,
            nType = elem.nodeType;  // 获取Node类型

        // 如果 elem是空或者NodeType是以下类型
        //      2: Attr, 属性, 子节点有Text, EntityReference
        //      3: Text, 元素或属性中的文本内容
        //      8: Comment, 注释
        // 不执行任何操作
        if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
            return;
        }

        // 如果支持attitude方法, 则调用property方法
        if ( typeof elem.getAttribute === strundefined ) {
            return jQuery.prop( elem, name, value );
        }

        // 如果elem的Node类型不是元素(1)
        if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
            name = name.toLowerCase();
            // 针对浏览器的兼容性,获取钩子函数,处理一些特殊的元素
            hooks = jQuery.attrHooks[ name ] ||
                ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
        }

        if ( value !== undefined ) {        // 如果value不为undefined,执行"SET"

            if ( value === null ) {         // 如果value为null,则移除attribute
                jQuery.removeAttr( elem, name );    

            } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
                return ret;                 // 使用钩子函数

            } else {                        // 使用Dom的setAttribute方法
                elem.setAttribute( name, value + "" );      // 注意,要将value转换为string,因为所有attribute的值都是string
                return value;
            }

        // 如果value为undefined,就执行"GET"
        } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
            return ret;         // 使用钩子函数

        } else {
            ret = jQuery.find.attr( elem, name );   // 实际上调用了Sizzle.attr,这个方法中针对兼容性问题作出处理来获取attribute的值

            // 返回获得的值
            return ret == null ?
                undefined :
                ret;
        }
    },

    ...
});

从代码能够窥见,jQuery.attr调用的是getAttribute和setAttribute方法。

jQeury.prop

jQuery.extend({

    ... 
    prop: function( elem, name, value ) {
        var ret, hooks, notxml,
            nType = elem.nodeType;

        // 过滤注释、Attr、元素文本内容
        if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
            return;
        }

        notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

        if ( notxml ) {     // 如果不是元素
            name = jQuery.propFix[ name ] || name;  // 修正属性名
            hooks = jQuery.propHooks[ name ];       // 获取钩子函数
        }

        if ( value !== undefined ) {        // 执行"SET"
            return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
                ret :                       // 调用钩子函数
                ( elem[ name ] = value );   // 直接对elem[name]赋值

        } else {                            // 执行"GET"
            return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
                ret :               // 调用钩子函数
                elem[ name ];       // 直接返回elem[name]
        }
    },

    ...
});

jQuery.prop则是直接对DOM对象上的property进行操作。

经过对照jQuery.prop和jQuery.attr能够开掘,前面二个直接对DOM对象的property进行操作,而后人会调用setAttribute和getAttribute方法。setAttribute和getAttribute方法又是哪些艺术呢?有怎么样意义?

setAttribute和getAttribute

依据从前测验使用的输入框,推行如下代码:

in1.setAttribute('value', 'new attr from setAttribute');

console.log(in1.getAttribute('value'));         // 'new attr from setAttribute'
console.log(in1.value);                         // 'new attr from setAttribute'
console.log(in1.attributes.value);              // 'value="new attr from setAttribute"',实际是一个Attr对象

实行完setAttribute今后,宛好似间接改造attributes中的同名属性;
而getAttribute的结果与会见property的结果一模二样,而不会像直接访谈attritudes那样重返一个Attr对象。

非同一般的事例

href

不过,是否具备标签,全部属性都保持保持这么的风味呢?上边大家看看href那个天性/天性。

率先在html中开创二个标签:

<a href='page_1.html' id='a_1'></a>

在JS脚本中推行如下代码:

console.log(a1.href);  // 'file:///D:/GitHub/JS/html/test_01/page_1.html'
console.log(a1.getAttribute('href'));   // 'page_1.html'

能够观察,property中保存的是相对路线,而attribute中保留的是相对路径。那么,借使改正了那一个值会发生什么样状态呢?

更改attribute:

a1.setAttribute('href', 'page_2.html');        // 相对路径
console.log(a1.href);   // 'file:///D:/GitHub/JS/html/test_01/page_2.html'
console.log(a1.getAttribute('href'));   // 'page_2.html'

a1.setAttribute('href', '/page_3.html');    // 根目录路径
console.log(a1.href);                       // 'file:///D:/page_3.html'
console.log(a1.getAttribute('href'));       // '/page_3.html'

更改property:

a1.href = 'home.html'; // 相对路径
console.log(a1.href);   // 'file:///D:/GitHub/JS/html/test_01/home.html'
console.log(a1.getAttribute('href'));   // 'home.html'

a1.href = '/home.html'; // 根目录路径
console.log(a1.href);   // 'file:///D:/home.html'
console.log(a1.getAttribute('href'));   // '/home.html'

从这里能够窥见,href是超过常规规的品质/个性,二者是双向绑定的,纠正大肆一方,都会招致另外一方的的值产生退换。何况,那实际不是轻松的双向绑定,property中的href永恒保存相对路线,而attribute中的href则是保存相对路线。

来看此间,attribute和property的分别又多了一点,然则,这又令人变得越来越困惑了。是不是还会有别的肖似的非正规例子吗?

id

品尝改换property中的id:

   a1.id = 'new_id';
    console.log(a1.id);                     // 'new_id'
    console.log(a1.getAttribute('id'));     // 'new_id'

天呀,以往attribute中的id从property中的id发生了伙同,数据方向形成了property
<=> attribute

disabled

再来看看disabled那性情情,大家往第三个增进“disabled”性子:

<input id="in_1" value="1" sth="whatever" disabled='disabled'> // 此时input已经被禁用了

然后试行上边的代码:

console.log(in1.disabled);     // true
in1.setAttribute('disabled', false);    // 设置attribute中的disabled,无论是false还是null都不会取消禁用
console.log(in1);               // true
console.log(in1.getAttribute('disabled'));  // 'false'

更动attributes中的disabled不会变动改良property,也不会吊销输入栏的剥夺效果。

借使改成上面包车型地铁代码:

console.log(in1.disabled);     // true
in1.disabled = false;           // 取消禁用
console.log(in1.disabled);      // false
console.log(in1.getAttribute('disabled'));  // null,attribute中的disabled已经被移除了

又或者:

console.log(in1.disabled);     // true
in1.removeAttribute('disabled'); // 移除attribute上的disabled来取消禁用
console.log(in1.disabled);      // false
console.log(in1.getAttribute('disabled'));  // null,attribute中的disabled已经被移除了

能够开采,将property中的disabled设置为false,会移除attributes中的disabled。那样数据绑定又改成了,property<=>attribute;

所以property和attritude之间的数额绑定难点并不能够仅仅地以“property<-attribute”来说明。

总结

解析了如此多,对property和attribute的分别精通也越来越深了,在这里地总计一下:

创建

  • DOM对象初阶化时会在创造暗许的基本property;
  • 独有在HTML标签中定义的attribute才会被保留在property的attributes属性中;
  • attribute会起头化property中的同名属性,但自定义的attribute不会情不自禁在property中;
  • attribute的值都以字符串

数据绑定

  • attributes的数据会同步到property上,但是property的改动不会转移attribute;
  • 对此value,class那样的习性/个性,数据绑定的趋向是单向的,attribute->property
  • 对于id来说,数据绑定是双向的,attribute<=>property
  • 对此disabled来讲,property上的disabled为false时,attribute上的disabled必定会并留存,那时候数码绑定能够认为是双向的;

使用

  • 能够动用DOM的setAttribute方法来还要改良attribute;
  • 直接访谈attributes上的值会拿到三个Attr对象,而经过getAttribute方法访谈则会直接获取attribute的值;
  • 大许多情状(除非有浏览器宽容性难题),jQuery.attr是经过setAttribute实现,而jQuery.prop则会直接待上访问DOM对象的property;

到此处停止,得出,property是DOM对象自小编就全部的性质,而attribute是大家透过安装HTML标签而给之赋予的性状,attribute和property的同名属性/天性之间会时有产生局地特别的数码联系,而这几个联系会针对不相同的习性/本性有两样的区分。

其实,在那,property和attribute之间的差别和联系难以用简易的技巧特色来汇报,笔者在StackFlow上找到如下的答复,只怕会愈发雷同于真正的答案:

These words existed way before Computer Science came around.

Attribute is a quality or object that we attribute to
someone or something
. For example, the scepter is an attribute of
power and statehood.

Property is a quality that exists without any attribution. For
example, clay has adhesive qualities; or, one of the properties of
metals is electrical conductivity. Properties demonstrate themselves
though physical phenomena without the need attribute them to someone
or something. By the same token, saying that someone has masculine
attributes is self-evident. In effect, you could say that a property
is owned by someone or something.

To be fair though, in Computer Science these two words, at least for
the most part, can be used interchangeably – but then again
programmers usually don’t hold degrees in English Literature and do
not write or care much about grammar books
图片 1
.

最重大的两句话:

  • attribute(性格),是大家授予有些事物的特质或对象。
  • property(属性),是生龙活虎度存在的没有必要外部授予的特质。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website