Home > Web Front-end > JS Tutorial > body text

Summary of javascript inheritance learning experience_basic knowledge

WBOY
Release: 2016-05-16 15:10:22
Original
993 people have browsed it

After reading a lot of things about js inheritance, it’s time to summarize.
Let me first give you a general understanding. I hope you can correct me if I am wrong, so that I can correct my three views. In addition, the following examples are not original, they are just changed variable names, and some are even used directly.

What is js inheritance used for:

First of all, js does not have the same concept of inheritance as other object-oriented languages. The inheritance mentioned in js refers to simulated inheritance.
What exactly does js inheritance do? When I first started working on the front-end, I used it for interviews (I basically didn’t use it when I first wrote some small effects. Why do I need to read it? Because interviewers love to ask this question), so I just used it for interviews. Take a rough look at it, and if you can give a rough outline during the interview, you are considered a good interviewer on this issue. Later, I gradually started to use it even though the concept was not very clear.
What is it really used for? It is mainly used to reuse the code we have written before. For example, if we have written a function, an object, or used something written by someone else, and we want to add something ourselves, we can't change someone else's stuff. We can just inherit it and use it. This is the real use of inheritance.

How to implement js inheritance:
I won’t go into the code first, let’s talk about my thoughts first. In fact, inheritance is to find ways to transfer the properties or methods of other objects (everything in js is an object) to our own object, so that our own object can be used. This also achieves the purpose of reuse.
Now that the purpose is clear, here are the means to achieve it.
According to the characteristics of js, implementation is nothing more than one or a combination of the following methods.

1. Constructor, js does not seem to have a strict definition of constructor, but you can use new to create new objects. It is said that the constructor is also a method for implementing inheritance in strict object-oriented languages, so of course js can simulate it, so people who have learned the oop language will think of this first.

2. Use function prototypes and prototype chains to link two objects. Because the js prototype chain is relatively unique, it is easy to think of this.
There are several types of prototypes, that is, what is used as the prototype of the inherited object, the prototype of the inherited object, or an instance of the inherited object, or the direct inheritor. The inheritance effects obtained by these prototypes as inheritance objects are different.

3. Copy attributes and methods. Just copy and clone all the attributes or methods of the inherited object and turn them into the attributes and methods of our own object. Then we can use them very smoothly. Of course, this can be divided into two situations: shallow copying and deep copying.

4. Use the two methods call and apply. These two methods are quite magical and can change the context of function execution (this). Therefore, using these two methods can also realize the inheritance and reuse of the methods of the inherited object. .

Generally speaking, the ways to achieve inheritance in js are probably these. The ever-changing implementation methods are all combined and upgraded based on these methods. Why are most of them used in combination? Of course, it is because of the effect of using a single method. Not ideal. Of course, you can choose which method to use based on the actual needs of your project. As long as it meets your own needs, it does not say which method must be used to achieve it. Just like saying that the fastest way to go from Beijing to Shijiazhuang is by plane. But if it's far from the airport, including getting to the airport and going from the airport to the city, it's not as fast as the high-speed rail overall, then you can take the high-speed rail. For example, if you have a car and can drive, but if you want a challenge, you can ride a bicycle. You can choose this based on your actual situation.


Code implementation, let’s talk about the above implementation methods based on the code. Some of them are taken from other places and add some comments.

After reading a lot of things about js inheritance, it’s time to summarize.
Let me first give you a general understanding. I hope you can correct me if I am wrong, so that I can correct my three views. In addition, the following examples are not original, they are just changed variable names, and some are even used directly.

What is js inheritance used for:

First of all, js does not have the same concept of inheritance as other object-oriented languages. The inheritance mentioned in js refers to simulated inheritance.
What exactly does js inheritance do? When I first started working on the front-end, I used it for interviews (I basically didn’t use it when I first wrote some small effects. Why do I need to read it? Because interviewers love to ask this question), so I just used it for interviews. Take a rough look at it, and if you can give a rough outline during the interview, you are considered a good interviewer on this issue. Later, I gradually started to use it even though the concept was not very clear.
What is it really used for? It is mainly used to reuse the code we have written before. For example, if we have written a function, an object, or used something written by someone else, and we want to add something ourselves, we can't change someone else's stuff. We can just inherit it and use it. This is the real use of inheritance.

js 継承の実装方法:
コードのことを先に話すのではなく、アイデアについて話しましょう。実際、継承とは、他のオブジェクト (js 内のすべてのものがオブジェクトです) のプロパティまたはメソッドを独自のオブジェクトに転送して、独自のオブジェクトを使用できるようにする方法を見つけることです。これにより、再利用の目的も達成されます。
目的が明確になったので、次にそれを達成するための手段を示します。
js の特性上、実装は以下のメソッドの 1 つまたは組み合わせに過ぎません。

1. コンストラクター、js にはコンストラクターの厳密な定義はないようですが、新しいオブジェクトを作成するために new を使用できます。コンストラクタは厳密なオブジェクト指向言語で継承を実装する手法でもあるとのことで、もちろんjsでもシミュレートできるので、oop言語を学習した人はまずこれを思い浮かべると思います。

2. 関数プロトタイプとプロトタイプ チェーンを使用して 2 つのオブジェクトをリンクする js プロトタイプ チェーンは比較的ユニークなので、これを考えるのは簡単です。
プロトタイプにはいくつかのタイプがあります。つまり、継承されたオブジェクトのプロトタイプとして使用されるもの、継承されたオブジェクトのプロトタイプ、継承されたオブジェクトのインスタンス、または直接継承者です。これらのプロトタイプを継承対象として得られる継承効果は異なります。

3. 属性とメソッドをコピーします。継承されたオブジェクトのすべての属性またはメソッドをコピーして複製し、それらを独自のオブジェクトの属性とメソッドに変換するだけです。もちろん、これは、浅いコピーと深いコピーの 2 つの状況に分類できます。

4. call と apply の 2 つのメソッドを使用します。これら 2 つのメソッドは非常に魔法であり、関数の実行コンテキストを変更できます。したがって、これら 2 つのメソッドを使用すると、継承されたメソッドの継承と再利用も実現できます。物体。 。

一般的に、js で継承を実現する方法はおそらくこれらです。これらのメソッドをベースに、これらのメソッドを組み合わせてアップグレードすることがほとんどです。単一の方法では理想的ではありません。もちろん、プロジェクトの実際のニーズに基づいて使用する方法を選択できますが、それが自分のニーズを満たしている限り、それを達成するためにどの方法を使用する必要があるかは決まりません。北京から石家荘へ行くのに一番早い方法は飛行機だと言っているのと同じです。ただし、空港から遠い場合、空港までの移動や空港から市内への移動など、全体的に高速鉄道ほど速くない場合は、高速鉄道を利用できます。たとえば、車を持っていて車の運転もできますが、挑戦したい場合は自転車に乗ることができます。これは実際の状況に応じて選択できます。


コードの実装。コードに基づいて上記の実装方法について説明します。一部は他の場所から引用し、いくつかのコメントを追加します。

1. コンストラクターの実装 (コンストラクターを借用):

function Super(arg){
      this.arr1 = "I'm super "+arg;
      this.show = function(){
        alert(this.arr1);
      }
    }

    Super.prototype.say = function(){
      alert(this.arr1);
    }

    function suber(arg){
      Super.apply(this, arguments); //在suber的上下文中运行super
    }

    var sub =new suber("suber");
    var sub2 = new suber("suber1");

    console.log(sub.arr1); //I'm super suber
    console.log(sub.show); //function (){ alert(this.arr1);}
    console.log(sub.say); //undefined
    console.log(sub.show === sub2.show); //false
Copy after login

おっと、sub.say が未定義であることがわかりました。これは、以下の 2 つのオブジェクト sub と sub2 の表示が等しくありません。これは、2 つの関数が 2 つの異なるオブジェクトを指していることを意味します。コピーされて2冊出ました。

このメソッドが継承を実装する場合、プロトタイプ オブジェクトのプロパティとメソッドは継承されません。Super のプロパティとメソッドは、新しいオブジェクトごとに個別にコピーされます。
したがって、プロトタイプのメソッドはどれも継承されていないため、このメソッドを単独で使用して継承を実装することは適切ではありません。そこでマスターたちはプロトタイプ継承というアイデアを思いつきました

2. プロトタイプの継承:

function Super(arg){
      this.arr1 = "I'm super "+arg;
      this.show = function(){
        alert(this.arr1);
      }
    }

    Super.prototype.say = function(){
      alert(this.arr1);
    }

    function suber(arg){}

    suber.prototype = new Super();

    var sub = new suber("suber1");
    var sub2 = new suber("suber2");

    console.log(sub.arr1); //I'm super undefined
    console.log(sub.show); //function (){ alert(this.arr1);}
    console.log(sub.say); //function (){ alert(this.arr1);}
    console.log(sub.show === sub2.show);  //true;
    console.log(sub.say === sub2.say);  //true;
Copy after login


今回はarr1を継承しましたが、パラメータが追加されておらず未定義のため、このメソッドのサブクラスを宣言した際に渡されたパラメータとサブクラスから継承したプロパティを受け取ることができませんでした。それ以外はすべてごく普通です。 Show と Say は両方とも継承されます。ただし、注意すべき点が 1 つあります。say は super のプロトタイプ オブジェクトを通じて継承され、show は新しいスーパー オブジェクト インスタンスを作成するときのインスタンスの属性であるということです。

それでは、プロトタイプにパラメータの送信と継承をどのように実装するか? もちろん、上記の 2 つの方法を組み合わせるだけなので、先輩たちは次の方法を考案しました。


3. 結合された継承 (コンストラクターを借用し、プロトタイプを設定):

function Super(arg){
      this.arr1 = "I'm super "+arg;
      this.show = function(){
        alert(this.arr1);
      }
    }

    Super.prototype.say = function(){
      alert(this.arr1);
    }

    function suber(arg){
      Super.apply(this, arguments);
    }

    suber.prototype = new Super();


    var sub = new suber("suber1");
    var sub2 = new suber("suber2");

    console.log(sub.arr1); //I'm super suber1
    console.log(sub.show); //function (){ alert(this.arr1);}
    console.log(sub.say); //function (){ alert(this.arr1);}
    console.log(sub.show === sub2.show);  //false;
    console.log(sub.say === sub2.say);  //true;
Copy after login

今回はほぼ完璧ですが、sub.show と sub2.show が等しくないことがわかります。これはなぜですか? apply の場所によって show が suber の独自の属性になるため、それを suber プロトタイプに入れるだけです。 show (Super の show は suber プロトタイプ オブジェクトのインスタンス オブジェクトとして使用されます) は上書きされるため、一度に 1 つずつコピーされることになります。これを避ける方法はありません。この不必要なオーバーヘッドが発生しないようにするために、これらの共有可能な関数の多くをプロトタイプ オブジェクトに配置できます。
suber の構築時の呼び出しと、suber プロトタイプ オブジェクトに値を代入するときの呼び出しにより、Super が 2 回呼び出されるため、新しい suber オブジェクトが作成されるたびに Super が 2 回呼び出され、次のときに 2 つのインスタンス オブジェクトが生成されます。過剰なリソースを 2 回呼び出す必要があります。

そこで、先輩たちはこの問題を解決するために心を開いて、次の方法を開発しました。

4. 寄生組み合わせの継承:
このメソッドとメソッド 3 の主な違いは、親クラスのプロトタイプが親クラスの代わりに子クラスのプロトタイプに割り当てられることです。例を参照してください。

function Super(arg){
  this.arr1 = "I'm super "+arg;
  
}

Super.prototype.show = function(){ //这个方法放到了原型对象上。
    alert(this.arr1);
  }
Super.prototype.say = function(){
  alert(this.arr1);
}

function suber(arg){
  Super.apply(this, arguments);
}

