JavaScriptのプロパティと属性の違いを詳しく解説

黄舟
リリース: 2017-03-11 15:23:40
オリジナル
1306 人が閲覧しました

プロパティと属性は非常に混同しやすく、この 2 つの単語の中国語訳も非常に似ています (プロパティ: 属性、属性: 特性) が、実際には、これらは別のものであり、異なるカテゴリに属します。

  • プロパティはDOMの属性であり、JavaScriptのオブジェクトです。

  • 属性はHTMLタグの機能であり、その値は文字列のみです。

はJavaScriptに基づいてプロパティと属性を分析します。 HTMLにはこんなコードがあります:

<input id="in_1" value="1" sth="whatever">
ログイン後にコピー

単純にHTMLページに入力フィールドを作成します(このとき、DOMには存在しない属性「sth」がこのタグに追加されることに注意してください)。 JS の次のステートメント

var in1 = document.getElementById(&#39;in_1&#39;);
ログイン後にコピー

Execute ステートメント

console.log(in1);
ログイン後にコピー

コンソールの出力結果から、in1 には「attributes」という名前の属性が含まれており、そのタイプは NamedNodeMap であり、さらに 2 つの基本属性「id」と「value」があることがわかります。 " がありますが、カスタム属性である "sth" はありません。

attributes: NamedNodeMap
value: "1"
id: "in_1"
ログイン後にコピー

一部のコンソールでは in1 の属性が出力されない場合があります。その場合は、次のコマンドを実行して、観察したい属性を出力できます:

console.log(in1.id);		// &#39;in_1&#39;
console.log(in1.value);		// 1
console.log(in1.sth);		// undefined
ログイン後にコピー

ラベル内の 3 つの属性のうち、"id" と " のみを見つけることができます。 value" は in1 に作成されますが、"sth" は作成されません。これは、各 DOM オブジェクトにはデフォルトの基本属性があり、作成時に TAG タグ内でカスタマイズした属性のみが DOM に直接配置されないためです。

追加のテストを行って、別の入力タグを作成し、次のようなことを実行しましょう: html:

<input id="in_2">
ログイン後にコピー

JS:

var in2 = document.getElementById(&#39;in_2&#39;);
console.log(in2);
ログイン後にコピー

印刷された情報からわかるように、たとえ私たちがTAG 「値」を定義しますが、DOM のデフォルトの基本属性であるため、DOM の初期化時にも作成されます。このことから、次のように結論付けることができます:

DOM にはデフォルトの基本プロパティがあり、これらのプロパティはいわゆる

「プロパティ」

です。いずれの場合も、初期化中に DOM オブジェクト上に作成されます。

  • TAGでこれらのプロパティに値を割り当てると、これらの値はDOM内の同じ名前のプロパティに初期値として割り当てられます。

  • ここで最初の入力 (「#in_1」) に戻り、「sth」はどこに行ったのかを尋ねます。心配しないで、attributes 属性を出力して見てみましょう
  • id: "in_2"
    value: null
    ログイン後にコピー

    それにはいくつかの属性があります:

    console.log(in2);
    ログイン後にコピー
  • "sth" が Attributes オブジェクトに配置されていることがわかります。このオブジェクトは、で定義した属性と属性を記録します。 TAG の順序。このとき、2つ目のinputタグの属性を出力してみると、「id」属性は1つだけで、「length」は1であることがわかります。

ここからわかるように、属性はプロパティのサブセットであり、HTML タグに定義された属性を保存します。態度の各属性をさらに詳しく調べてみると、これらは単純なオブジェクトではなく、NodeType、NodeName、その他の属性を持つ Attr 型のオブジェクトであることがわかります。これについては後で詳しく説明します。

注意

、attribute 属性の出力はオブジェクトの値を直接取得するのではなく、次のような属性の名前と値を含む

string

を取得することに注意してください:

0: id
1: value
2: sth
length: 3
__proto__: NamedNodeMap
ログイン後にコピー

これから次のように結論付けることができます: HTML タグ内では、合計と値は DOM オブジェクトのattributes プロパティに保存されます JavaScript におけるこれらの属性の型は Attr であり、属性名と値を保存するだけではありません

    。では、プロパティと属性を変更すると、その値はどうなるでしょうか?次のステートメントを実行します:
  • console.log(in1.attibutes.sth);		// &#39;sth="whatever"&#39;
    ログイン後にコピー

    このとき、ページ上の入力フィールドの値は「propの新しい値」になり、プロパティの値も新しい値になりますが、属性は「1」のままです。ここから、プロパティと属性の同じ名前を持つプロパティの値は双方向バインドされていないことが推測できます。

  • 一方、態度に価値を設定すると、どのような効果があるでしょうか?
  • 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;
    ログイン後にコピー

    このとき、ページ内の入力フィールドが更新され、プロパティの値も変更されています。さらに、次のステートメントを実行すると同じ結果が得られます

    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;
    ログイン後にコピー
  • これから、次の結論を導き出すことができます:

プロパティは属性から同期できます

属性はプロパティの値を同期しません

;
  • 属性とプロパティ間のデータバインディングは一方向であり、属性->プロパティです。

  • プロパティと属性の値を変更すると、HTML ページの更新が反映されます。

    属性を分析し、 jQuery に基づくプロパティ
  • それでは、jQuery の attr メソッドと prop メソッドとは何でしょうか?

    まず jQuery.prop を使用してテストします
  • in1.attributes.value.nodeValue = &#39;new value of attr&#39;;
    ログイン後にコピー
  • 入力フィールドの値は更新されますが、属性は更新されません。

    次に jQuery.attr を使用してテストします
  • $(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;
    ログイン後にコピー
入力フィールドの値が更新され、プロパティと属性の両方が更新されます。

上記のテスト現象から、jQuery.attr と jQuery.prop は基本的にネイティブの操作メソッドと同じであると推測できますが、プロパティは属性から同期を取得しますが、属性はプロパティから同期を取得しません。では、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调用

$().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。这样数据绑定又变成了,property<=>attribute;

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

总结

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

  • DOM オブジェクトを作成すると、デフォルトの基本プロパティが作成されます。

  • HTML タグで定義された属性のみがプロパティの

    attributes 属性に保存されます。プロパティ内に同じ名前の属性がありますが、カスタム属性はプロパティに表示されません。

  • 属性の値はすべて

  • データバインディング

    属性データは同期されます。ただし、プロパティを変更しても属性は変更されません。

値やクラスなどの属性/機能の場合、データ バインディングの方向は一方向です。
    attribute->property
  • ;

    id
  • の場合、データ バインディングは双方向です。
  • attribute<=>property

    ;

  • 無効の場合、プロパティの無効が false の場合、属性の無効は必ず存在します。
  • を使用すると、DOM の setAttribute メソッドを使用して、同時に属性を変更できます。属性の値は Attr オブジェクトを取得し、 getAttribute メソッドを通じてアクセスします。 属性の値は直接取得されます

  • ほとんどの場合 (ブラウザーの互換性の問題がない限り)、jQuery.attr は setAttribute を通じて実装されます。一方、jQuery.prop は DOM オブジェクトのプロパティに直接アクセスします
  • これまでのところ、property は DOM オブジェクト自体が所有するプロパティであり、attribute は HTML タグを設定することで付与されるプロパティであると結論付けることができます。属性と、同じ名前のプロパティのプロパティ/特性の間には、いくつかの特別なデータ接続が存在します。また、これらの接続は、異なる属性/特性では異なります。

  • 実際、ここで、プロパティと属性の違いと関係を単純な技術的特性で説明するのは困難です。私は StackFlow で次の答えを見つけました。これは実際の答えに近いかもしれません:

    これらの言葉は、コンピューターよりずっと前から存在していました。科学が登場しました。
    • 属性

      は、私たちが誰かまたは何かに帰属する
    • 性質
    • です。例えば、笏は権力と国家の属性です。

    • 財産

      という性質です。たとえば、粘土には粘着性があります。あるいは、金属の特性の 1 つは、誰かまたは何かのせいである必要がなくても、それ自体を示します。男性的な属性を持つということは自明のことですが、事実上、プロパティは誰かまたは何かによって所有されていると言えます

    ただし、公平を期すために言うと、これら 2 つの単語は、少なくともほとんどの場合使用できます。同じ意味で – しかし、繰り返しになりますが、プログラマーは通常、英文学の学位を取得しておらず、文法書を書いたり、文法書についてあまり気にしたりしません

    .

    最も重要な 2 つの文:

    属性 (特性)、それは品質または私たちが何かに帰属するオブジェクト。 プロパティ(財産)とは、すでに存在しており、外界から与える必要のない特性です。

    以上がJavaScriptのプロパティと属性の違いを詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート