Après avoir lu beaucoup de choses sur l'héritage js, il est temps de résumer.
Permettez-moi d'abord de vous donner une compréhension générale. J'espère que vous pourrez me corriger si je me trompe, afin que je puisse corriger mes trois points de vue. De plus, les exemples suivants ne sont pas originaux, ce sont simplement des noms de variables modifiés, et certains sont même utilisés directement.
À quoi sert l'héritage js :
Tout d'abord, js n'a pas le même concept d'héritage que les autres langages orientés objet. L'héritage mentionné dans js fait référence à l'héritage simulé.
À quoi sert exactement l'héritage js ? Lorsque j'ai commencé à travailler sur le front-end, je l'ai utilisé pour des interviews (je ne l'ai pratiquement pas utilisé lorsque j'ai écrit quelques petits effets pour la première fois. Pourquoi dois-je le lire ? Parce que les intervieweurs adorent pour poser cette question), je l'ai donc juste utilisé pour des entretiens. Jetez-y un coup d'œil, et si vous pouvez donner un aperçu lors de l'entretien, vous êtes considéré comme un bon intervieweur sur cette question. Plus tard, j’ai progressivement commencé à l’utiliser même si le concept n’était pas très clair.
A quoi sert-il réellement ? Il sert principalement à réutiliser le code que nous avons écrit auparavant. Par exemple, si nous avons écrit une fonction, un objet ou utilisé quelque chose écrit par quelqu'un d'autre et que nous voulons ajouter quelque chose nous-mêmes, nous ne pouvons pas modifier les éléments de quelqu'un d'autre. Nous pouvons simplement en hériter et l'utiliser. utilisation réelle de l'héritage.
Comment implémenter l'héritage js :
Je n’entrerai pas dans le code en premier, parlons d’abord de mes réflexions. En fait, l'héritage consiste à trouver des moyens de transférer les propriétés ou les méthodes d'autres objets (tout dans js est un objet) vers notre propre objet, afin que notre propre objet puisse être utilisé. Cela permet également d’atteindre l’objectif de réutilisation.
Maintenant que le but est clair, voici les moyens pour y parvenir.
Selon les caractéristiques de js, l'implémentation n'est rien de plus qu'une ou une combinaison des méthodes suivantes.
1. Constructeur, js ne semble pas avoir de définition stricte du constructeur, mais vous pouvez utiliser new pour créer de nouveaux objets. On dit que le constructeur est également une méthode pour implémenter l'héritage dans des langages strictement orientés objet, donc bien sûr js peut le simuler, donc les personnes qui ont appris le langage oop y penseront en premier.
2. Utilisez des prototypes de fonctions et des chaînes de prototypes pour relier deux objets. Parce que la chaîne de prototypes js est relativement unique, il est facile d'y penser.
Il existe plusieurs types de prototypes, c'est-à-dire ce qui est utilisé comme prototype de l'objet hérité, prototype de l'objet hérité ou instance de l'objet hérité, ou encore héritier direct. Les effets d'héritage obtenus par ces prototypes en tant qu'objets d'héritage sont différents.
3. Copiez les attributs et les méthodes. Copiez et clonez simplement tous les attributs ou méthodes de l'objet hérité et transformez-les en attributs et méthodes de nos propres objets. Bien entendu, cela peut être divisé en deux situations : la copie superficielle et la copie approfondie.
4. Utilisez les deux méthodes call et apply. Ces deux méthodes sont assez magiques et peuvent changer le contexte d'exécution de la fonction (this). Par conséquent, l'utilisation de ces deux méthodes peut également réaliser l'héritage et la réutilisation des méthodes héritées. objet. .
D'une manière générale, les moyens d'obtenir l'héritage dans js sont probablement les suivants. Les méthodes d'implémentation en constante évolution sont toutes combinées et mises à niveau en fonction de ces méthodes. Pourquoi la plupart d'entre elles sont-elles utilisées en combinaison ? une seule méthode. Pas idéal. Bien entendu, vous pouvez choisir la méthode à utiliser en fonction des besoins réels de votre projet, à condition qu'elle réponde à vos propres besoins et ne précise pas quelle méthode doit être utilisée pour y parvenir. C'est comme dire que le moyen le plus rapide pour aller de Pékin à Shijiazhuang est l'avion. Mais si c'est loin de l'aéroport, y compris pour se rendre à l'aéroport et aller de l'aéroport à la ville, ce n'est pas aussi rapide que le train à grande vitesse dans son ensemble, alors vous pouvez prendre le train à grande vitesse. Par exemple, si vous avez une voiture et pouvez conduire, mais si vous voulez relever un défi, vous pouvez faire du vélo. Vous pouvez choisir cela en fonction de votre situation réelle.
Implémentation du code, parlons des méthodes d'implémentation ci-dessus basées sur le code. Certaines d'entre elles sont extraites d'autres endroits et ajoutons quelques commentaires.
Après avoir lu beaucoup de choses sur l'héritage js, il est temps de résumer.
Permettez-moi d'abord de vous donner une compréhension générale. J'espère que vous pourrez me corriger si je me trompe, afin que je puisse corriger mes trois points de vue. De plus, les exemples suivants ne sont pas originaux, ce sont simplement des noms de variables modifiés, et certains sont même utilisés directement.
À quoi sert l'héritage js :
Tout d'abord, js n'a pas le même concept d'héritage que les autres langages orientés objet. L'héritage mentionné dans js fait référence à l'héritage simulé.
À quoi sert exactement l'héritage js ? Lorsque j'ai commencé à travailler sur le front-end, je l'ai utilisé pour des interviews (je ne l'ai pratiquement pas utilisé lorsque j'ai écrit quelques petits effets pour la première fois. Pourquoi dois-je le lire ? Parce que les intervieweurs adorent pour poser cette question), je l'ai donc juste utilisé pour des entretiens. Jetez-y un coup d'œil, et si vous pouvez donner un aperçu lors de l'entretien, vous êtes considéré comme un bon intervieweur sur cette question. Plus tard, j’ai progressivement commencé à l’utiliser même si le concept n’était pas très clair.
A quoi sert-il réellement ? Il sert principalement à réutiliser le code que nous avons écrit auparavant. Par exemple, si nous avons écrit une fonction, un objet ou utilisé quelque chose écrit par quelqu'un d'autre et que nous voulons ajouter quelque chose nous-mêmes, nous ne pouvons pas modifier les éléments de quelqu'un d'autre. Nous pouvons simplement en hériter et l'utiliser. utilisation réelle de l'héritage.
Comment implémenter l'héritage js :
Ne parlons pas d’abord du code, parlons d’abord de l’idée. En fait, l'héritage consiste à trouver des moyens de transférer les propriétés ou les méthodes d'autres objets (tout dans js est un objet) vers notre propre objet, afin que notre propre objet puisse être utilisé. Cela permet également d’atteindre l’objectif de réutilisation.
Maintenant que le but est clair, voici les moyens pour y parvenir.
Selon les caractéristiques de js, l'implémentation n'est rien de plus qu'une ou une combinaison des méthodes suivantes.
1. Constructeur, js ne semble pas avoir de définition stricte du constructeur, mais vous pouvez utiliser new pour créer de nouveaux objets. On dit que le constructeur est également une méthode pour implémenter l'héritage dans des langages strictement orientés objet, donc bien sûr js peut le simuler, donc les personnes qui ont appris le langage oop y penseront en premier.
2. Utilisez des prototypes de fonctions et des chaînes de prototypes pour relier deux objets. Parce que la chaîne de prototypes js est relativement unique, il est facile d'y penser.
Il existe plusieurs types de prototypes, c'est-à-dire ce qui est utilisé comme prototype de l'objet hérité, prototype de l'objet hérité ou instance de l'objet hérité, ou encore héritier direct. Les effets d'héritage obtenus par ces prototypes en tant qu'objets d'héritage sont différents.
3. Copiez les attributs et les méthodes. Copiez et clonez simplement tous les attributs ou méthodes de l'objet hérité et transformez-les en attributs et méthodes de nos propres objets. Bien entendu, cela peut être divisé en deux situations : la copie superficielle et la copie approfondie.
4. Utilisez les deux méthodes call et apply. Ces deux méthodes sont assez magiques et peuvent changer le contexte d'exécution de la fonction (this). Par conséquent, l'utilisation de ces deux méthodes peut également réaliser l'héritage et la réutilisation des méthodes héritées. objet. .
D'une manière générale, les moyens d'obtenir l'héritage dans js sont probablement les suivants. Les méthodes d'implémentation en constante évolution sont toutes combinées et mises à niveau en fonction de ces méthodes. Pourquoi la plupart d'entre elles sont-elles utilisées en combinaison ? une seule méthode. Pas idéal. Bien entendu, vous pouvez choisir la méthode à utiliser en fonction des besoins réels de votre projet, à condition qu'elle réponde à vos propres besoins et ne précise pas quelle méthode doit être utilisée pour y parvenir. C'est comme dire que le moyen le plus rapide pour aller de Pékin à Shijiazhuang est l'avion. Mais si c'est loin de l'aéroport, y compris pour se rendre à l'aéroport et aller de l'aéroport à la ville, ce n'est pas aussi rapide que le train à grande vitesse dans son ensemble, alors vous pouvez prendre le train à grande vitesse. Par exemple, si vous avez une voiture et pouvez conduire, mais si vous voulez relever un défi, vous pouvez faire du vélo. Vous pouvez choisir cela en fonction de votre situation réelle.
Implémentation du code, parlons des méthodes d'implémentation ci-dessus basées sur le code. Certaines d'entre elles sont extraites d'autres endroits et ajoutons quelques commentaires.
1. Implémentation du constructeur (constructeur emprunteur) :
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
Oups, j'ai trouvé que sub.say n'est pas défini, ce qui signifie qu'il n'a pas été hérité. L'affichage des deux objets sub et sub2 ci-dessous n'est pas égal, ce qui signifie que les deux fonctions pointent vers deux objets différents, ce qui signifie. ils sont copiés. Deux exemplaires sont sortis.
Donc, si cette méthode implémente l'héritage, les propriétés et méthodes de l'objet prototype ne seront pas héritées. Les propriétés et méthodes de Super seront copiées séparément pour chaque nouvel objet.
Par conséquent, il n’est pas approprié d’utiliser cette méthode seule pour implémenter l’héritage, car aucune des méthodes du prototype n’a été héritée. Alors les maîtres ont eu l'idée de l'héritage prototype
2. Héritage prototype :
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;
Cette fois, arr1 a été hérité, mais les paramètres n'ont pas été ajoutés et n'étaient pas définis. Par conséquent, lorsque la sous-classe de cette méthode a été déclarée, le paramètre transmis et la propriété héritée de la sous-classe n'ont pas pu être reçus. Tout le reste est assez normal. Montrer et dire sont tous deux hérités. Mais une chose à noter est que say est hérité via l'objet prototype de super, et show est un attribut de l'instance lors de la création d'une nouvelle instance de super objet.
Alors, comment implémenter la transmission des paramètres et hériter des éléments du prototype. Bien sûr, il suffit de combiner les deux méthodes ci-dessus, donc les seniors ont inventé la méthode suivante
3. Héritage combiné (emprunter le constructeur et définir le prototype) :
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;
Cette fois, c'est presque parfait, mais vous pouvez constater que sub.show et sub2.show ne sont pas égaux. Pourquoi est-ce parce que le lieu d'application fait que show devient le propre attribut de suber, alors mettez-le simplement dans le prototype de suber ? Le show (le show de Super est utilisé comme objet d'instance du sous-prototype) est écrasé, il est donc copié un par un. Bien sûr, il n'y a aucun moyen d'éviter cela. Afin de ne pas encourir cette surcharge inutile, davantage de ces fonctions partageables peuvent être placées dans des objets prototypes.
Étant donné que les appels lors de la construction de suber et les appels lors de l'attribution de valeurs à l'objet prototype suber provoquent l'appel de Super deux fois, alors Super est appelé deux fois à chaque fois qu'un nouvel objet suber est créé, et deux objets d'instance seront générés lorsque appelé deux fois. Les ressources excédentaires doivent être consommées.
Les seniors ont donc ouvert leur esprit pour résoudre ce problème et ont développé la méthode suivante.
4. Héritage des combinaisons parasites :
La principale différence entre cette méthode et la méthode 3 est que le prototype de la classe parent est affecté au prototype de la classe enfant au lieu de l'exemple de la classe parent
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;
好了,这样就把三方法的弊端干掉了,这个可以完美的使用了吧。
五、复制属性实现
拷贝我们可以写一个拷贝函数来实现。
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
复制成功,那么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
好了,深度复制完毕,但似有木有发现问题,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
这个就是借用了,好了,下课。
好吧,好吧,其实这里边还有其他东西要看。可以借用并不“带表”可以随便把某个方法赋值给谁然后跟没发生什么似的继续用。所以我们平时使用借用时要注意一下上下文,下面看下那些容易出错的地方。
//赋值给一个变量时候上下文会变化 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, "
神马意思呢,就是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"
通过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"
介绍完了,该说说自己的疑惑了,当复制属性方法遇到的被继承对象里边存在方法,如何单独复制出来呢,现在的是直接共用了,因为毕竟方法一般不会经常改动。求解答?
下面是转载过来的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) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? 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; };
以上这篇javascript 继承学习心得总结就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。