/*inherit函数的作用,使用一个新的空函数,来切断父类对象的原型对象与子类原型对象的直接联系,而是通过这个空构造的实例对象实现继承,这样可以避免更改子类原型的属性或者方法而影响了父类原型对象的属性或者方法。*/

function inherit(obj){ 
  function F(){}
  F.prototype = obj;
  return new F();  
}

suber.prototype = inherit(Super.prototype);


var sub = new suber("suber1");
var sub2 = new suber("suber2");

console.log(sub.arr1); //I'm super suber1
console.log(sub.show); //function (){ alert(this.arr1);}
console.log(sub.say); //function (){ alert(this.arr1);}
console.log(sub.show === sub2.show);  //true;
console.log(sub.say === sub2.say);  //true;
Copy after login

好了,这样就把三方法的弊端干掉了,这个可以完美的使用了吧。

五、复制属性实现

拷贝我们可以写一个拷贝函数来实现。

function extend(Super,suber){
  suber = suber || {};
  for(var i in Super){
    if(Super.hasOwnProperty(i)){
      suber[i] = Super[i];
    }
   }
  return suber;
}


var parent = {
  name:"dad",
  num:[1,2,3],
  say:function(){alert("dad");}
}

var child = {
  age:5,
  sex:"boy"
};

child = extend(parent, child);

//以下测试
console.log(child); /*{
             age:5,
             sex:"boy",
             name:"dad",
             num:[1,2,3],
             say:function(){alert("dad");}
            }*/
console.log(child.say === parent.say); //true
console.log(child.num === parent.num); //true
Copy after login

复制成功,那么child成功继承了parent的一些属性,但是后面两个测试发现他们是相等的,就表明了他们在公用同一个数组,同一个函数,函数这个可以,但是数组这个就有问题了,如果一个chiild改变了数组,几个被继承对象的数组也跟着变了,这就不给力了啊。
为什么会发生这种情况呢,js里边对象存储的是指针,然后这个指针指向这个值,我们在这复制的实际是指向该对象的指针的值,所以继承对象和被继承对象都指向了同一个对象,接下来看看如何使用深度复制来解决这个问题。

深度复制对象属性:

function extenddeep(Super, suber){
      var tostr = Object.prototype.toString, astr = "[object Array]";
      suber = suber || {};

      for(var i in Super){
        if(Super.hasOwnProperty(i)){
          if(typeof Super[i] === "object"){
            suber[i] = (tostr.call(Super[i]) == astr) ? [] : {};
            extenddeep(Super[i],suber[i]);
          }else {
            suber[i] = Super[i];
          }
        }
      }

      return suber;
    }


    var parent = {
      name:"papa",
      num:[1,2,3],
      say:function(){alert("I'm father of my son!");}
    }

    var child = {
      name:"jone",
      sex:"boy",
    }

    var kid = extenddeep(parent, child);

    console.log(kid);  // {name: "papa"
                num: Array[3]
                say: ()
                sex: "boy"
              // }

    console.log(kid.say === parent.say); //true
    console.log(kid.num === parent.num);  //false
    console.log(kid.name); //papa
Copy after login

好了,深度复制完毕,但似有木有发现问题,name是parent的,也就是说如果继承对象有和被继承对象一样的属性名的属性如果不做处理就会被替换掉。那么我们可以做一下处理,先声明一个属性,保存parent里的东西,剩下的的当然就是child自己的东西了,最后再把属性给child对象就可以了。

六、利用call和apply这两个方法(借用方法)。
这个就是通过call和apply来复用其他对象的方法,达到复用的目的。

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

    var tow = {
      name:"two"
    }

    one.say.call(tow, "hi");  //hi, two
Copy after login

这个就是借用了,好了,下课。

好吧,好吧,其实这里边还有其他东西要看。可以借用并不“带表”可以随便把某个方法赋值给谁然后跟没发生什么似的继续用。所以我们平时使用借用时要注意一下上下文,下面看下那些容易出错的地方。

