Maison  >  Article  >  interface Web  >  Explication détaillée de la différence entre propriété et attribut en JavaScript

Explication détaillée de la différence entre propriété et attribut en JavaScript

黄舟
黄舟original
2017-03-11 15:23:401284parcourir

Propriété et attribut sont très faciles à confondre, et les traductions chinoises des deux mots sont également très similaires (propriété : attribut, attribut : caractéristique), mais en fait, ce sont des choses différentes et appartiennent à des catégories différentes.

  • property est un attribut dans le DOM et un objet en JavaScript ;

  • l'attribut est une fonctionnalité de la balise HTML, et sa valeur peut seulement c'est une chaîne

analyse la propriété et l'attribut basé sur JavaScript

Il existe un tel morceau de code en html :

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

Simplement sur la page html Créez un champ de saisie (notez qu'un attribut "sth" qui n'existe pas dans le DOM est ajouté à cette balise. A ce moment, exécutez l'instruction suivante en JS

var in1 = document.getElementById(&#39;in_1&#39;);
Exécutez l'instruction

console.log(in1);
À partir des résultats de l'impression de la console, vous pouvez voir que in1 contient un attribut nommé "attributs", son type est NamedNodeMap, et il y a aussi deux attributs de base "id" et " valeur", mais il n'y a pas de propriétés personnalisées "qch".

attributes: NamedNodeMap
value: "1"
id: "in_1"
Certaines consoles peuvent ne pas imprimer les attributs sur in1, vous pouvez alors exécuter la commande suivante pour imprimer les attributs à observer :

console.log(in1.id);		// &#39;in_1&#39;
console.log(in1.value);		// 1
console.log(in1.sth);		// undefined
Vous pouvez constater que les trois attributs dans l'étiquette, seuls "id" et "value" seront créés sur in1, mais "sth" ne sera pas créé. En effet, chaque objet DOM aura ses attributs de base par défaut, et lors de sa création, il ne créera que ces attributs de base. Les attributs que nous personnalisons dans la balise TAG ne seront pas

placés directement dans le DOM.

Faisons un test supplémentaire, créons une autre balise d'entrée et faisons quelque chose comme :

html :

<input id="in_2">
JS :

var in2 = document.getElementById(&#39;in_2&#39;);
console.log(in2);
Comme vous pouvez le voir sur les informations d'impression :

id: "in_2"
value: null
Bien que nous n'ayons pas défini de "valeur" dans TAG, puisqu'il s'agit de l'attribut de base par défaut du DOM, il sera toujours utilisé lors de la création du DOM. . De là, nous pouvons conclure :

  • DOM a ses propriétés de base par défaut, et ces propriétés sont ce qu'on appelle

    "propriété" , quoi qu'il arrive, elles seront Il est créé sur l'objet DOM lors de l'initialisation.

  • Si ces propriétés sont attribuées dans TAG, alors ces valeurs seront attribuées comme valeurs initiales à la propriété du même nom dans le DOM.

Revenant maintenant à la première entrée ("#in_1"), nous demanderons, où est passé "qch" ? Ne vous inquiétez pas, imprimons l'attribut attributs et voyons

console.log(in2);
Il y a plusieurs attributs dessus :

0: id
1: value
2: sth
length: 3
__proto__: NamedNodeMap
Il s'avère que "qch" est placé dans le. objet attributs. Cet objet Les attributs et le nombre d'attributs que nous avons définis dans le TAG sont enregistrés dans l'ordre. À ce stade, si vous imprimez les attributs de la deuxième balise d'entrée, vous constaterez qu'il n'y a qu'un seul attribut « id » et que la « longueur » est 1.

Comme vous pouvez le voir ici, les attributs sont un sous-ensemble de propriétés, qui enregistrent les attributs définis sur les balises HTML. Si vous explorez plus en détail chaque attribut dans les attitudes, vous constaterez qu'il ne s'agit pas de simples objets. Il s'agit d'un objet de type Attr avec NodeType, NodeName et d'autres attributs. Nous en reparlerons plus tard.

Notez que l'impression de l'attribut attribut n'obtiendra pas directement la valeur de l'objet, mais obtiendra une chaîne contenant le nom et la valeur de l'attribut, telle que :

console.log(in1.attibutes.sth);		// &#39;sth="whatever"&#39;
On peut ainsi conclure que :

  • Les attributs et valeurs définis dans la balise HTML seront enregistrés dans l'attribut attributs de l'objet DOM

    ;
  • Les attributs de ces attributs Le type en JavaScript est Attr, ce qui n'est pas aussi simple que d'enregistrer le nom et la valeur de l'attribut

Alors, quel sera le effet si on change les valeurs de propriété et d'attribut ? Exécutez l'instruction suivante :

in1.value = &#39;new value of prop&#39;;
console.log(in1.value);				// &#39;new value of prop&#39;
console.log(in1.attributes.value);	// &#39;value="1"&#39;
À ce moment, la valeur du champ de saisie sur la page devient "nouvelle valeur de prop", et la valeur dans la propriété devient également la nouvelle valeur, mais les attributs sont est toujours "1". On peut en déduire que les valeurs des propriétés portant le même nom de propriété et d'attribut ne sont pas liées dans les deux sens.

Quel serait l'effet si on fixait les valeurs dans les attitudes par contre ?

in1.attributes.value.value = &#39;new value of attr&#39;;
console.log(in1.value);				// &#39;new value of attr&#39;
console.log(in1.attributes.value);	// &#39;new value of attr&#39;
À ce moment, le champ de saisie de la page est mis à jour et la valeur de la propriété change également. De plus, le même résultat sera obtenu en exécutant l'instruction suivante

in1.attributes.value.nodeValue = &#39;new value of attr&#39;;
De là, on peut conclure :

  • la propriété peut être synchronisée from attribut ;

  • l'attribut ne synchronisera pas la valeur sur la propriété; La liaison est unidirectionnelle, attribut->propriété

  • La modification de toute valeur sur la propriété et l'attribut reflétera la mise à jour de la page HTML
  • Analyse des attributs et des propriétés basées sur jQuery
  • Et alors ? sont les méthodes attr et prop dans jQuery ?

  • Utilisez d'abord jQuery.prop pour tester

La valeur du champ de saisie est mise à jour, mais l'attribut n'est pas mis à jour.

Ensuite, utilisez jQuery.attr pour tester

$(in1).prop(&#39;value&#39;, &#39;new prop form $&#39;);

console.log(in1.value);				// &#39;new prop form $&#39;
console.log(in1.attributes.value);	// &#39;1&#39;
La valeur du champ de saisie est mise à jour, et la propriété et l'attribut sont mis à jour.

On peut déduire du phénomène de test ci-dessus que jQuery.attr et jQuery.prop sont fondamentalement les mêmes que les méthodes de fonctionnement natives. La propriété obtiendra la synchronisation à partir de l'attribut, mais l'attribut n'obtiendra pas la synchronisation à partir de l'attribut. la propriété. Alors, comment jQuery l’implémente-t-il ?

下面,我们来看看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调用

$().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(&#39;value&#39;, &#39;new attr from setAttribute&#39;);

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

执行完setAttribute以后,就如同直接更改attributes中的同名属性;
而getAttribute的结果与访问property的结果一模一样,而不会像直接访问attritudes那样返回一个Attr对象。

特殊的例子

href

然而,是不是所有标签,所有属性都维持保持这样的特性呢?下面我们看看href这个属性/特性。

首先在html中创建一个标签:

<a href=&#39;page_1.html&#39; id=&#39;a_1&#39;></a>

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

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

可以看到,property中保存的是绝对路径,而attribute中保存的是相对路径。那么,如果更改了这些值会发生什么情况呢?

更改attribute:

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

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

更改property:

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

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

从这里可以发现,href是特殊的属性/特性,二者是双向绑定的,更改任意一方,都会导致另一方的的值发生改变。而且,这并不是简单的双向绑定,property中的href永远保存绝对路径,而attribute中的href则是保存相对路径。

看到这里,attribute和property的区别又多了一点,然而,这又让人变得更加疑惑了。是否还有其他类似的特殊例子呢?

id

尝试改变property中的id:

	a1.id = &#39;new_id&#39;;
	console.log(a1.id);						// &#39;new_id&#39;
	console.log(a1.getAttribute(&#39;id&#39;));		// &#39;new_id&#39;

天呀,现在attribute中的id从property中的id发生了同步,数据方向变成了property attribute

disabled

再来看看disabled这个属性,我们往第一个添加“disabled”特性:

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

然后执行下面的代码:

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

改变attributes中的disabled不会改变更改property,也不会取消输入栏的禁用效果。

如果改成下面的代码:

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

又或者:

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

可以发现,将property中的disabled设置为false,会移除attributes中的disabled。这样数据绑定又变成了,propertyattribute;

所以property和attritude之间的数据绑定问题并不能单纯地以“property”来说明。

总结

分析了这么多,对property和attribute的区别理解也更深了,在这里总结一下:

Créer

  • Lorsque l'objet DOM est initialisé, la propriété de base par défaut sera créée

  • Seuls les attributs définis dans les balises HTML seront créés ; être créé Est enregistré dans l'attribut attributs de la propriété

  • l'attribut initialisera l'attribut du même nom dans la propriété, mais l'attribut personnalisé n'apparaîtra pas dans la propriété ;

  • Les valeurs d'attribut sont toutes des chaînes

liaison de données

    ;
  • Les données des attributs seront synchronisées avec la propriété, mais les modifications apportées à la propriété ne modifieront pas les attributs

  • Pour les attributs/caractéristiques tels que la valeur et la classe, la direction de la liaison des données est unidirectionnelle, attribute->property;

  • Pour id, la liaison des données est bidirectionnelle, attribut;

  • Pour désactivé, lorsque désactivé sur la propriété est faux, désactivé sur l'attribut existera définitivement à ce moment-là, la liaison de données. peut être considéré comme Bidirectionnel

Utilisez

  • pour modifier l'attribut en même temps en utilisant la méthode setAttribute de DOM;

  • L'accès direct à la valeur des attributs obtiendra un objet Attr, tandis que l'accès via la méthode getAttribute obtiendra directement la valeur de l'attribut

  • Dans la plupart des cas (sauf en cas de problèmes de compatibilité du navigateur), jQuery.attr est implémenté via setAttribute, tandis que jQuery.prop accédera directement à la propriété de l'objet DOM

Jusqu'à présent, on peut conclure que la propriété est l'objet DOM lui-même. Elle a des attributs, et les attributs sont des attributs que nous lui attribuons en définissant des balises HTML. Il y aura des connexions de données spéciales entre l'attribut et les attributs/caractéristiques de la propriété. même nom, et ces connexions seront différentes pour différents attributs/caractéristiques.

En fait, ici, la différence et le lien entre propriété et attribut sont difficiles à décrire avec de simples caractéristiques techniques. J'ai trouvé la réponse suivante sur StackFlow, qui est peut-être plus proche de la vraie réponse :

Ces mots existaient bien avant l'avènement de l'informatique.

L'attribut est une qualité ou un objet que nous attribuons à quelqu'un ou à quelque chose. Par exemple, le sceptre est un attribut de pouvoir et d'État. > Par exemple, l'argile a des qualités adhésives ou l'une des propriétés des métaux est la conductivité électrique. Ces propriétés se manifestent sans qu'il soit nécessaire de les attribuer à quelqu'un. ou quelque chose comme ça. De la même manière, dire que quelqu'un a des attributs masculins va de soi. En effet, on pourrait dire qu'une propriété appartient à quelqu'un ou à quelque chose.

Pour être juste. cependant, en informatique, ces deux mots, du moins pour la plupart, peuvent être utilisés de manière interchangeable – mais là encore, les programmeurs ne sont généralement pas diplômés en littérature anglaise et n'écrivent pas ou ne se soucient pas beaucoup des livres de grammaire

.Les deux phrases les plus critiques :

L'attribut (caractéristique) est la qualité ou l'objet que nous attribuons à quelque chose.

Explication détaillée de la différence entre propriété et attribut en JavaScript

La propriété est une caractéristique qui existe déjà et n'a pas besoin d'être donnée par le monde extérieur.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn