Property和Attribute的区别

Posted by Qz on December 17, 2017

“Yeah It’s on. ”

正文

网页链接

property 和 attribute非常容易混淆,两个单词的中文翻译也都非常相近(property:属性,attribute:特性),但实际上,二者是不同的东西,属于不同的范畴。

  • property是DOM中的属性,是JavaScript里的对象;
  • Attribute就是dom节点自带的属性,例如html中常用的id、class、title、align等,它的值只能够是字符串;

只要是DOM标签中出现的属性(html代码),都是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中的

  • DOM有其默认的基本属性,而这些属性就是所谓的“property”,无论如何,它们都会在初始化的时候再DOM对象上创建。
  • 如果在TAG对这些属性进行赋值,那么这些值就会作为初始值赋给DOM的同名property。

回到第一个input(“#in_1”),我们就会问,“sth”去哪里了?别急,我们把attributes这个属性打印出来看看

NamedNodeMap {0: id, 1: value, 2: sth, length: 3}
length:3
0:id
1:value
2:sth
__proto__:NamedNodeMap

原来“sth”被放到了attributes这个对象里面,这个对象按顺序记录了我们在TAG中定义的属性和属性的数量

attributes是属于property的一个子集,它保存了HTML标签上定义属性

可以得出:

  • 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"'

propety中的value也变成了新的值,但attributes却仍然是“1”。从这里可以推断,property和attribute的同名属性的值并不是双向绑定的。

如果反过来,设置attitudes中的值,效果会怎样呢?

in1.attributes.value.value

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'

都发生了改变

执行下面语句也会得到一样的结果 in1.attributes.value.nodeValue = ‘new value of attr’;

由此,可得出结论:

  • property能够从attribute中得到同步;
  • attribute不会同步property上的值;
  • attribute和property之间的数据绑定是单向的,attribute->property;
  • 更改property和attribute上的任意值,都会将更新反映到HTML页面中;

基于jQuery分析attribute和property

首先利用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都更新了。

特殊的例子

href

下面我们看看href这个属性/特性。

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

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

var a1 = document.getElementById('a_1');
console.log(a1.href);	// 'file:///D:/GitHub/JS/html/test_01/page_1.html'
console.log(a1.getAttribute('href'));	// 'page_1.html'

更改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则是保存相对路径。

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;

总结

创建

  • 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;

  • attribute(特性),是我们赋予某个事物的特质或对象。
  • property(属性),是早已存在的不需要外界赋予的特质。

  • 特性和属性两者的存储方式不同;
  • “脚踏两只船”要了解;
  • DOM属性可能会导致循环引用内存泄漏。

补充

Attribute取值&赋值

getAttribute()可以取得任何特性,不管是标准的还是自定义的。

但是这个方法的浏览器兼容性有问题,有些浏览器可能会获取属性Property的值,因此jQuery要做一个测试,看getAttribute()是否是绝对获取特性Attribute的值。

div1.className = 'a';
var judge = div1.getAttribute("className") === 'a';

如果以上代码成立,说明getAttribute()方法出现了问题,将不再使用。

用setAttrbute()赋值,任何Attribute都可以,包括自定义的。而且,赋值的Attribute会立刻表现到DOM元素上。

最后注意,setAttribute()的两个参数,都必须是字符串。即对特性Attribute职能赋值字符串,而对属性Property就可以赋任何类型的值了。

Property取值&赋值

属性取值很简单。取任何属性的只,用“.”就可以:

var id = div1.id;
var className = div1.className;
var childNodes = div1.childNodes;
var attrs = div1.attributes;

赋值和基本的js对象属性赋值一样,用“.”即可:

div1.className = 'a';
div1.align = 'center';
div1.AAAAA = true;
div1.BBBBB = [1, 2, 3];

再次强调: class特性在变成属性时,名字改成了“className”,因此div1.className和div1.getAttrbute(‘class’)相同。

上面代码中的div1.attributes是取的attributes这一属性,取出来保存到attrs变量中,attrs就成了一个NamedNodeList类型的对象,里面存储了若干个Attr类型。

对于属性Property的赋值在IE中可能会引起循环引用,内存泄漏。为了防止这个问题,jQuery.data()做了特殊处理,解耦了数据和DOM对象

style和onclick

用“.”获取Style:

<div id="div1" class="divClass" style="width:100%; padding:10px;">100</div>

console.log(div1.style);

以上代码中,返回了一个CSSStyleDeclaration对象,这个对象中包含着样式的所有信息

用getAttribute()获取style

<div id="div1" class="divClass" style="width:100%; padding:10px;">100</div>

console.log(div1.getAttribute("style"));

以上代码返回的就是一个简单的字符串:“width:100%; padding:10px;”