而是指我们在写程序的时候,原型继承是让父对

作者: 前端  发布:2019-09-04

                fn.apply( obj[i], args || [i, obj[i]] ); 

复制代码 代码如下:

具体调用时候就需要分析jQuery.prototype.init函数了。

但是有个地方需要注意,就是Object.create(null)创建的对象的原型为undefined,也就是没有toString和valueOf方法,所以alert(man);的时候会出错,但alert(man.car);是没问题的。

            for ( var i in obj ) 

复制代码 代码如下:

Js代码  

// 父构造函数
function Person() {
    // an "own" property
    this.name = "Adam";
}
// 给原型添加新属性
Person.prototype.getName = function () {
    return this.name;
};
// 继承
var kid = object(Person.prototype);
console.log(typeof kid.getName); // "function",因为是在原型里定义的
console.log(typeof kid.name); // "undefined", 因为只继承了原型

 

//Employee继承Person
varEmployee=Class(Person,{
Create:function(name,age,salary){
Person.Create.call(this,name,age);
//调用基类的构造函数
this.salary=salary;
},
ShowMeTheMoney:function(){
alert(this.name "$" this.salary);
}
});
varSteveJobs=New(Employee,["SteveJobs",53,1234]);
SteveJobs.SayHello();
SteveJobs.ShowMeTheMoney();

       2.2.2、jQuery(DOMElement)当传入参数是一dom element的时候,init方法片段:

我们再来看一下深拷贝:

jQuery对象调用原型方法,原型方法调用静态方法,调用时把this作为参数传进去,静态方法返回时要把this返回。这样就实现了jQuery的方法链

varAnimal=newClass({
initialize:function(age){
this.age=age;
}
});
varCat=newClass({
Extends:Animal,
initialize:function(name,age){
this.parent(age);//callsinitalizemethodofAnimalclass
this.name=name;
}
});
varmyCat=newCat('Micia',20);
alert(myCat.name);//alerts'Micia'.
alert(myCat.age);//alerts20.

         2.2.3、如果传入是body,jQuery(“body”),返回只有一个body元素

// Mixin
var Mixin = function () { };
Mixin.prototype = {
    driveForward: function () {
        console.log('drive forward');
    },
    driveBackward: function () {
        console.log('drive backward');
    }
};

 

复制代码 代码如下:

     这样var p = Person('a',21)和var p = new Person('a',21) ,p就都是Person实例了,如果去掉if判断和else后边的内容,第一种调用p是undefined。

    child = child || {};

<head> 

复制代码 代码如下:

Js代码  

// 接着,创建包含属性的配置设置
// 属性设置为可写,可枚举,可配置
var config = {
    writable: true,
    enumerable: true,
    configurable: true
};

    // Person类 

复制代码 代码如下:

Java代码  

复制代码 代码如下:

        if ( obj.length == undefined ) 

b、继承类

1、jQuery是个什么东西?Ext是什么东西?(此处不是指两个框架,而是指我们在写程序的时候,经常用到的两个关键字--暂时称之为关键字)

一个对象借用另外一个对象的一个或两个方法,而这两个对象之间不会有什么直接联系。不用多解释,直接用代码解释吧:

}); 

复制代码 代码如下:

       2.2.6、jQuery(jQuery()),也就是传一个jQuery实例进去,会创建一个新对象,然后把老对象的内容拷贝到新对象里头。

总结

 

