zoukankan      html  css  js  c++  java
  • [js高手之路] 设计模式系列课程

    一、我们从一个简单的构造函数+原型程序开始

    1 var G = function(){};
    2         G.prototype = {
    3             length : 5,
    4             size : function(){
    5                 return this.length;
    6             }
    7         }

    上例是个非常简单的程序,如果需要调用,我们可以用new的方式

    var oG = new G();
    console.log( oG.size() ); //5
    1、常见的错误调用方式1
    console.log( G.size() ); //报错
    G.size这种调用,是把size当做静态方法调用,如果需要正常的调用, 应该把size方法加在函数本身,如:
    G.size = function(){
    return 10;
    }
    2、常见的错误调用方式2
    G().size() //报错
    G()返回的是undefined, undefined.size() 肯定是报错, size是函数G原型对象上的方法,如果要确保这种方式调用正常,那么我们就要让G() 返回一个G函数的实例
    所以,我们可以这样做:
    1         var G = function () {
    2             if( this instanceof G ) {
    3                 return this;
    4             }else {
    5                 return new G();
    6             }
    7         };

    把G的构造函数改造一下,判断this是否是当前G函数的实例,如果是,直接返回,如果不是,返回new G()  这样根据原型对象的查找原则,就能确保调用到size方法

    完整的代码:

     1         var G = function () {
     2             if( this instanceof G ) {
     3                 return this;
     4             }else {
     5                 return new G();
     6             }
     7         };
     8         G.prototype = {
     9             length: 5,
    10             size: function () {
    11                 return this.length;
    12             }
    13         }
    14         console.log( G().size() );

    在jquery框架中,他是怎么做的?

     1         var G = function () {
     2             return G.fn;
     3         };
     4         G.fn = G.prototype = {
     5             length: 5,
     6             size: function () {
     7                 return this.length;
     8             }
     9         }
    10         console.log( G.prototype.size() ); //5
    11         console.log( G().size() ); //5

    在jquery中, 为函数G增加一个属性fn( 记住:在js中函数是对象,这其实就是给对象增加了一个属性 ),然后在G函数中返回 G.fn 就是就是返回G.prototype

    那么用G().size 其实就是相当于调用 G.prototype.size()

    二、在jquery中,这个构造函数一般是用来选择元素的。

    G返回的是G的原型对象,我们要增加选择元素的功能,自然而然,就是往原型对象上添加:

     1         var G = function ( id ) {
     2             return G.fn.init( id );
     3         };
     4         G.fn = G.prototype = {
     5             init : function( id ){
     6                 return document.getElementById( id );
     7             },
     8             length: 5,
     9             size: function () {
    10                 return this.length;
    11             }
    12         }

    向G的原型对象上添加一个init方法, 然后在构造函数中调用

    1         window.onload = function(){
    2             console.log( G( 'box' ) );
    3             G('box').style.backgroundColor = 'red';
    4             // G('box').size(); //报错,无法链式调用
    5         }
    6 
    7     <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就可以派上用场了

     1 <script>
     2         var G = function (id) {
     3             return G.fn.init(id);
     4         };
     5         G.fn = G.prototype = {
     6             init: function (id) {
     7                 this[0] = document.getElementById(id);
     8                 this.length = 1;
     9                 return this;
    10             },
    11             length: 0,
    12             size: function () {
    13                 return this.length;
    14             }
    15         }
    16         window.onload = function () {
    17             console.log(G('box'));
    18             console.log( G('box2').size() );
    19         }
    20     </script>
    1     <div id="box">ghost wu tell you how to learn design pattern</div>
    2     <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共用之后,元素选择就会产生覆盖

     1 <script>
     2         var G = function (id) {
     3             return G.fn.init(id);
     4         };
     5         G.fn = G.prototype = {
     6             init: function (id) {
     7                 this[0] = document.getElementById(id);
     8                 this.length = 1;
     9                 console.log( this === G.fn, this === G.prototype, this );
    10                 return this;
    11             },
    12             length: 0,
    13             size: function () {
    14                 return this.length;
    15             }
    16         }
    17 
    18         window.onload = function(){
    19             console.log( G( 'box' ) );
    20             console.log( G( 'box2' ) );
    21         }
    22     </script>
    23 
    24     <div id="box">ghost wu tell you how to learn design pattern</div>
    25     <div id="box2">id为box2的第二个div</div>

    调用两次构造函数G 去获取元素的时候, this[0] 现在都指向了 id为box2的元素, 把第一次G('box')选择到的id为box的元素覆盖了,产生覆盖的原因是this共用,那么我们

    可以通过什么方法把this分开呢?不同的this指向不同的实例? 用什么? 恩,对了, 用new,每次new一个构造函数就会生成新的实例

    四、解决this覆盖与链式调用

     1     <script>
     2         var G = function (id) {
     3             return new G.fn.init(id);
     4         };
     5         G.fn = G.prototype = {
     6             init: function (id) {
     7                 this[0] = document.getElementById(id);
     8                 this.length = 1;
     9                 return this;
    10             },
    11             length: 0,
    12             size: function () {
    13                 return this.length;
    14             }
    15         }
    16         window.onload = function(){
    17             console.log( G( 'box' ) );
    18             console.log( G( 'box2' ) );
    19         }
    20     </script>
    21     <div id="box">ghost wu tell you how to learn design pattern</div>
    22     <div id="box2">id为box2的第二个div</div>

    通过构造函数中new G.fn.init( id ) 的方式,每次生成一个新的实例,但是产生了一个新的问题,不能链式调用, 因为init中的this发生了改变,不再指向( G.fn, G.prototype ).

    G('box2').size();//报错,
    为了能够调用到size(), 所以在执行完构造函数之后,我们要确保this指向G的实例,或者G的原型对象,
    只需要把init函数的原型对象指向G.fn就可以了
     1         var G = function (id) {
     2             return new G.fn.init(id);
     3         };
     4         G.fn = G.prototype = {
     5             init: function (id) {
     6                 this[0] = document.getElementById(id);
     7                 this.length = 1;
     8                 return this;
     9             },
    10             length: 0,
    11             size: function () {
    12                 return this.length;
    13             }
    14         }
    15         G.fn.init.prototype = G.fn;

    加上G.fn.init.prototype = G.fn;我们就修正了this的覆盖与链式调用问题

     五、扩展选择器

    上面支持id选择器,我们只需要在init函数加上其他类型的扩展就可以了,比如,我这里增加了一个标签选择器

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 
     4 <head>
     5     <meta charset="UTF-8">
     6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     8     <title>Document</title>
     9     <style>
    10         div,p {
    11             border:1px solid red;
    12             margin: 10px;
    13             padding: 10px;
    14         }
    15     </style>
    16     <script>
    17         var G = function ( selector, context ) {
    18             return new G.fn.init( selector, context );
    19         };
    20         G.fn = G.prototype = {
    21             constructor : G,
    22             init: function ( selector, context ) {
    23                 this.length = 0;
    24                 context = context || document;
    25                 if ( selector.indexOf( '#' ) == 0 ) {
    26                     this[0] = document.getElementById( selector.substring( 1 ) );
    27                     this.length = 1;
    28                 }else {
    29                     var aNode = context.getElementsByTagName( selector );
    30                     for( var i = 0, len = aNode.length; i < len; i++ ){
    31                         this[i] = aNode[i];
    32                     }
    33                     this.length = len;
    34                 }
    35                 this.selector = selector;
    36                 this.context = context;
    37                 return this;
    38             },
    39             length: 0,
    40             size: function () {
    41                 return this.length;
    42             }
    43         }
    44         G.fn.init.prototype = G.fn;
    45 
    46         window.onload = function(){
    47             console.log( G('#box')[0] );
    48             var aP = G('p', G('#box')[0]);
    49             // var aP = G('p');
    50             // var aP = G('#p1');
    51             for( var i = 0, len = aP.size(); i < len; i++ ){
    52                 aP[i].style.backgroundColor = 'blue';
    53             }
    54         }
    55     </script>
    56 </head>
    57 
    58 <body>
    59     <div id="box">
    60         <p>跟着ghostwu学习设计模式</p>
    61         <p>跟着ghostwu学习设计模式</p>
    62         <p>跟着ghostwu学习设计模式</p>
    63         <p>跟着ghostwu学习设计模式</p>
    64     </div>
    65     <p id="p1">跟着ghostwu学习设计模式</p>
    66     <p>跟着ghostwu学习设计模式</p>
    67 </body>
    68 
    69 </html>
  • 相关阅读:
    html <applet>元素属性介绍
    C#内存释放(转)
    mongodb for linux (安装)
    WCF客户端搭建(通过自定义WCF Client封装) wu
    SQL常用语句 wu
    任务调度平台 wu
    将DataSet 纵向显示数据
    关于OP和SI项目的记录点,防止遗忘难以查找
    java进阶
    git代码量统计
  • 原文地址:https://www.cnblogs.com/ghostwu/p/7427277.html
Copyright © 2011-2022 走看看