zepto 对象思想与源码分析
原型对象
Array.prototype
每一个函数,都有一个 prototype 属性,不管是你自定义的,还是函数内置的。
1 2 3
| var fn = function() {}; console.log(fn.prototype); console.log(fn.prototype.constructor === fn);
|
这里的 fn.prototype
打印出一个对象,对象里的 constructor
属性又指回了该函数本身 fn。
即每一个原型对象都有一个 consctructor 属性指向关联的构造函数,比如:
1
| Array.prototype.constructor === Array;
|
我们接着看:
1
| console.log(Array.prototype);
|
这里,除了 constructor 属性,还有其他内置的属性,即我们经常使用的操作数组的方法。
__proto__
(隐式原型)
所有通过函数 new
(构造函数)出来的实例对象,都有一个 __proto__
属性,指向该对象的 prototype
,比如:
1 2
| var arr = new Array(); arr.__proto__ === Array.prototype;
|
原型链:由相互关联的原型(__proto__
)组成的链状结构就是原型链。
举个关于继承 extends 的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function Animal() { this.eat = function() { console.log("Animal eat"); }; } function Dog() { this.bark = function() { console.log("Dog bark"); }; }
Dog.prototype = new Animal(); var dog = new Dog();
dog.eat(); dog.bark();
|
上面看明白了,那么 ES6 的继承我们也就可以明白原理了,即 class Dog extends Animal
相当于 Dog.prototype = new Animal()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Animal { constructor(name) { this.name = name; } eat() { console.log("Animal eat!" + this.name); } } class Dog extends Animal { constructor(name) { super(name); this.name = name; } bark() { console.log("Dog bark!" + this.name); } }
const dog = new Dog("哈士奇"); dog.eat(); dog.bark();
|
接下来,我们就清楚为什么能这样:
当我们要使用一个对象(数组)的某个功能时,如果该对象本身具有这个功能,直接调用,没有的话,那就去自身的__proto__
属性中去找
1 2 3 4 5 6 7 8 9 10
| var obj = { myfn: function() { console.log("myfn"); } }; obj.myfn(); obj.hasOwnProperty("myfn"); obj.toString(); obj.hasOwnProperty("toString"); obj.__proto__.hasOwnProperty("toString");
|
hasOwnProperty()
就可以得出这个属性是否是属于该对象本身的属性:
- myfn 是我们自定义的,
obj.hasOwnProperty('myfn')
为 true
- toString() 我们不是自定义的,却可以使用,查一下是否属于自定义属性,
obj.hasOwnProperty('toString')
,答案为 false
- 既然不属于自定义属性,那就去自身的
__proto__
去找,然后去原型对象上查一下,obj.__proto__.hasOwnProperty('toString')
,哦,原来在这儿
在源码中,我们经常看到Array.prototype.concat
,其实就是我们使用的[].concat
,[]
,因为[].__proto__ === Array.prototype
__proto__
是可修改的
比如,我们新增一个addClass()
方法:
1 2 3 4 5 6
| var arr = [1, 2, 3]; arr.__proto__.addClass = function() { console.log(123); }; arr.push(4); arr.addClass();
|
但是,这里要注意,如下重写之后,就没有了诸如 push、concat 等方法:
1 2 3 4 5 6
| arr.__proto__ = { addClass: function() { console.log(123); } }; arr.push(3);
|
Object.prototype
的原型
万物皆对象,到最后依旧是对象,最后这个东东是个啥,我们来看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function Person() {}
var myfn = new Person(); myfn.__proto__ === Person.prototype;
Person.prototype.__proto__ === Object.prototype;
Function.prototype.__proto__ === Object.prototype;
var fn = function() {}; fn.prototype.__proto__ === Object.prototype; Object.prototype.__proto__ === null; typeof null;
|
总结:
所有的函数都有一个 prototype
属性,该属性指向了一个对象,该对象就是调用该构造函数而创建出来的实例(如 myfn)的原型(如myfn.__proto__
),即:myfn.__proto__ === Person.prototype
所有的对象(除 null)都具有一个__proto__
属性,该属性指向该对象的原型,比如:myfn.__proto__ === Person.prototype
原型也是一个对象,根据上条,那原型的原型,就是Object.prototype
最后的 null 对象,可以当做是 什么都没有
盗一张图,我们就更加清楚了(蓝色这条表示的是原型链)
参考资料:
Zepto 对象设计
主体设计
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
| var Zepto = (function() { var $, zepto = {};
zepto.Z = function(dom, selector) { dom = dom || []; dom.__proto__ = $.fn; dom.selector = selector || ""; return dom; };
zepto.init = function(selector, context) { var dom;
return zepto.Z(dom, selector); };
$ = function(selector, context) { return zepto.init(selector, context); };
$.fn = { forEach: emptyArray.forEach, concat: function() {} };
return $; })();
window.Zepto = Zepto; window.$ === undefined && (window.$ = Zepto);
|
自定义模拟
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
| (function(window) { var $, zepto = {};
function Z(dom, selector) { var i, len = dom.length ? dom.length : 0; for (var i = 0; i < dom.length; i++) { this[i] = dom[i]; } this.length = len; this.selector = selector || ""; }
zepto.Z = function(dom, selector) { return new Z(dom, selector); };
zepto.init = function(selector) { var slice = Array.prototype.slice; var dom = slice.call(document.querySelectorAll(selector));
return zepto.Z(dom, selector); };
$ = function(selector) { return zepto.init(selector); };
$.fn = { css: function(key, value) { console.log("css"); }, html: function(value) { console.log("html"); } };
Z.prototype = $.fn;
window.$ = $; })(window);
|
流程:$() -> zepto.init() -> zepto.Z() -> new Z() -> Z.prototype = $.fn;
最新版(v1.2.0)模拟
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| (function(global, factory) { if (typeof define === "function" && define.amd) { define(function() { return factory(global); }); } else { factory(global); } })(this, function(window) { var Zepto = (function() { var $; var zepto = {};
function Z(dom, selector) { var i, len = dom ? dom.length : 0; for (i = 0; i < len; i++) this[i] = dom[i]; this.length = len; this.selector = selector || ""; } zepto.Z = function(dom, selector) { return new Z(dom, selector); };
zepto.init = function(selector, context) { var slice = Array.prototype.slice; var dom = slice.call(document.querySelectorAll(selector));
return zepto.Z(dom, selector); };
$ = function(selector, context) { return zepto.init(selector, context); };
$.fn = { constructor: zepto.Z, // 手动绑定 constructor 属性 length: 0, push: "push method", css: function() { console.log("css"); return this; }, html: function() { console.log("html") return this; } }; $.fn.on = function(event, data, callback) { console.log("on method"); }; $.fn.off = function(event, data, callback) { console.log("off method"); };
$.fn.bind = function(event, data, callback) { return this.on(event, data, callback); }; $.fn.unbind = function(event, callback) { return this.off(event, callback); };
zepto.Z.prototype = Z.prototype = $.fn;
$.zepto = zepto; return $; })();
window.Zepto = Zepto; window.$ === undefined && (window.$ = Zepto); });
|
amd 规范:
1 2 3 4 5 6 7 8 9
| (function(global, factory) { if (typeof define === "function" && define.amd) { define(function() { return factory(global); }); } else { factory(global); } })(this, function(window) {})
|
学习资料