jQuery源码阅读8-1:属性操作prop()和access()

属性操作是比较独立的一块,
这次说下prop相关的, 和属性必须用的access 下次说更麻烦一点的attr,val 还有css相关的

以prop的一次操作为例, 调用顺序是: fn.prop -> access -> jQuery.prop

一:用法

1
2
3
4
5
6
7
8
/**
* 博文作者:ruantao1989@gmail.com
* 引自博客:ruantao.duapp.com/blog
*/
//==>源码2014行
prop: function( name, value ) {
return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//==>源码2416行
prop: function( elem, name, value ) {
var ret, hooks, notxml,
nType = elem.nodeType;
//以下nodeType不操作
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
//elem是选择出来的对象,不是xml
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
if ( notxml ) {
//propFix对象中出现的键,需要修改成值, 不需修改的 还用name
name = jQuery.propFix[ name ] || name;
//这的hook只对应"tabIndex"一个值, 针对tabIndex做了兼容处理
hooks = jQuery.propHooks[ name ];
}
//有value就是要写入
if ( value !== undefined ) {
//其实看源代码propHooks没set
if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
//dom对象修改属性值
return ( elem[ name ] = value );
}
//没value,读取
} else {
//如果之前生成钩子,钩子里又有get可用,且钩子运行完有结果, 则返回结果
if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
//dom对象读取属性值
return elem[ name ];
}
}
},

负责在中间层调度的access最后再说, 这中间要经过几个判断

1
2
3
4
5
6
7
8
9
//==>源码4020行, 选择器中的isXML判断
isXML = Sizzle.isXML = function( elem ) {
// documentElement is verified for cases where it doesn't yet exist
//如果elem为某个元素,并且有document超父级对象,如果没有,就认为elem为document对象
var documentElement = elem && (elem.ownerDocument || elem).documentElement;
//没有documentElement直接返回false
//判断其node名称是否为html,是html返回false,不是返回true
return documentElement ? documentElement.nodeName !== "HTML" : false;
};

之后是prop的钩子(钩子的设计不赖, 一下吧所有特殊处理都抽象出来的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//propHooks只针对tabIndex做了兼容处理
//其余的属性都用不上这个钩子
propHooks: {
tabIndex: {
get: function( elem ) {
var attributeNode = elem.getAttributeNode("tabindex");
//对取出来的tabindex多特殊处理
return attributeNode && attributeNode.specified ?
parseInt( attributeNode.value, 10 ) :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
undefined;
}
}
}

最后说下access, 这个东西把所有属性相关的操作都统一接口了, 判断的情况巨多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//==>源码786行
//作为中间层调度各种读写
//拿attr来说这样调用: jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
var exec,
bulk = key == null,//bulk是判断(key == null),读取时候key等于属性id
i = 0,
length = elems.length;
//传入的key要是数组或者对象, 就遍历对象属性, 分别access
if ( key && typeof key === "object" ) {
for ( i in key ) {
jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
}
chainable = 1;
//写入单一属性
} else if ( value !== undefined ) {
//value传入一个function, 且pass传值的时候, exec改为pass的传值
exec = pass === undefined && jQuery.isFunction( value );
//写入模式
if ( bulk ) {
//仅在attr(function(){},true)时用
if ( exec ) {
exec = fn;//attr(function(){},true)中的function
fn = function( elem, key, value ) {
return exec.call( jQuery( elem ), value );//调call
};
//正常情况下的set
} else {
//fn一般就是jQuery.attr
fn.call( elems, value );
fn = null;
}
}
//如果elems是数组, 组装参数, 遍历调用fn
if ( fn ) {
for (; i < length; i++ ) {
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
}
}
chainable = 1;
}
return chainable ?
//写入时chainable传值就true, 遍历后会改成1
//chainable的值是arguments.length > 1, 光传对象id就是读, 后边跟值就是写
elems :
//读取模式
bulk ?
fn.call( elems ) :
//没有elems 返回emptyGet(是undefined), 有elems调==>源码2275的attr:
length ? fn( elems[0], key ) : emptyGet;
},