//赋值给一个变量时候上下文会变化
    var say = one.say;
    console.log(say('hoho')); // "hoho, undefined"

    //作为回调函数时也会发生变化
    var yetother = {
      name:"yetother obj",
      method:function(callback){
        return callback("Hola");
      }
    }

    console.log(yetother.method(one.say)); //"Hola, "
Copy after login

神马意思呢,就是this.name是undefined,当one.say赋值给say是,实际上是say存储了指向函数对象的指针,say这个变量明显又是全局变量的一个属性,那么它运行的时候实际的上下文就变成了windows了,当然这个时候name就变成undefined了。回调这个也是一样,return 的是函数运行的结果。如果我们事先设置 windows.name="windows" 那么得到的结果就变成了 "hoho, windows" 和"Hola, windows" 了。

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

    var othersay = bind(yetother, one.say);

    othersay("Hola"); //"Hola, yetother obj"
Copy after login

通过apply可以改变方法执行的上下文,那么我们构建一个函数来实现这样一个功能,通过使用方法调用实现上下文的改变,这样就不会出现上下文不是我们期望的上下文的情况了。

//这段是直接复制过来的。
    // ECMAScript 5给Function.prototype添加了一个bind()方法,以便很容易使用apply()和call()。

      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)));
          };
        };
      }

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

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

介绍完了,该说说自己的疑惑了,当复制属性方法遇到的被继承对象里边存在方法,如何单独复制出来呢,现在的是直接共用了,因为毕竟方法一般不会经常改动。求解答?

下面是转载过来的jQuery的extend方法,好像也没有特殊处理函数这块,继承完了两个函数也是共用的。

$.extend源码

jQuery.extend = jQuery.fn.extend = function() {
          var options, name, src, copy, copyIsArray, clone,
             target = arguments[0] || {},
             i = 1,
             length = arguments.length,
             deep = false ;

          // Handle a deep copy situation
          //如果第一个参数是boolean类型
          //修正参数,将第二个参数作为target
          if ( typeof target === "boolean" ) {
             deep = target;

              // skip the boolean and the target
             target = arguments[ i ] || {};
              //i++是为了后续 i === length的判断
             i++;
          }

          // Handle case when target is a string or something (possible in deep copy)
          //如果目标既不是对象也不是方法(例如给基本类型扩展属性方法和属性不会报错但是是无用的),修正target为 js对象
          if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
             target = {};
          }

          // extend jQuery itself if only one argument is passed
          //如果只有一个参数,修正对象为JQuery函数或JQuery对象
          if ( i === length ) {
             target = this ;
              //修正target所在位置,后面的都是要添加给target的对象
             i--;
          }

          for ( ; i < length; i++ ) {
              // Only deal with non-null/undefined values
              if ( (options = arguments[ i ]) != null ) {
                 // Extend the base object
                 for ( name in options ) {
                    src = target[ name ];
                    copy = options[ name ];

                    // Prevent never-ending loop
                    //如果target和copy是同一个对象,略过,防止自己的属性引用了本身对象导致的循环引用,以致GC无法回收
                    if ( target === copy ) {
                        continue ;
                    }

                    // Recurse if we're merging plain objects or arrays
                    //如果是deep为true,并且要添加给target的copy为对象获数组
                    if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                        if ( copyIsArray ) {
                           copyIsArray = false ;
                           clone = src && jQuery.isArray(src) &#63; src : [];

                       } else {
                           clone = src && jQuery.isPlainObject(src) &#63; src : {};
                       }
                 
                        // Never move original objects, clone them
                        //很巧妙 ,用一个递归,实现引用对象的深克隆,递归的返回条件是属性石基本类型,基本类型都是深克隆
                       target[ name ] = jQuery.extend( deep, clone, copy );

                    // Don't bring in undefined values
                    } else if ( copy !== undefined ) {
                        //浅克隆
                       target[ name ] = copy;
                    }
                 }
             }
          }

          // Return the modified object
          return target;
      };
Copy after login

以上这篇javascript 继承学习心得总结就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!