【深入JavaScript】一种JS的继承方法

这些天读了John Resig的《Secrets of JavaScript Ninja》,其中讨论到JS中实现继承的方案,非常有趣,自己探索了一下,形成了笔记,放到这里。

这个方案在Resig的博客上也有,虽然代码略微有点不一致,但核心思想是一样的,请戳 这里 。

<html>
<head>
    <title></title>
</head>
<body>
<script type="text/javascript">
    // call a immediate funciton,prevent global namespace from being polluted.
    (function(){
        // 这个initializing变量用于标识当前是否处于类的初始创建阶段,下面会继续详述
        var initializing = false,
        // 这是一个技巧性的写法,用于检测当前环境下函数是否能够序列化
        // 附一篇讨论函数序列化的文章:http://www.cnblogs.com/ziyunfei/archive/2012/12/04/2799603.html
        // superPattern引用一个正则对象,该对象用于验证被验证函数中是否有使用_super方法
            superPattern = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

        Object.subClass = function(properties){
            // 当前对象(父类)的原型对象
            var _super = this.prototype;

            // initializing = true表示当前处于类的初始创建阶段。
            // this构造函数里会判断initializing的状态,如果为false则不执行Init方法。
            // 事实上这也是非常需要的,因为在这个时候,我们需要的只是一个干净的虚构的构造函数,完全不需要其执行init函数,以避免污染。init方法只有在当前类被实例化的时候才需要被执行,而当前正执行继承行为,不应该执行Init方法。
            initializing = true;
            // 当前对象(父类)的一个实例对象
            var proto = new this();
            // 初始创建阶段完成,置initializing为false
            initializing = false;

            // 在properties里提供的属性,作为当前对象(父类)实例的公共属性,供其子类实例共享;
            // 在properties里提供的方法,作为当前对象(父类)实例的公共方法,供其子类实例共享。
            for(var name in properties){
                proto[name] = typeof properties[name] == ‘function‘ && //检测当前提供的是否为函数
                              typeof _super[name] == ‘function‘ && //检测当前提供的函数名是否已经存在于父类的原型对象中,如果是,则需要下面的操作,以保证父类中的方法不会被覆盖且可以以某种方式被调用,如果否,则直接将该函数赋值为父类实例的方法
                              superPattern.test(properties[name]) ? f//检测当前提供的函数内是否使用了_super方法,如果有使用_super方法,则需要下面的操作,以保证父类中的方法不会被覆盖且可以以某种方式被调用,如果没有用到_super方法,则直接将该函数赋值为父类实例的方法,即使父类原型中已经拥有同名方法(覆盖)

                    // 使用一个马上执行的函数,返回一个闭包,这样每个闭包引用的都是各自的name和fn。
                    (function(name, fn){
                        return function() {
                            // 首先将执行方法的当前对象(子类的实例化对象)的_super属性保存到tmp变量里。
                            // 这是非常必要的, 因为this永远指向当前正在被调用的对象。
                            // 当C继承B,B继承A,而A\B\C均有一个dance方法且B\C的dance方法均使用了this._super来引用各自父类的方法时,下面这句操作就显得非常重要了。它使得在方法调用时,this._super永远指向“当前类”的父类的原型中的同名方法,从而避免this._super被随便改写。
                            var tmp = this._super;

                            // 然后将父类的原型中的同名方法赋值给this._super,以便子类的实例化对象可以在其执行name方法时通过this._super使用对应的父类原型中已经存在的方法
                            this._super = _super[name];

                            // 执行创建子类时提供的函数,并通过arguments传入参数
                            var ret = fn.apply(this, arguments);

                            // 将tmp里保存的_super属性重新赋值回this._super中
                            this._super = tmp;

                            // 返回函数的执行结果
                            return ret;
                        };
                    })(name, properties[name]) :
                    properties[name];
            }

            // 内部定义个名叫Class的类,构造函数内部只有一个操作:执行当前对象中可能存在的init方法
            // 这样做的原因:新建一个类(闭包),可以防止很多干扰(详细可对比JS高级设计第三版)
            function Class(){
                // 如果不是正在实现继承,并且当前类的init方法存在,则执行init方法
                // 每当subClass方法执行完毕后,都会返回这个Class构造函数,当用户使用new 方法时,就会执行这里面的操作
                // 本质:每次调用subClass都新建一个类(闭包)
                if(!initializing && this.init){
                    // 这是子类的初始化方法,里面可以定义子类的私有属性,公共属性请在上方所述处添加
                    this.init.apply(this, arguments);
                }
            }

            // 重写Class构造函数的prototype,使其不再指向了Class原生的原型对象,而是指向了proto,即当前对象(类)的一个实例
            // 本质:一个类的原型是另一个类的实例(继承)
            Class.prototype = proto;
            // 为什么要重写Class的构造函数?因为这个Class函数,它原来的constructor指向的是Function对象,这里修正它的指向,使其指向自己。
            Class.constructor = Class;
            // 就是这个操作,使得每次调用subClass都会新生命的Class对象,也拥有subClass方法,可以继续被继承下去
            // 本质:使得每次继承的子类都拥有被继承的能力
            Class.subClass = arguments.callee;
            // 返回这个内部新定义的构造函数(闭包)
            return Class;
        };
    })();

    var Person = Object.subClass({
        init: function(isDancing) {
            this.dancing = isDancing;
        },
        dance: function(){
            console.log(‘i am a person,i dance.‘);
            return this.dancing;
        }
    });

    var Ninja = Person.subClass({
        init:function(){

        },
        dance: function() {
            console.log(‘i am an Ninja,i dance.‘);
            this._super();
            return;
        },
        swingSword:function(){
            return true;
        }
    });

    var Chileung = Ninja.subClass({
        dance: function(){
            console.log(‘i am Chileung.i dance.‘);
            this._super();
            return;
        }
    });

    var p = new Person();
    p.dance();

    var n = new Ninja();
    n.dance();

    var c = new Chileung();
    c.dance();
