underscore.js源码 Underscore.js
没有对原生 JavaScript
对象进行扩展,而是通过调用 _()
方法进行封装,一旦封装完成,原生 JavaScript
对象便成为一个 Underscore
对象。
判断给定变量是否是对象 1 2 3 4 5 _.isObject = function(obj) { var type = typeof obj; return type === 'function ' || type === 'object ' && !!obj; };
这是underscore.js
的判断给定变量是否是object
的一段源码。 我们知道typeof
会返回如下六个值:
1. 'undefined' --- 这个值未定义;
2. 'boolean' --- 这个值是布尔值;
3. 'string' --- 这个值是字符串;
4. 'number' --- 这个值是数值;
5. 'object' --- 这个值是对象或null;
6. 'function' --- 这个值是函数。
而&&
的优先级要高与||
。!!
的作用相当于Boolean()
,将其转换为布尔值。
判断给定值是否是DOM元素 1 2 3 4 _.isElement = function (obj) { return !!(obj && obj.nodeType === 1 ); };
同样!!
相当于Boolean()
的作用,nodeType === 1
则说明是元素节点,属性attr
是2 ,文本text
是3
1 2 3 4 5 6 7 8 9 <body > <p id ="test" > 测试</p > <script > var t = document .getElementById('test' ); alert(t.nodeType); alert(t.nodeName); alert(t.nodeValue); </script > </body >
firstChild
属性
1 2 3 4 var t = document .getElementById('test' ).firstChild;alert(t.nodeType); alert(t.nodeName); alert(t.nodeValue);
文本节点也算是一个节点,所以p的子节点是文本节点,所以返回3
zepto源码 判断是否是数组 1 2 3 4 5 6 7 8 9 isArray = Array .isArray || function (object ) { return object instanceof Array } `Array.isArray()` 方法:如果一个对象是数组就返回`true` ,如果不是则返回`false` 。`instanceof` 用于判断一个变量是否某个对象的实例,如 var a= []; alert(a instanceof Array );
同时 alert(a instanceof Object)
也会返回 true
isArray
返回布尔值,如果Array.isArray
为true
,则返回true
,否则返回object instanceof Array
的结果。
数据类型判断 1 2 3 4 5 6 7 8 9 10 11 class2type = {}, function type (obj ) { return obj == null ? String (obj) : class2type[toString.call(obj)] || "object" } function isFunction (value ) { return type(value) == "function" } function isWindow (obj ) { return obj != null && obj == obj.window } function isDocument (obj ) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } function isObject (obj ) { return type(obj) == "object" }
class2type
是一个空对象,实际上一个什么都没有的空对象是这样创建的Object.create(null);
我们可以通过Object.prototype.toString.call()
方法来判断数据类型,例如:
1 2 3 4 5 6 7 console.log (Object.prototype.toString .call (123 )) console.log (Object.prototype.toString .call ('123' )) console.log (Object.prototype.toString .call (undefined)) console.log (Object.prototype.toString .call (true )) console.log (Object.prototype.toString .call ({})) console.log (Object.prototype.toString .call ([])) console.log (Object.prototype.toString .call (function(){}))
首先如果参数obj
是undefined
或null
,则通过String(obj)
转换为对应的原始字符串“undefined
”或“null
”。
然后class2type[toString.call(obj)]
首先借用Object
的原型方法toString()
来获取obj
的字符串表示,返回值的形式是 [object class]
,其中的class
是内部对象类。
然后从对象class2type
中取出[object class]
对应的小写字符串并返回;如果未取到则一律返回“object
。
get方法 1 2 3 get : function (idx) { return idx === undefined ? slice.call(this ) : this [idx >= 0 ? idx : idx + this .length] },
取集合中对应指定索引的值,如果idx
小于0,则idx
等于idx+length
,length
为集合的长度.
可能你刚看到slice.call(this)
会觉得很纳闷,其实不仅是zepto.js
的源码,包括jQuery
,backbone
的源码都是这么写的,只不过它们在最开头做了声明:
1 2 3 var push = array .push;var slice = array .slice;var splice = array .splice;
所以slice.call(this)
其实还是Array.slce.call(this)
prototype.js源码 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 addClassName: function (element, className ) { element = $(element); Element.removeClassName(element, className); element.className += ' ' + className; }, removeClassName: function (element, className ) { element = $(element); if (!element) return ; var newClassName = '' ; var a = element.className.split(' ' ); for (var i = 0 ; i < a.length; i++) { if (a[i] != className) { if (i > 0 ) newClassName += ' ' ; newClassName += a[i]; } } element.className = newClassName; },
因为addClassName
依赖于removeClassName()
,所以先分析后者,$()
是先将元素封装成prototype
对象,
if(!element) return
这句的意思就是如果元素对象不存在,则忽略不再继续执行的意思,也就是终止的意思。
split() 方法用于把一个字符串分割成字符串数组。
如果把空字符串 (""
) 用作 分隔符,那么 该对象 中的每个字符之间都会被分割。
判断是否拥有 class 属性值 1 2 3 4 5 6 7 8 9 10 11 12 13 hasClassName: function (element, className ) { element = $(element); if (!element) return ; var a = element.className.split(' ' ); for (var i = 0 ; i < a.length; i++) { if (a[i] == className) return true ; } return false ; },
兼容旧版本浏览器增加Array的push方法 1 2 3 4 5 6 7 8 9 10 11 * 为兼容旧版本的浏览器增加 Array 的 push 方法。 */ if (!Array .prototype.push) { Array .prototype.push = function ( ) { var startLength = this .length; for (var i = 0 ; i < arguments .length; i++) this [startLength + i] = arguments [i]; return this .length; } }
!Array.prototype.push
如果为true
,说明浏览器不支持该方法,则往下执行。this[startLength + i] = arguments[i]
将传递进来的每个参数依次放入数组中,最后返回数组的长度
访问对象可以使用(.)
表示法,也可以使用[]
来访问,同样访问数组元素也是
jQuery
源码jQuery
源码太多关联了,所以不好单独拿出来做分析,就举一两个简单的例子吧:
toArray
方法1 2 3 4 5 jQuery.prototype = { toArray: function ( ) { return slice.call( this ); }, }
Array.prototype.slice.call(arguments)
能将具有length
属性的对象转成数组,也就是说其目的是将arguments
对象的数组提出来转化为数组。例如:
1 2 3 4 <script > var a = {length:4 ,0 :'zero' ,1 :'one' ,2 :'two' }; console .log(Array .prototype.slice.call(a)); </script >
slice
有两个用法,一个是String.slice
,一个是Array.slice
,第一个返回的是字符串,第二个返回的是数组。
Array.prototype.slice.call(arguments)
能够将arguments
转成数组,那么就是arguments.toArray().slice()
;
因为arguments
并不是真正的数组对象,只是与数组类似而已,所以它并没有slice
这个方法,而Array.prototype.slice.call(arguments)
可以理解成是将arguments
转换成一个数组对象 ,让arguments
具有slice()
方法。 比如:
1 2 var arr = [1 ,2 ,3 ,4 ]; console.log (Array.prototype.slice.call(arr,2 ));
Array
这是我们想要的基对象名称
prototype
这可以被认为是一个数组的实例方法的命名空间
slice
这提取数组的一部分并返回新的数组,并没有开始和结束索引,它只是返回一个数组的拷贝
call
这是一个非常有用的功能,它允许你从一个对象调用一个函数并且使用它在另一个上下文环境
下面的写法是等效的:
1 Array.prototype .slice .call == [].slice .call
看这个例子:
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 object1 = { name:'frank' , greet:function ( ) { alert('hello ' +this .name) } }; object2 = { name:'trigkit4' }; object1.greet.call(object2); 分解一下就是`object1.greet` 运行弹出`hello + 'this.name'` ,然后`object2` 对象冒充,`this` 就指代`object2` var t = function ( ) { console .log(this ); console .log(typeof this ); console .log(this instanceof String ); }; t.call('trigkit4' ); `call(this)` 指向了所传进去的对象。
在Object.prototype
中已经包含了一些方法:
1 2 3 4 5 6 7 8 9 10 11 12 1 .toString ( )2 .toLocaleString ( )3 .valueOf ( )4 .hasOwnProperty (V)5 .isPrototypeOf (V)6 .propertyIsEnumerable (V)
on方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 jQuery.fn.extend ({ on: function ( types, selector, data , fn , one ) { var type, origFn // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined } } }) jQuery.extend (object) :为扩展jQuery类本身.为类添加新的方法。jQuery.fn.extend (object) :给jQuery对象添加方法。
!=
在表达式两边的数据类型不一致时,会隐式转换为相同数据类型,然后对值进行比较.!==
不会进行类型转换,在比较时除了对值进行比较以外,还比较两边的数据类型, 它是恒等运算符===
的非形式。
on : function(){}
是js
对象字面量的写法
{键:值,键:值}
语法中的“健/值”
会成为对象的静态成员。如果给某个“健”指定的值是一个匿名函数,那么该函数就会变成对象的静态方法;否则就是对象的一个静态属性。
jQuery类型判断 1 2 3 4 5 6 7 8 type: function ( obj ) { if ( obj == null ) { return obj + "" ; } return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call(obj) ] || "object" : typeof obj; },
前面已经分析了,class2type = {};
所以class2type[ toString.call(obj) ]
={}.toString.call(obj)
。它的作用是改变toString
的this
指向为object
的实例。