下面看一下my.class的源代码解析:
my.Class实现思路基本是这样的,如果只有一个参数,那么声明的是一个基础类,这个参数是用来声明新类的方法和属以及构造函数。它不是继承而来,但它可以被继承。

  1. jQuery.fn.extend({  
  2.     find: function( selector ) {  
  3.        var self = this,  
  4.            i, l;  
  5.        ……//此处省略10 行  
  6.        var ret = this.pushStack( "", "find", selector ),  
  7.            length, n, r;  
  8.        for ( i = 0, l = this.length; i < l; i  ) {  
  9.            length = ret.length;  
  10.            jQuery.find( selector, this[i], ret );  
  11.            if ( i > 0 ) {  
  12.               // Make sure that the results are unique  
  13.               for ( n = length; n < ret.length; n  ) {  
  14.                   for ( r = 0; r < length; r  ) {  
  15.                      if ( ret[r] === ret[n] ) {  
  16.                          ret.splice(n--, 1);  
  17.                          break;  
  18.                      }  
  19.                   }  
  20.               }  
  21.            }  
  22.        }  
  23.        return ret;  
  24. }  

console.log(one.say.apply(two, ['hello'])); // "hello, another object"

function jQuery(a,c) { 

varFoo=klass({
foo:0,
initialize:function(){
this.foo=1
},
getFoo:function(){
returnthis.foo
},
setFoo:function(x){
this.foo=x
returnthis.getFoo()
}
})

     挖坑,关于原型方法,实例的关系。

复制代码 代码如下:

</head> 

//声明一个类
varPerson=klass(function(name){
this.name=name
})
.statics({//静态方法
head:':)',
feet:'_|_'
})
.methods({//实例方法
walk:function(){}
})

3、jQuery和Ext都怎么实现继承的,有什么异同?各有什么优势?

var dad = {
    counts: [1, 2, 3],
    reads: { paper: true }
};
var kid = extendDeep(dad);

jQuery.extend = jQuery.fn.extend = function(obj,prop) { 

(function(){
//新建类
varPerson=my.Class({
//添加静态方法
STATIC:{
AGE_OF_MAJORITY:18
},
//构造函数
constructor:function(name,age){
this.name=name;
this.age=age;
},
//实例方法
sayHello:function(){
console.log('Hellofrom' this.name '!');
},
//实例方法
drinkAlcohol:function(){
this.age<Person.AGE_OF_MAJORITY?
console.log('Tooyoung!Drinkmilkinstead!'):
console.log('Whiskeyorbeer?');
}
});
//暴露给命名空间
myLib.Person=Person;
})();
varjohn=newmyLib.Person('John',16);
john.sayHello();//log"HellofromJohn!"
john.drinkAlcohol();//log"Tooyoung!Drinkmilkinstead!"

Js代码  

var twosay = bind(two, one.say);
console.log(twosay('yo')); // "yo, another object"

    } 

这种判断都是基于正则匹配:fnTest=/xyz/.test(function(){xyz;})?/bsuprb/:/.*/;关键字"super"
如果显示的声明了要调用父类的方法,那么声明方法的时候,就包装成一个内部调用父类方法且返回相同值的函数,给当前类的方法。

         bindReady方法是通过attachEvent/addEventListener为document注册了load事件。

function bind(o, m) {
    return function () {
        return m.apply(o, [].slice.call(arguments));
    };
}

Person.prototype默认就是new Object()对象,所以你调用alert(tom.toString())会弹出[ object Object ],那是因为调用了Object的toString方法,因为Person.prototype没有覆盖toString方法啊。

如果有三个以上参数那么,除出第一个参数做为继承的父类,最后一个参数用声明新类的方法和属性以及构造函数。中间的参数是用类来扩展新类的方法。当然也可以通过my.extendClass扩展新方法。
同时,类库为commonJS和浏览环境都提供了支持!

         此处rootjQuery默认又等于jQuery(document);ready实际上就是在为document注册load事件,源码:

模式2:复制所有属性进行继承

</body> 

functionF(){};
F.prototype=superCtor.prototype;
ctor.prototype=newF();
ctor.prototype.constructor=ctor;

Js代码  

介绍

作者 baozhifei

d、实现一个类的方法

Js代码  

复制代码 代码如下:

 

但是如果...

  1. new jQuery.fn.init(selector,context,rootjQuery)   

/* 浅拷贝 */
function extend(parent, child) {
    var i;
    child = child || {};
    for (i in parent) {
        if (parent.hasOwnProperty(i)) {
            child[i] = parent[i];
        }
    }
    return child;
}

<body> 

//给myLib.Dreamer添加新方法
my.extendClass(myLib.Dreamer,{
//添加静态方法
STATIC:{
s_dongSomeThing:function(){
console.log("dosomething!");
}
},
//添加实例方法
touchTheSky:function(){
console.log('Touchingthesky');
},
//添加实例方法
reachTheStars:function(){
console.log('Sheissopretty!');
}
});

Js代码  

复制代码 代码如下:

面向对象编程(OOP)是一种流行的编程方法。但javascript的OOP,较之JAVA、c 有很大的同,主要体现它的继承方式不同。javascript是基于原型PROTOTYPE继承的。所有对象都是基于原型链,最终追述到Object对象。

Js代码  

那如何我们只想混入部分属性呢?该个如何做?其实我们可以使用多余的参数来定义需要混入的属性,例如mix(child,parent,method1,method2)这样就可以只将parent里的method1和method2混入到child里。上代码:

再来看看jQuery的extend方法,1.0的版本中这个方法很简单,就是对象属性的浅拷贝。代码如下:

复制代码 代码如下:

           2.2.4、jQuery(selector),如jquery.mobile.js中initializePage中$(“:jqmData(role=’page’)”)

/* 深拷贝 */
function extendDeep(parent, child) {
    var i,
        toStr = Object.prototype.toString,
        astr = "[object Array]";

 

//创建类Person
varPerson=Class(object,{
Create:function(name,age){
this.name=name;
this.age=age;
},
SayHello:function(){
alert("Hello,I'm" this.name "," this.age "yearsold.");
}
});
varBillGates=New(Person,["BillGates",53]);
BillGates.SayHello();

  1. jQuery.extend = jQuery.fn.extend = function() {  
  2.     var options, name, src, copy, copyIsArray, clone,  
  3.        target = arguments[0] || {}, i = 1,     
  4. length = arguments.length, deep = false;  
  5.     // Handle a deep copy situation  
  6.     if ( typeof target === "boolean" ) {  
  7.        deep = target;  
  8.        target = arguments[1] || {};  
  9.        // skip the boolean and the target  
  10.        i = 2;  
  11.     }  
  12.     // Handle case when target is a string or something (possible in deep copy)  
  13.     if ( typeof target !== "object" && !jQuery.isFunction(target) ) {  
  14.        target = {};  
  15.     }  
  16.     // extend jQuery itself if only one argument is passed  
  17.     if ( length === i ) {  
  18.        target = this;//如果参数只有一个,target就指向this  
  19.        --i;  
  20.     }  
  21.     for ( ; i < length; i  ) {  
  22.        // Only deal with non-null/undefined values  
  23.        if ( (options = arguments[ i ]) != null ) {  
  24.            // Extend the base object  
  25.            for ( name in options ) {//开始复制  
  26.               src = target[ name ];  
  27.               copy = options[ name ];  
  28.               // Prevent never-ending loop  
  29.               if ( target === copy ) {  
  30.                   continue;  
  31.               }  
  32.               // Recurse if we're merging plain objects or arrays  
  33.               if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {  
  34.                   if ( copyIsArray ) {  
  35.                      copyIsArray = false;  
  36.                      clone = src && jQuery.isArray(src) ? src : [];  
  37.                   } else {  
  38.                      clone = src && jQuery.isPlainObject(src) ? src : {};  
  39.                   }  
  40.                   // Never move original objects, clone them  
  41.                   target[ name ] = jQuery.extend( deep, clone, copy );  
  42.               // Don't bring in undefined values  
  43.               } else if ( copy !== undefined ) {  
  44.                   target[ name ] = copy;  
  45.               }  
  46.            }  
  47.        }  
  48.     }  
  49.     // Return the modified object  
  50.     return target;  
  51. };  

// 传入一个回调函数callback
var yetanother = {
    name: 'Yet another object',
    method: function (callback) {
        return callback('Hola');
    }
};
console.log(yetanother.method(one.say)); // "Holla, undefined"

    </script> 

这里不想讨论过多的关于javascript的继承方式和其它语言的继承方式的不同之处。主要讨论如何封装javascript的Class,以便更好的管理和维护基础代码,减少重复代码,以及更好的模块化编程。

 

复制代码 代码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "; 

一个java或者c 程序员,不一定能写出高性能的javascript代码,但更容易写出高性能的javascript代码。
javascript的简单是基于它“胸襟广阔”的包容性。它声明时,不需要指定类型,甚至可以任意的转换类型。它面向对象,却没有类(Class)的限制。它是一门崇尚自由又非常严谨的语言,如果你是一个自由主义者,那么,拥抱javascript吧!

  1. if (!selector ) {  
  2.       return this;  
  3. }  

mix函数将所传入的所有参数的子属性都复制到child对象里,以便产生一个新对象。

    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> 

复制代码 代码如下:

Js代码  

原型继承是让父对象作为子对象的原型,从而达到继承的目的:

    each: function( obj, fn, args ) { 

通过statics方式添加静态方法,通过实例的implements和静态方法methods添加实例方法。
通过父类的extend实现继承。
同时,类库为commonJS和浏览环境都提供了支持!

  1. else if ( jQuery.isFunction( selector ) ) {  
  2.     return rootjQuery.ready( selector );  
  3. }  

// 测试是否成功得到混入的方法
vehicle.driveForward();
vehicle.driveBackward();

jQuery.extend({ 

//SuperHuman继承Person
varSuperHuman=Person.extend(function(name){
//自动调用父类的构造方法
})
.methods({
walk:function(){
//显式声明调用父类的walk方法
this.supr()
this.fly()
},
fly:function(){}
})
newSuperHuman('Zelda').walk()

  1. bindReady: function() {  
  2.      if ( readyList ) {  
  3.          return;  
  4.      }  
  5.      readyList = jQuery._Deferred();  
  6.      // Catch cases where $(document).ready() is called after the  
  7.      // browser event has already occurred.  
  8.      if ( document.readyState === "complete" ) {  
  9.          // Handle it asynchronously to allow scripts the opportunity to delay ready  
  10.          return setTimeout( jQuery.ready, 1 );  
  11.      }  
  12.      // Mozilla, Opera and webkit nightlies currently support this event  
  13.      if ( document.addEventListener ) {  
  14.          // Use the handy event callback  
  15.          document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );  
  16.          // A fallback to window.onload, that will always work  
  17.          window.addEventListener( "load", jQuery.ready, false );  
  18.      // If IE event model is used  
  19.      } else if ( document.attachEvent ) {  
  20.          // ensure firing before onload,  
  21.          // maybe late but safe also for iframes  
  22.          document.attachEvent( "onreadystatechange", DOMContentLoaded );  
  23.          // A fallback to window.onload, that will always work  
  24.          window.attachEvent( "onload", jQuery.ready );  
  25.          ……  
  26.      }  
  27.   }  

var twosay3 = one.say.bind(two, 'Enchanté');
console.log(twosay3()); // "Enchanté, another object"

现在,再来看看jQuery的方法继承,我挑了一个很常用的each方法,是不是一下就能看清楚啊。

同样的,除最后一个参数是当前类的方法声明,其它参数均做为继承父类,需要循环继承,但当这里处理的相对比较简单,没涉及到覆盖。你可以自己动手添加。

    此处把元素赋值为this[0]可以在后续访问元素的时候,直接用返回实例的[0]来访问,如果是多个元素,则可以用下标一个个的访问,后边看到selector的时候会看到。同时由于后边把init的原型指向了jQuery的原型,所以这里this的原型方法都是jQuery.prototype的方法:

// Car
var Car = function (settings) {
    this.model = settings.model || 'no model provided';
    this.colour = settings.colour || 'no colour provided';
};

 

但是如果因此你就下结论:javascript是门简单的语言。那你就大错特错了。想写出高性能的代码,同样需要具备一个高级程序员的基本素养。

    2.2、jQuery首先是个Function,既然是个Function,那个就可以new,可以像Function一样调用,以下将解析几种jQuery调用方法的源码:

// 创建新对象Car
var vehicle = new Car({ model: 'Ford Escort', colour: 'blue' });

    for ( var i in prop ) obj[i] = prop[i]; 

复制代码 代码如下:

      此处先挖个坑,构造函数中(如此处的Person)的this到底怎么理解?

深拷贝以后,两个值就不相等了,bingo!

在分析jQuery的方法继承前,我们先来弄清楚javascript的原型继承。看下面一段代码:

复制代码 代码如下:

  1. if (typeof Ext === 'undefined') {  
  2.      global.Ext = {};  
  3. }  

/* 使用新版的ECMAScript 5提供的功能 */
var child = Object.create(parent);

 

五、悟透javascript:语法甘露
先看用法实例
a、创建类

Js代码  

模式3:混合(mix-in)

    // 原型方法 

varPerson=my.Class({
//youcannowcalltheconstructorwithorwithoutnew
constructor:function(name,city){
if(!(thisinstanceofPerson))
returnnewPerson(name,city);
this.name=name;
this.city=citye;
}
});

Js代码  

// 给Car混入属性,但是值混入'driveForward' 和 'driveBackward'*/
augment(Car, Mixin, 'driveForward', 'driveBackward');

    }, 

下面是源码分析:显然,多了一个New方法,创建类和新建类的实例都被巧妙的封装了。形成了一个有意义的整体!还有一点不同的地方,所有的类都基于字面量,而不是基于函数。代码很简短,但其中原理却很丰富也很巧妙,可以细细品味一番!

  1. Ext = {  
  2.  version : '3.3.1',  
  3.  versionDetail : {  
  4.   major : 3,  
  5.   minor : 3,  
  6.   patch : 1  
  7.  }  
  8. };   

console.dir(cake);

    Person.sayHello = function(p) { 

复制代码 代码如下:

Js代码  

defineProp(man, 'car', 'Delorean');
defineProp(man, 'dob', '1981');
defineProp(man, 'beard', false);

 

复制代码 代码如下:

     如果传入的context为空,就从当前对象查找find(selector)否则就从rootjQuery查找,这里rootjQuery是个实力,所以此方法调用就是调用的原型上的find方法:

var dad = {
    counts: [1, 2, 3],
    reads: { paper: true }
};
var kid = extend(dad);
kid.counts.push(4);
console.log(dad.counts.toString()); // "1,2,3,4"
console.log(dad.reads === kid.reads); // true

<html lang="en"> 

复制代码 代码如下:

...

var child = Object.create(parent, {
    age: { value: 2} // ECMA5 descriptor
});
console.log(child.hasOwnProperty("age")); // true

传一个参数时,会把obj的属性复制到this上,传两个参数时,会把prop的属性复制到obj上。

复制代码 代码如下:

两种方法来添加。原型方法则通过jQuery.fn.extend / jQuery.prototype.extend来添加。      

模式4:借用方法

             return jQuery.each( this, fn, args ); 

varCat=newClass({
initialize:function(name){
this.name=name;
}
});
varmyCat=newCat('Micia');
alert(myCat.name);//alerts'Micia'
varCow=newClass({
initialize:function(){
alert('moooo');
}
});

  1. function Person(name,age){  
  2.    if(this instanceof Person){  
  3.       this.name = name;  
  4.       this.age = age;  
  5.    }else{  
  6.       return new Person(name,age);  
  7.    }  
  8. }  

就不用总结了吧。

 

/**
*ThedefinitionofCatClass.
*/
varCat=Class({
/**
*Constructor.
*
*@paramnameCat'sname
*/
initialize:function(name){
this.name=name;
},
/**
*Eatfunction.
*/
eat:function(){
alert(this.name "iseatingfish.");
}
});
/**
*ThedefinitionofBlackCatClass.
*/
varBlackCat=Class(Cat,{
/**
*Constructor.
*
*@paramnameCat'sname.
*@paramageCat'sage.
*/
initialize:function(name,age){
//calltheconstructorofsuperclass.
9159.com,BlackCat.super_.call(this,name);
this.age=age;
},
/**
*Eatfunction.
*/
eat:function(){
alert(this.name "(" this.age ")iseatingdog.");
}
});
/**
*ThedefinitionofBlackFatCatClass.
*/
varBlackFatCat=Class(BlackCat,{
/**
*Constructor.
*
*@paramnameCat'sname.
*@paramageCat'sage.
*@paramweightCat'sweight.
*/
initialize:function(name,age,weight){
//calltheconstructorofsuperclass.
BlackFatCat.super_.call(this,name,age);
this.weight=weight;
},
/**
*Eatfunction.
*/
eat:function(){
alert(this.name "(" this.age ")iseatingdog.Myweight:" this.weight);
}
});
/**
*ThedefinitionofDogClass.
*/
varDog=Class({});
varcat=newBlackFatCat("John",24,"100kg");
cat.eat();
//true
alert(catinstanceofCat);
//true
alert(catinstanceofBlackCat);
//true
alert(catinstanceofBlackFatCat);
//true
alert(cat.constructor===BlackFatCat);
//false
alert(catinstanceofDog);

    综上,jQuery()返回的是一个jQuery.prototype.init函数的实例,但是由于这个函数的原型指向了jQuery函数的原型,jQuery.prototype上的方法也可以直接在这个实例上调用。同时jQuery会被当成一个数组来使用,根据下标索引提取满足参数的dom元素。

混入就是将一个对象的一个或多个(或全部)属性(或方法)复制到另外一个对象,我们举一个例子:

     

看下面实例:

         2.2.5、jQuery()如果传入为空,则返回不包含元素的jQuery对象:

var two = {
    name: 'another object'
};

 

varAnimal=newClass({
initialize:function(age){
this.age=age;
}
});
varCat=newClass({
Implements:Animal,
setName:function(name){
this.name=name
}
});
varmyAnimal=newCat(20);
myAnimal.setName('Micia');
alert(myAnimal.name);//alerts'Micia'.

  1. ready: function( fn ) {  
  2.       // Attach the listeners  
  3.       jQuery.bindReady();  
  4.       // Add the callback  
  5.       readyList.done( fn );  
  6.       return this;  
  7.    }  

kid.counts.push(4);
console.log(kid.counts.toString()); // "1,2,3,4"
console.log(dad.counts.toString()); // "1,2,3"

原型方法就相当于类的方法,我们首先new了一个Person对象叫tom,当我们调用tom.sayHello(),浏览器会首先查明tom是什么类,也就是浏览器会去查找tom的constructor属性,这个属性代表着实例的构造函数。tom的constructor是指向Person的,那也就是说tom是Person类的实例。然后浏览器会查看Person的prototype属性上有没有sayHello方法,有则调用之。

//创建类的函数,用于声明类及继承关系
functionClass(aBaseClass,aClassDefine){
//创建类的临时函数壳
functionclass_(){
this.Type=aBaseClass;
//我们给每一个类约定一个Type属性,引用其继承的类
for(varmemberinaClassDefine)
this[member]=aClassDefine[member];
//复制类的全部定义到当前创建的类
};
class_.prototype=aBaseClass;
returnnewclass_();
};
//创建对象的函数,用于任意类的对象创建
functionNew(aClass,aParams){
//创建对象的临时函数壳
functionnew_(){
this.Type=aClass;
//我们也给每一个对象约定一个Type属性,据此可以访问到对象所属的类
if(aClass.Create)
aClass.Create.apply(this,aParams);
//我们约定所有类的构造函数都叫Create,这和DELPHI比较相似
};
new_.prototype=aClass;
returnnewnew_();
};

       3.2、jQuery的继承的实现是基于拷贝的,这种实现比较简单,容易理解。但是要注意的是,jQuery这个function本身和jQuery的原型都有继承方法,其中jQuery.extend是把方法、属性拷贝到function上,后续可以直接通过jQuery.method调用;jQuery.fn.extend是把方法、属性拷贝到jQuery的原型上,后续可以通过jQuery实例(实际是jQuery.fn.init的实例,这个init函数的原型指向jQuery的原型)调用:

// 父构造函数
function Person() {
    // an "own" property
    this.name = "Adam";
}
// 给原型添加新属性
Person.prototype.getName = function () {
    return this.name;
};
// 创建新person
var papa = new Person();
// 继承
var kid = object(papa);
console.log(kid.getName()); // "Adam"

 

javascript是个入门门槛很低的语言,甚至一个从来没有接触过javascript的技术人员,几小时内就可以写出一个简单有用的程序代码。

      首先说明这不是一篇完整解读ExtJS和jQuery所有方面差异的文章,只是针对我个人刚看了两天的jQuery产生的一些疑问的整理。之前用过一段时间ExtJS,了解ExtJS的一些机制。现在做移动开发,又选定了jquery mobile,要写控件,所以需要了解jquery。(不过换工作之后应该不会再用jQuery了,坑估计是短期内填不上了)

var driver = Object.create( man );
defineProp (driver, 'topSpeed', '100mph');
driver.topSpeed // 100mph

 

复制代码 代码如下:

     此处又调用到了jQuery.find方法,注意,jQuery是一个Function,这个find跟rootjQuery不同,jQuery.find是function的一个属性,非严格意义上可以简单的认为类似于java的静态方法。此find方法实则是Sizzle本身:

先来看一个浅拷贝的例子:

     

d、实现一个类的方法
因为有时候你可能希望覆写或者混合一个实例方法,可以这样:

    既然Ext是个Object,那jQuery是不是也是个Object呢?NO,来分析下源码,jquery.js:

您可能感兴趣的文章:

  • 深入理解JavaScript系列(45):代码复用模式(避免篇)详解
  • 简单谈谈javascript代码复用模式
  • JavaScript代码复用模式详解
  • JavaScript代码复用模式实例分析

    // Person实例对象 

原文来自:穆乙

    具体实现还要看selector的内容,可能是getTagByName或者querySelectorAll,如getTagByName(‘name’)、querySelectorAll(“[data-role=’page’]”)。

// 通常使用Object.defineProperty()来添加新属性(ECMAScript5支持)
// 现在,为了方便,我们自定义一个封装函数
var defineProp = function (obj, key, value) {
    config.value = value;
    Object.defineProperty(obj, key, config);
}

        return new jQuery(a,c); 

四、mootools类库的Class
源码解析可以看这里:
看具体用法:
a、新建一个类

  1. if ( selector.nodeType ) {  
  2.      this.context = this[0] = selector;  
  3.      this.length = 1;  
  4.      return this;  
  5.  }  

代码的最后一行,你可以发现dad和kid的reads是一样的,也就是他们使用的是同一个引用,这也就是浅拷贝带来的问题。

    tom.sayHello(); 

复制代码 代码如下:

  1. jQuery.fn = jQuery.prototype = ……  
  2. jQuery.fn.init.prototype = jQuery.fn;  

var one = {
    name: 'object',
    say: function (greet) {
        return greet ', ' this.name;
    }
};

 

复制代码 代码如下:

    此处大概明白了,jQuery是个Function,并且由于此处调用jQuery的时候,有个return,所以var v = jQuery(xxx)和var v = new jQuery(xxx)调用后,v都是同一个函数的实例。在《JavaScript高级程序设计》第18章第一节有提到过这种技巧,叫做作用域安全的构造函数,不过书上提到的形式稍有差异:

该方法使用起来就比较灵活了。

    var tom = new Person('Tom'); 

下面看一下klass源代码解析
klass的基本设计思路很明确,极力的模仿其它语言的继承方式。比如:子类构造方法调用父类的构造方法,还可以显式的声明调用父类的方法。

2、jQuery和Ext在这两个关键字都怎么使用的,有何异同?

var twosay2 = one.say.bind(two);
console.log(twosay2('Bonjour')); // "Bonjour, another object"

 

复制代码 代码如下:

     需要特别注意的是这个this,当jQuery.extend的时候,this指的是jQuery.fn.init这个function,后续的方法、属性复制是复制给function的;当通过jQuery.fn.extend的时候,this指向的是原型对象,后续的方法、属性复制是复制给了原型对象。

而且,也可以更细粒度地在第二个参数上定义属性:

}; 

//可以传递一个字面量去继承
varAlien=SuperHuman.extend({
beam:function(){
this.supr()
//beamintospace
}
})
varSpazoid=newAlien('Zoopo')
if(beamIsDown){
//覆写beam方法
Spazoid.implement({
beam:function(){
this.supr()
//fallbacktojets
this.jets()
}
})
}

       JavaScript是一门基于对象的语言,但不是面向对象,也就是说语言层面没有提供继承的语法,但是可以通过应用层面实现继承。由于把这种实现放到了应用层面,所以实现就变得五花八门了,可以通过拷贝、原型链等。了解两种继承的调用方式对理解下边说到的实现原理是很有帮助的。

复制代码 代码如下:

</html> 

复制代码 代码如下:

  1. if ( selector === "body" && !context && document.body ) {  
  2.     this.context = document;  
  3.     this[0] = document.body;  
  4.     this.selector = selector;  
  5.     this.length = 1;  
  6.     return this;  
  7. }  

// ECMAScript 5给Function.prototype添加了一个bind()方法,以便很容易使用apply()和call()。

 

下面是几个github上找到的比较好的Class封装类库:
一、MY-CLASS
项目地址:
先看基本用法:
a、新建一个类

Js代码  

// 新对象
var child = object(parent);

            for ( var i = 0; i < obj.length; i ) 

(function(){
//Dreamer继承Person
varDreamer=my.Class(Person,{
//构造方法
constructor:function(name,age,dream){
Dreamer.Super.call(this,name,age);
this.dream=dream;
},
//实例方法
sayHello:function(){
superSayHello.call(this);
console.log('Idreamof' this.dream '!');
},
//实例方法
wakeUp:function(){
console.log('Wakeup!');
}
});
//Super访问父类
varsuperSayHello=Dreamer.Super.prototype.sayHello;
//暴露给全局命名空间
myLib.Dreamer=Dreamer;
})();
varsylvester=newmyLib.Dreamer('Sylvester',30,'eatingTweety');
sylvester.sayHello();//log"HellofromSylvester!IdreamofeatingTweety!"
sylvester.wakeUp();//log"Wakeup!"

Js代码  

所以,继承就这么可以做了:

 

另一方面,构造方法,也是比较灵活的。如果显示的声明了initialize,那么这就是构造方法。否则如果参数是个function那么它就做为构造方法,否则就用父类的构造方法。

  1. jQuery.method=function(){}  
  2. jQuery.extend({method:function(){}})   

// 将say赋值给一个变量,this将指向到全局变量
var say = one.say;
console.log(say('hoho')); // "hoho, undefined"

        else 

c、给类添加新方法

2.3、jQuery接着:-)也起到命名空间的作用。

复制代码 代码如下:

}; 

/*globalsdefine:true,window:true,module:true*/
(function(){
//Namespaceobject
varmy={};
//保证AMD分模块可用
if(typeofdefine!=='undefined')
define([],function(){
returnmy;
});
elseif(typeofwindow!=='undefined')
//保证客户端可用
window.my=my;
else
//保证后台可用
module.exports=my;
//============================================================================
//@methodmy.Class
//@paramsbody:Object
//@paramsSuperClass:function,ImplementClasses:function...,body:Object
//@returnfunction
my.Class=function(){
varlen=arguments.length;
varbody=arguments[len-1];//最后一个参数是指定本身的方法
varSuperClass=len>1?arguments[0]:null;//第一个参数是指继承的方法,实例和静态部分均继承
varhasImplementClasses=len>2;//如果有第三个参数,那么第二个就是implementClass,这里其实只继承实例对象
varClass,SuperClassEmpty;
//保证构造方法
if(body.constructor===Object){
Class=function(){};
}else{
Class=body.constructor;
//保证后面不覆盖constructor
deletebody.constructor;
}
//处理superClass部分
if(SuperClass){
//中间件实现实例属性的继承
SuperClassEmpty=function(){};
SuperClassEmpty.prototype=SuperClass.prototype;
Class.prototype=newSuperClassEmpty();//原型继承,解除引用
Class.prototype.constructor=Class;//保证constructor
Class.Super=SuperClass;//父对象访问接口
//静态方法继承,重载superClass方法
extend(Class,SuperClass,false);
}
//处理ImplementClass部分,其实只继承实例属性部分,除SuperClass#arguments[0]#和body#arguments[length-1]#
if(hasImplementClasses)
for(vari=1;i<len-1;i )
//implement是继承的实例属性部分,重载父对象implementClass方法
extend(Class.prototype,arguments[i].prototype,false);
//处理本身声明body部分,静态要STATIC指定,实例部分要删除STATIC部分
extendClass(Class,body);
returnClass;
};
//============================================================================
//@methodmy.extendClass
//@paramsClass:function,extension:Object,?override:boolean=true
varextendClass=my.extendClass=function(Class,extension,override){
//静态部分继承静态部分
if(extension.STATIC){
extend(Class,extension.STATIC,override);
//保证实例部分不继承静态方法
deleteextension.STATIC;
}
//实例属性继继承实例部
extend(Class.prototype,extension,override);
};
//============================================================================
varextend=function(obj,extension,override){
varprop;
//其实这里的flase是表明,覆盖父对象的方法
if(override===false){
for(propinextension)
if(!(propinobj))
obj[prop]=extension[prop];
}else{
//这里其实不覆盖父对象的方法,包括toString
for(propinextension)
obj[prop]=extension[prop];
if(extension.toString!==Object.prototype.toString)
obj.toString=extension.toString;
}
};
})();

Js代码  

console.log(dad.reads === kid.reads); // false
kid.reads.paper = false;

 

/**
*[email protected]
*version1.0
*
*Followoursoftware:)
*MITLicense
*/
!function(context,f){
//fnTest用来验证是否可能通过正则找出调用super父类方法的方法
varfnTest=/xyz/.test(function(){xyz;})?/bsuprb/:/.*/,
noop=function(){},
proto='prototype',
isFn=function(o){
returntypeofo===f;
};
//基础类
functionklass(o){
returnextend.call(typeofo==f?o:noop,o,1);
}
//包装成一个借用super同名方法的函数
functionwrap(k,fn,supr){
returnfunction(){
//缓存原this.super
vartmp=this.supr;
//暂把this.super改造成借用super的同名方法above
//供o里显式的声明(fnTest.text(fn)==true)要借用super的同名方法使用
this.supr=supr[proto][k];
//借用执行并保存返回值
varret=fn.apply(this,arguments);
//恢复原this.super
this.supr=tmp;
//返回返回值,保证wrap后的返回值跟原来一致
returnret;
};
}
//如果o和super有同名方法,且o显式声明借用super的同名方法,就wrap成一个待执行函数供使用
//如果没有显式的声明借用super的同名方法,或者是o独有的方法,或者不是方法就直接用
functionprocess(what,o,supr){
for(varkino){
//如果是非继承方法,按方法注释规则执行,最终都放进what
if(o.hasOwnProperty(k)){
what[k]=typeofo[k]==f
&&typeofsupr[proto][k]==f
&&fnTest.test(o[k])
?wrap(k,o[k],supr):o[k];
}
}
}
//继承方法的实现,fromSub是用来控制是否继承而来,上面的klass里面fromSub是1,表明非继承而来,构造函数不借用super执行
functionextend(o,fromSub){
//noop做为媒介类实现原型继承的解除引用
noop[proto]=this[proto];
varsupr=this,
prototype=newnoop(),//创建实例对象供原型继承使用,解除引用
isFunction=typeofo==f,
_constructor=isFunction?o:this,//如果o是一个构造方法就用,否则由this来决定构造函数
_methods=isFunction?{}:o,//如果o是一个{...}应该用methods放到fn原型里,如果里面有initialize就是构造函数,如果o是函数就由上面_constructor决定o是构造函数
fn=function(){//因为kclass借助了kclass,所以最终实际上返回的就是fn,fn其实就新类的构造函数
//1如果o是{...}就会被methods直接过滤并添加到fn的原型里,如果o里面有initialize,那么fn的原型里就有initialize,那么它就是构造方法
//2如果o是function,methods什么也添加不到fn的原型里,但是_constructor会接受o当构造函数
//3如果o是{....},同时里面也没有initialize,那么就是this当构造函数,如果在klass里由call决定,显然构造函数是noop,如果在非基础类里,构造函数就是父类的构造函数
//由于o不是函数不会自动调用父类的构造函数,只是把父类的构造函数当做当前类的构造函数----这都是由于this的指向决定的
console.log(this);
if(this.initialize){
this.initialize.apply(this,arguments);
}else{
//调用父类构造方法
//如上面3,o不是函数,不会调用父类的构造方法
//基础类无父类,不会调用父类构造方法
fromSub||isFn(o)&&supr.apply(this,arguments);
//调用本类构造方法
//参考上面2,3要么是noop要么是o
console.log(_constructor==noop);
_constructor.apply(this,arguments);
}
};
//构造原型方法的接口
fn.methods=function(o){
process(prototype,o,supr);
fn[proto]=prototype;
returnthis;
};
//执行实现新类原型,保证新类的constructor
fn.methods.call(fn,_methods).prototype.constructor=fn;
//保证新类可以被继承
fn.extend=arguments.callee;
//添加实例方法或者静态方法,statics:静态方法,implement实例方法
fn[proto].implement=fn.statics=function(o,optFn){
//保证o是一个object对象,如果o是一个字符串,那么就是添一个方法的情况,如果o是一个object对象说明是批量添加的
//因为要从o里面拷贝
o=typeofo=='string'?(function(){
varobj={};
obj[o]=optFn;
returnobj;
}()):o;
//添加实例方法或者静态方法,statics:静态方法,implement实例方法
process(this,o,supr);
returnthis;
};
returnfn;
}
//后台用,nodejs
if(typeofmodule!=='undefined'&&module.exports){
module.exports=klass;
}else{
varold=context.klass;
//防冲突
klass.noConflict=function(){
context.klass=old;
returnthis;
};
//前台浏览器用
//window.kclass=kclass;
context.klass=klass;
}
}(this,'function');

    2.1、Ext是个对象,是一个命名空间,跟java里头的package类似,使用Ext下边的方法、Function/类的时候,就像使用一个对象的属性一样,如工具方法Ext.apply、Ext.applyIf可以直接调用,构造函数Ext.json.DataStore,前边加new创建实例。关于这么做的好处,了解java package的好处的都知道那么一些吧,我只还记得避免命名冲突。

function object(o) {
    function F() {
    }

                fn.apply( obj[i], args || [i, obj[i]] ); 

c、扩充类的实现

       既然说到命名空间了,就不得不说jQuery的原型和function的方法,jQuery.method()类似静态方法,可以通过

模式1:原型继承

    if ( window == this ) 

复制代码 代码如下:

  1. var jQuery = (function() {  
  2.   // Define a local copy of jQuery  
  3.   var jQuery = function( selector, context ) {  
  4.     // The jQuery object is actually just the init constructor 'enhanced'  
  5.     return new jQuery.fn.init( selector, context, rootjQuery );  
  6.    },    
  7.    ...//此处省略900行  
  8.   return jQuery;  
  9. })();  

本文介绍的四种代码复用模式都是最佳实践,推荐大家在编程的过程中使用。

    function Person(name) { 

//声明一个新类
myLib.ImaginaryTraveler=my.Class({
travel:function(){console.log('Travelingonacarpet!');},
crossOceans:function(){console.log('SayinghitoMobyDick!');}
});
(function(){
//Dreamer继承Person实现ImaginaryTraveler的方法
varDreamer=my.Class(Person,ImaginaryTraveler,{
//构造方法
constructor:function(name,age,dream){
Dreamer.Super.call(this,name,age);
this.dream=dream;
}
//...
});
//暴露给全局命名空间
myLib.Dreamer=Dreamer;
})();
varaladdin=newDreamer('Aladdin');
aladdininstanceofPerson;//true
aladdininstanceofImaginaryTraveler;//false
aladdin.travel();
aladdin.wakeUp();
aladdin.sayHello();

Js代码  

这种方式的继承就是将父对象里所有的属性都复制到子对象上,一般子对象可以使用父对象的数据。

 

继承的思路,就是如果有两个参数,第一个参数做为父类被继承,第二参数用来声明新类的方法和属性以及构造函数,它同样可以被继承。

    之前用的是ExtJS,Ext是个Object,通过字面量创建的,Ext.js文件里,3.3.1版: 

复制代码 代码如下:

 

三、还有一种简单实现
实现思路很简单,就是利用ECMAScript5原型式继承Object.create方法,封装成一个方法,如果不支持ECMAScript5的环境,就平移退化到

    把dom元素放到了new出来的init对象上,此处this应该是一个对象,应该是个Object的,但是从Chrome调试看,此时this竟然显示为jQuery.fn.jQuery.init[0],Object.prototype.toString.call(this)结果是”[object Object]”,是个对象,为何显示这么奇怪呢?在FF里,this显示为[],按照道理说,对象应该不会这么显示的才对。

// 首先,定义一个新对象man
var man = Object.create(null);

上面的代码很简单,创建了一个Person类,给Person添加方法有两种途径,第一种是把方法注册在Person.prototype上,这里叫做原型方法,第二种是把方法注册在Person上,叫做静态方法。

由于写的比较笼统,可能有很多地方没有解析到,也可能有不准确的地方,还望指正。
看完上面几种解析,相信息自己也可以写出一个自己的封装类库出来,至于,怎么实现看个人喜好了。但基本的思都是一样的基于原型的继承方式和循环拷贝新方法。

       3.1、Ext(3.x)的继承跟《JavaScript高级程序设计》里讲到的寄生组合模式类似,这种实现方式复杂,不太容易理解。具体是在当前类和超类之间插入一个空对象,作为子类的原型对象,这个空对象的构造函数的原型指向超类的原型,然后把子类的方法,全部放入到这个空对象上。这样可以做到方法通过原型链实现继承,实例属性通过借用构造函数实现继承。这种方法也是目前最完美的实现方案。ExtJS继承的源码解析参考这里。

// 定义的2个参数分别是被混入的对象(reciving)和从哪里混入的对象(giving)
function augment(receivingObj, givingObj) {
    // 如果提供了指定的方法名称的话,也就是参数多余3个
    if (arguments[2]) {
        for (var i = 2, len = arguments.length; i < len; i ) {
            receivingObj.prototype[arguments[i]] = givingObj.prototype[arguments[i]];
        }
    }
    // 如果不指定第3个参数,或者更多参数,就混入所有的方法
    else {
        for (var methodName in givingObj.prototype) {
            // 检查receiving对象内部不包含要混入的名字,如何包含就不混入了
            if (!receivingObj.prototype[methodName]) {
                receivingObj.prototype[methodName] = givingObj.prototype[methodName];
            }
        }
    }
}

jQuery.fn = jQuery.prototype = { 

varClass=(function(){
/**
*Inheritsfunction.(node.js)
*
*@paramctorsubclass'sconstructor.
*@paramsuperctorsuperclass'sconstructor.
*/
varinherits=function(ctor,superCtor){
//显式的指定父类
ctor.super_=superCtor;
//ECMAScript5原型式继承并解除引用
if(Object.create){
ctor.prototype=Object.create(superCtor.prototype,{
constructor:{
value:ctor,
enumerable:false,
writable:true,
configurable:true
}
});
}else{
//无Object.create方法的平稳退化
functionF(){};
F.prototype=superCtor.prototype;
ctor.prototype=newF();
ctor.prototype.constructor=ctor;
}
};
/**
*Classfunction.
*/
returnfunction(){
//最后一个参数是新类方法、属性和构造函数声明
varsubClazz=arguments[arguments.length-1]||function(){};
//initialize是构造函数,否构造函数就是一个空函数
varfn=subClazz.initialize==null?function(){}:subClazz.initialize;
//继承除最一个参数以的类,多继承,也可以用作扩展方法
for(varindex=0;index<arguments.length-1;index ){
inherits(fn,arguments[index]);
}
//实现新类的方法
for(varpropinsubClazz){
if(prop=="initialize"){
continue;
}
fn.prototype[prop]=subClazz[prop];
}
returnfn;
}
})();

jQuery.find = Sizzle; 

复制代码 代码如下:

 

b、继承的实现

      4.0版本,此处global == window:

function mix() {
    var arg, prop, child = {};
    for (arg = 0; arg < arguments.length; arg = 1) {
        for (prop in arguments[arg]) {
            if (arguments[arg].hasOwnProperty(prop)) {
                child[prop] = arguments[arg][prop];
            }
        }
    }
    return child;
}

        Person.sayHello(this); 

b、继承一个类

         2.2.1、jQuery(function(){}),当传入是function的时候,init方法片段:

    F.prototype = o;
    return new F();
}

 

如果怕忘记new操作符

    jQuery(xxx)的时候,转调到

// 测试
console.log(child.name); // "Papa"

    // 静态方法 

二、KLASS
项目地址:
先看使用方法:
a、新建一个类

    在jquery.js最后,把变量jQuery赋值给了$,后续可以通过$这种简写使用jQuery,算是一个简写的别名吧:

var cake = mix(
                { eggs: 2, large: true },
                { butter: 1, salted: true },
                { flour: '3 cups' },
                { sugar: 'sure!' }
                );

    Person.prototype.sayHello = function() { 

c、字面量方式声明一个类

  1. // Handle HTML strings  
  2. if ( typeof selector === "string" ) {  
  3.    // Are we dealing with HTML string or an ID?  
  4.    if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {  
  5.       // Assume that strings that start and end with <> are HTML and skip the regex check  
  6.       match = [ null, selector, null ];  
  7.    } else {  
  8.       match = quickExpr.exec( selector );  
  9.    }  
  10.    // Verify a match, and that no context was specified for #id  
  11.    if ( match && (match[1] || !context) ) {  
  12.       ……//省略几十行,这一段是避免xss攻击什么的,没读懂,以后再来读  
  13.           return this;  
  14.       }  
  15.    // HANDLE: $(expr, $(...))  
  16.    } else if ( !context || context.jquery ) {  
  17.       return (context || rootjQuery).find( selector );  
  18.    // HANDLE: $(expr, context)  
  19.    // (which is just equivalent to: $(context).find(expr)  
  20.    } else {  
  21.       return this.constructor( context ).find( selector );  
  22.    }  
  23. }  

// 要继承的父对象
var parent = {
    name: "Papa"
};

        return obj; 

b、继承一个类

Js代码  

    for (i in parent) {
        if (parent.hasOwnProperty(i)) {
            if (typeof parent[i] === 'object') {
                child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
                extendDeep(parent[i], child[i]);
            } else {
                child[i] = parent[i];
            }
        }
    }
    return child;
}

再有就是prototype还可以等于其他类,例如Person.prototype = new Animal(),这样tom在调用sayHello方法的时候,当找到Person.prototype属性时发现是Animal,浏览器会继续向上追溯,查找Animal.prototype属性上有没有sayHello方法,没有则继续向上查一直到Object的prototype属性。这样就实现了类的继承,这样的继承也叫原型继承。

  1. window.jQuery = window.$ = jQuery;   

var dad = { name: "Adam" };
var kid = extend(dad);
console.log(kid.name); // "Adam"

 

虽然jQuery是个function,但是可以在function上添加属性(这么叫准确么?)然后就可以直接jQuery.method()、jQuery.filed的调了。这里jQuery至少起到了一个命名空间的作用。 

同时,ECMAScript5也提供了类似的一个方法叫做Object.create用于继承对象,用法如下:

上一篇,我们介绍了jQuery是如何new出来的jQuery对象,这一篇讲解jQuery方法是如何继承的。

if (typeof Function.prototype.bind === 'undefined') {
    Function.prototype.bind = function (thisArg) {
        var fn = this,
slice = Array.prototype.slice,
args = slice.call(arguments, 1);
        return function () {
            return fn.apply(thisArg, args.concat(slice.call(arguments)));
        };
    };
}

在分析jQuery的方法继承前,我们先来弄清楚javas...

// 测试
console.log(one.say('hi')); // "hi, object"

     

 

    } 

        alert(p.name ', hello !'); 

 

    } 

        this.name = name; 

    if ( !prop ) { prop = obj; obj = this; } 

    }, 

静态方法很简单,就相当于java里的static method,在内存中只有一份,并且它的函数内部this是指向Person的。

 

    <title></title> 

    return obj; 

    <script type="text/javascript"> 

    each: function( fn, args ) { 

本文由9159.com发布于前端,转载请注明出处:而是指我们在写程序的时候,原型继承是让父对

关键词: 9159.com

上一篇:语法解释,jquery与之对应的是val
下一篇:没有了