</script>
</body>
</html>
在博客园里也找到了一篇不错的有关这种继承方式的讨论,参见 这里

另外自己以前曾经也思考过Zakas提出的继承方案,文章见 这里

时间: 2024-10-07 05:31:45

【深入JavaScript】一种JS的继承方法的相关文章

javascript四种类型识别的方法

× 目录 [1]typeof [2]instanceof [3]constructor[4]toString 前面的话 javascript有复杂的类型系统,类型识别则是基本的功能.javascript总共提供了四种类型识别的方法,本文将对这四种方法进行详细说明 typeof运算符 typeof是一元运算符,放在单个操作数的前面,返回值为表示操作数类型的首字母小写的字符串 [注意]typeof运算符后面带不带圆括号都可以 console.log(typeof 'a');//'string' co

Js的继承方法

Js继承 要想继承,就必选有一个要被继承的产业 function who(name){ this.name = name this.say = function(){ console.log(this.name) } } who.prototype.age = 10 第一种方法:原型链继承 function xiaoming(){ this.name = 'xiaoming' } xiaoming.prototype = new who() let xiaoming1 = new xiaomin

JavaScript 五种(构造方式)继承

一.对象冒充 function Parent(username){ this.username = username; this.hello = function(){ alert(this.username); } } function Child(username,password){ //通过以下3行实现将Parent的属性和方法追加到Child中,从而实现继承 //第一步:this.method是作为一个临时的属性,并且指向Parent所指向的对象, //第二步:执行this.metho

三种JS截取字符串方法

JS提供三个截取字符串的方法,分别是:slice(),substring()和substr(),它们都可以接受一个或两个参数: var stmp = "rcinn.cn"; 使用一个参数 alert(stmp.slice(3));//从第4个字符开始,截取到最后个字符;返回"nn.cn" alert(stmp.substring(3));//从第4个字符开始,截取到最后个字符;返回"nn.cn" 使用两个参数 alert(stmp.slice(1

javascript 自动绑定JS callback 的方法函数

自己写的一个javascript 智能绑定callback 并且调用执行的函数,主要用于异步请求的 ajax中: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <script> /** * @author [email 

JavaScript四种数值取整方法

一.Math.trunc() 1.定义 Math.trunc()方法去除数字的小数部分,保留整数部分. 2.语法 Math.trunc(value) 3.示例 console.log(Math.trunc(2.01)); // 2 console.log(Math.trunc(2.9)); // 2 console.log(Math.trunc('0.22')); // 0 console.log(Math.trunc(-1.22)); // -1 console.log(Math.trunc(

JavaScript 几种数组去重的方法

一.利用for嵌套for,然后splice去重 let arr=[5,5,5,12,12,-3,33,33,35,8]; for( let i=0; i<arr.length;i++){ for(let k=i+1;k<arr.length;k++){ if(arr[i]===arr[k]){ arr.splice(i,1); i--; } }} console.log(arr) 二.Set方法去重 let arr = [1,2,2,3,4,3,1,6,7,3,5,7]; let s1 = n

js oop中的三种继承方法

JS OOP 中的三种继承方法: 很多读者关于js opp的继承比较模糊,本文总结了oop中的三种继承方法,以助于读者进行区分. <继承使用一个子类继承另一个父类,子类可以自动拥有父类的属性和方法.(继承的两方,发生在两个类之间)> 一.通过object实现继承 1:定义父类 function Parent(){} 2:定义子类 funtion Son(){} 3:通过原型给Object对象添加一个扩展方法. Object.prototype.customExtend = function(p

5种JS继承方法

<!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" content="http://www.jb51.net/" /> <title>5种JS继承方法</title> <script type="text/javascript"> //1