- var G = function() {
- if (this instanceof G) {
- return this;
- } else {
- return new G();
- }
- };
把 G 的构造函数改造一下,判断 this 是否是当前 G 函数的实例,如果是, 直接返回,如果不是,返回 new G() 这样根据原型对象的查找原则,就能确保调用到 size 方法
完整的代码:
- var G = function() {
- if (this instanceof G) {
- return this;
- } else {
- return new G();
- }
- };
- G.prototype = {
- length: 5,
- size: function() {
- return this.length;
- }
- }
- console.log(G().size());
在 jquery 框架中,他是怎么做的?
- var G = function() {
- return G.fn;
- };
- G.fn = G.prototype = {
- length: 5,
- size: function() {
- return this.length;
- }
- }
- console.log(G.prototype.size()); //5
- console.log(G().size()); //5
在 jquery 中, 为函数 G 增加一个属性 fn(记住:在 js 中函数是对象,这其实就是给对象增加了一个属性),然后在 G 函数中返回 G.fn 就是就是返回 G.prototype
那么用 G().size 起始就是相当于调用 G.prototype.size()
二、在 jquery 中,这个构造函数一般是用来选择元素的。
G 返回的是 G 的原型对象,我们要增加选择元素的功能,自然而然,就是往原型对象上添加:
- var G = function(id) {
- return G.fn.init(id);
- };
- G.fn = G.prototype = {
- init: function(id) {
- return document.getElementById(id);
- },
- length: 5,
- size: function() {
- return this.length;
- }
- }
向 G 的原型对象上添加一个 init 方法, 然后在构造函数中调用
- window.onload = function() {
- console.log(G('box'));
- G('box').style.backgroundColor = 'red';
- // G('box').size(); //报错,无法链式调用
- }
- < div id = "box" > ghost wu tell you how to learn design pattern < /div>/
虽然通过 init 方法,能够选择到 dom 元素,但是不能实现链式调用, 因为 G('box') 返回的是一个 dom 对象, 而在 dom 对象上是没有 size 这个方法的,因为 size 是 G.prototype 上的
所以要实现链式调用,就要确保 init 方法返回的是 G 的实例或者 G.prototype, 这个时候,this 就可以派上用场了
- <script>
- var G = function(id) {
- return G.fn.init(id);
- };
- G.fn = G.prototype = {
- init: function(id) {
- this[0] = document.getElementById(id);
- this.length = 1;
- return this;
- },
- length: 0,
- size: function() {
- return this.length;
- }
- }
- window.onload = function() {
- console.log(G('box'));
- console.log(G('box2').size());
- }
- </script>
- <div id="box">ghost wu tell you how to learn design pattern</div>
- <div id="box2">id为box2的第二个div</div>
把选择到的元素放在 this 中, 这个时候的 this 指向的是 G.fn,G.prototype
因为在构造函数中,是这样调用的: G.fn.init(id), 我把 G.fn 标成红色, 也就是相当于 G.fn 是一个对象,没错他确实就是一个对象 G.prototype,所以在 init 中的 this 指向的就是
init 方法前面的对象 (G.fn, G.prototype).
三、this 覆盖
接下来,就会产生一个问题, this 共用之后,元素选择就会产生覆盖
- <script>
- var G = function (id) {
- return G.fn.init(id);
- };
- G.fn = G.prototype = {
- init: function (id) {
- this[0] = document.getElementById(id);
- this.length = 1;
- console.log( this === G.fn, this === G.prototype, this );
- return this;
- },
- length: 0,
- size: function () {
- return this.length;
- }
- }
- window.onload = function(){
- console.log( G( 'box' ) );
- console.log( G( 'box2' ) );
- }
- </script>
- <div id="box">ghost wu tell you how to learn design pattern</div>
- <div id="box2">id为box2的第二个div</div>
调用两次构造函数 G 去获取元素的时候, this[0] 现在都指向了 id 为 box2 的元素, 把第一次 G('box') 选择到的 id 为 box 的元素覆盖了,产生覆盖的原因是 this 共用,那么我们
可以通过什么方法把 this 分开呢?不同的 this 指向不同的实例? 用什么? 恩,对了, 用 new,每次 new 一个构造函数就会生成新的实例
四、解决 this 覆盖与链式调用
- <script>
- var G = function (id) {
- return new G.fn.init(id);
- };
- G.fn = G.prototype = {
- init: function (id) {
- this[0] = document.getElementById(id);
- this.length = 1;
- return this;
- },
- length: 0,
- size: function () {
- return this.length;
- }
- }
- window.onload = function(){
- console.log( G( 'box' ) );
- console.log( G( 'box2' ) );
- }
- </script>
- <div id="box">ghost wu tell you how to learn design pattern</div>
- <div id="box2">id为box2的第二个div</div>
通过构造函数中 new G.fn.init(id) 的方式,每次生成一个新的实例,但是产生了一个新的问题,不能链式调用, 因为 init 中的 this 发生了改变,不再指向 (G.fn, G.prototype).
- var G = function(id) {
- return new G.fn.init(id);
- };
- G.fn = G.prototype = {
- init: function(id) {
- this[0] = document.getElementById(id);
- this.length = 1;
- return this;
- },
- length: 0,
- size: function() {
- return this.length;
- }
- }
- G.fn.init.prototype = G.fn;
加上 G.fn.init.prototype = G.fn; 我们就修正了 this 的覆盖与链式调用问题
五、扩展选择器
上面支持 id 选择器,我们只需要在 init 函数加上其他类型的扩展就可以了,比如,我这里增加了一个标签选择器
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>Document</title>
- <style>
- div,p {
- border:1px solid red;
- margin: 10px;
- padding: 10px;
- }
- </style>
- <script>
- var G = function ( selector, context ) {
- return new G.fn.init( selector, context );
- };
- G.fn = G.prototype = {
- constructor : G,
- init: function ( selector, context ) {
- this.length = 0;
- context = context || document;
- if ( selector.indexOf( '#' ) == 0 ) {
- this[0] = document.getElementById( selector.substring( 1 ) );
- this.length = 1;
- }else {
- var aNode = context.getElementsByTagName( selector );
- for( var i = 0, len = aNode.length; i < len; i++ ){
- this[i] = aNode[i];
- }
- this.length = len;
- }
- this.selector = selector;
- this.context = context;
- return this;
- },
- length: 0,
- size: function () {
- return this.length;
- }
- }
- G.fn.init.prototype = G.fn;
- window.onload = function(){
- console.log( G('#box')[0] );
- var aP = G('p', G('#box')[0]);
- // var aP = G('p');
- // var aP = G('#p1');
- for( var i = 0, len = aP.size(); i < len; i++ ){
- aP[i].style.backgroundColor = 'blue';
- }
- }
- </script>
- </head>
- <body>
- <div id="box">
- <p>跟着ghostwu学习设计模式</p>
- <p>跟着ghostwu学习设计模式</p>
- <p>跟着ghostwu学习设计模式</p>
- <p>跟着ghostwu学习设计模式</p>
- </div>
- <p id="p1">跟着ghostwu学习设计模式</p>
- <p>跟着ghostwu学习设计模式</p>
- </body>
- </html>
来源: http://www.cnblogs.com/ghostwu/p/7427277.html