JS继承和继承基础总结
转自:https://www.cnblogs.com/diligenceday/p/4246515.html
首先,推荐一篇博客豪情的博客JS提高: http://www.cnblogs.com/jikey/p/3604459.html ,里面的链接全是精华, 一般人我不告诉他;
我们会先从JS的基本的设计模式开始,由浅入深, 会描述prototype,__proto__,consturctor等基础知识和JS的常见继承方式, 以及四个类工厂的推荐和使用(包括JS.Class,prototype的类工厂,john resig写的一个简洁类工厂库,以及Pjs一个很飘逸的继承库,很飘逸-_-),最后有3个参考资料链接:,最后有我个人的视频,欢迎拍砖哇, ///(o)_(o)。
工厂模式:因为使用用一个接口创建很多对象会产生大量的重复代码,为了解决这个问题,人们就开始使用工厂模式:
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>//工厂模式:因为使用用一个接口创建很多对象会产生大量的重复代码,为了解决这个问题,人们就开始使用工厂模式:function Person(hairs,face, eye) {var o = new Object();o.hairs = hairs;o.face = face;o.eye = eye;o.say = function(){console.log("say someting to me!");};return o;};//我们通过 Person(0,1,2)就可以创建一个包含特定信息的Person对象, 以后要生成对象直接执行Person然后给他传参数就好了;//比如我们要生成10个Person, 很方便很快;var c = 10;while( c-- ) {console.log(Person(c,c,c))};</script> </body> </html>
构造函数模式:使用构造函数模式我们能少些更多代码,比如上面的工厂模式我们可以改写成:
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>function Person(hairs, face, eye) {this.hairs = hairs;this.face = face;this.eye = eye;};//同样, 我们再生成10个小朋友var c = 10;while( c-- ) {console.log( new Person(c,c,c) );};</script> </body> </html>
//知识点1: 那么工厂模式和构造函数模式有什么区别呢:
/*
* 没有显式地创建对象
* 直接把属性和方法赋值给了this
* 没有return语句
* 构造函数的前面有一个new;
* */
// 知识点2:
/*
* 通过new的构造函数会经过四个阶段:
* 1:创建一个新对象;
* 2:把this赋值给这个新对象
* 3:执行构造函数中的代码
* 4:返回新对象
* */
原型是神马? 原型就是公用的方法或者属性,这么理解最简单, 当然:
1、prototype本质上还是一个JavaScript对象;
2、每个函数都有一个默认的prototype属性;
3、通过prototype我们可以扩展Javascript的内建对象
一些代码弄懂原型的本质:
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>function Memo() {};Memo.prototype.hehe = 1;Memo.prototype.shut = function() {console.log("miaomiao")};var m0 = new Memo();var m1 = new Memo();console.log(m0.shut === m1.shut); //ture, 因为m0的shut和m1的shut就是指向了Memo.prototype.shut这个方法,所以他们相等;//Object.getPrototypeOf会返回实例的原型;console.log(Object.getPrototypeOf(m0)) //输出了:Memo {hehe: 1, shut: function}console.log( Memo.prototype.isPrototypeOf(m0) )// 输出: true;//原型对象有一个很大的问题要非常注意 ==》》 原型的属性和方法是被共享的, 比如:Memo.prototype.shut = "W_W";l(m0.shut) //输出:W_W, 悲剧了吧, 本来原型上的shut是个方法, 被改成字符串以后, 实例上的shut也发生了改变;l(m1.shut) //输出:W_W;m0.__proto__.shut = 1111; //m0的__proto__指向了Memo.prototype.shut,__proto__在标准浏览器下面是不能枚举到的,但确实是存在的一个属性;l(m1.shut) //输出了1111//只要原型上的属性或者方法被改了, 实例上的也会发生改变;</script></body> </html>
知识点:Object.getPrototypeOf;
Fn.prototype.isPrototypeOf( instance );
好的, 现在再来了解一下constructor, 如果你已经知道constructor是什么的话, 这段略过, constructor是默认指向创建当前对象的构造函数, 但是这里面有一些坑要注意, 比如你的原型prototype被改了, 实例的constructor就变了 ,
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>var Fn = function(){//我是构造函数Fn;};l("Fn的constructor");l(Fn.prototype.constructor);/*输出function (){//我是构造函数Fn;} */var f = new Fn;l("f的constructor");l(f.constructor);/*输出;* function (){//我是构造函数Fn;}*///当函数创建的时候默认就为prototype新建一个constructor指向自己;l(Fn.constructor === Function); //输出true, Function这个构造函数是Function;l(Fn.constructor) // 输出function Function() { [native code] } ,意思是Function这个构造函数 ;l(Fn.constructor === Fn.__proto__.constructor) //输出true; Fn的constructor实际上是指向Fn.__proto__的;//好的, 现在重新定义一个Fn;var Fn = function(){//我是构造函数Fn;};Fn.prototype = {};l(Fn.prototype.constructor)/*打印出了内部的代码, 为什么呢? 因为我们改变了Fn的原型, Fn的constructor指向了{}空对象的Contructor;* function Object() { [native code] } */</script> </body> </html>
大家一定要懂的是实例上的__proto__就是指向原型上的prototype, 这样会少走一些弯路,可以节约更多的时间用来看片, 你懂的;
每一个函数新建的时候都有一个默认的prototype, prototype这个对象上面默认有一个指向自己的constructor;
prototype和__proto__的区别:__proto__是实例和Person.prototype之间的关系,而constructor是实例和Person之间的关系
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>var Fn = function() {};Fn.prototype.hehe = 1;Fn.prototype.lala = 2;var f = new Fn;l(f) //输出了Fn {hehe: 1, lala: 2} ;l(f.__proto__) //输出了Fn {hehe: 1, lala: 2} ;l(Fn.prototype === f.__proto__) //输出了true, 这里要懂的东西是实例上的__proto__这个属性指向了构造函数的prototype;l(f === f.__proto__) //输出false; 这里要懂的是f是new出来的实例;//因为f上面的hehe和lala都是继承的属性, 所以这里面的log并没有被输出;for(var p in f){l("输出所以属性:" + p); //输出所以属性:hehe demo.html:11; 输出所以属性:lala//过滤非私有属性;if( f.hasOwnProperty(p) )l("输出私有属性" + p); //因为f没有私有属性,所以这边没有log出来;};</script> </body> </html>
有了上面的基础, 我们开始说说JS的面向对象和继承吧;
1:组合使用构造器和原型模式, 这种模式是构造函数和原型混合的模式, 使用最广泛, 认同度也最高的一种模式, 也是最基础的模式;
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>function Duck( name ,word) {this.name = name;this.word = word;};Duck.prototype.say = function() {console.log( this.name+" say : " + this.word )};var duck = new Duck("nono","hehe");duck.say();</script> </body> </html>
寄生构造模式; 听名字真的很玄乎..其实跟工厂模式一模一样的, 其实就是自定义模型的封装;
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>function Foxy(name , word) {var result = new Object();result.name = name;result.word = word;return result;};l( new Foxy("nono","say someting") ); //输出:Object {name: "nono", word: "say someting"} ;function Monkey( ) {var aResult = [];//技巧:通过apply把arguments保存到数组;aResult.push.apply(aResult, arguments);return aResult;};l( new Monkey("nono","dino","kite","sam") ); //打印出了这个:["nono", "dino", "kite", "sam"];//要注意的是使用寄生模式的返回的对象和构造函数一点关系都没有;</script> </body> </html>
JS的原型继承, 继承是依赖于原型链的;那么JS原型链是什么呢:
/* 这段话慢慢读, 从搞基程序设计三抄过来的,很重要, 实体书最好自己看一看哇;
* ECMAScript中描述了原型链的概念, 并将原型链作为实现继承的主要方法, 基本思想是利用引用类型继承另一个引用类型的属性和方法。
* 简单回顾一下构造函数,原型和实例的关系:每一个函数都有一个原型对象, 每一个原型对象都有一个指向构造函数的指针,
* 而实例包含了一个指向原型对象的内部(不可见的)指针。 那么我们让原型对象等于另一个类型的实例, 那么这个原型对象将会包含指向
* 另一个原型对象的指针,如果另一个原型对象又是指向了别的原型的一个实例, 这样层层嵌套, 就形成了原型链;
* */
那么我们利用原型的原型链相互继承来写一个基本的例子:
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>var Fn = function() {this.property = true;}Fn.prototype.getFnProperty = function() {console.log( this.property );};var SubFn = function() {this.subProperty = false;};//SubFn继承了Fn的实例SubFn.prototype = new Fn();//为实例添加额外的实例方法;SubFn.prototype.getSubProperty = function(){console.log(this.subProperty);};var subFn = new SubFn();subFn.getFnProperty(); //输出了truesubFn.getSubProperty(); //输出了false/*现在subFn的constructor 是function () {this.property = true;};所以要修正SubFn.prototype.constructor = SubFn*/</script> </body> </html>
原型式继承第二个例子:
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>// 首先, 准备一个方法;var inherit = function(o) {if(!typeof o === "object")return;function F () {}F.prototype = o;F.prototype.constructor = F;return new F();};var Fn = function() {this.property = true;}Fn.prototype.getFnProperty = function() {console.log( this.property );};var SubFn = function() {this.subProperty = false;};//SubFn继承了Fn的实例SubFn.prototype = new Fn();//为实例添加额外的实例方法;SubFn.prototype.getSubProperty = function(){console.log(this.subProperty);};var subFn = new SubFn();//这个方法的内部, 临时创建了一个构造函数, 然后将传入的对象作为这个构造函数的原型, 最后返回一个临时的新实例;//ECMASscript 5 有新增了一个Object.create 效果和inherit一模一样, 它可以接收第二个参数,// 第二个参数要通过defineProperties的方式设置,会覆盖原型的属性, 比如:Object.create(subFn, {getFnProperty: {value:1}});var Fn = function() {};//如果我们inherit传对象;Fn.prototype = inherit( {0:0,1:1,2:2,3:3,4:4} );l( new Fn ) //==>Fn {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, constructor: function(){....} 继承了哦//如果我们给inherit传一个构造函数的实例;Fn.prototype = inherit( new SubFn );l(new Fn);</script> </body> </html>
有时候看到原型的各种引用会尿失禁, 引来引去的,坑爹啊, 不说了去洗裤子了....
寄生组合式继承
组合继承是JS的常用继承模式, 但是也有自己的不足, 组合继承最大的问题的无论是什么情况下, 都会两次调用超类的构造函数;
一个是在创建子类原型的时候, 另一个是在子类构造函数的内部, 那么子类的原型会包含所有超类实例的全部属性,
寄生组合式继承就是为了解决子类原型包含所有超类实例全部属性这个问题而存在的;
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>//最好所有的代码自己打一遍, 增加印象;function l ( arg ) {console.log( arg );};</script><script>var inherit = function(o) {if(!typeof o === "object")return;function F () {}F.prototype = o;F.prototype.constructor = F;return new F();};//首先要准备inheritPrototype方法;var util = util || {};util.inherit = inherit;util.inheritPrototype = function(subType, superType) {var _prototype = this.inherit( superType.prototype );_prototype.constructor = subType;subType.prototype = _prototype;};function F( name ) {this.name = name;this.type = "human";this.habits = ["dance","code"];};F.prototype.laugh = function() {console.log("heha!");};var InheritF = function() {F.apply( this, arguments );};util.inheritPrototype(InheritF, F);InheritF.prototype.letsGo = function() {l("1,2,3,4")};var nono = new InheritF("nono");nono.habits.push("read books");l(nono.habits)var nonono = new InheritF("nono");l( nonono.habits );//继承的方法千万种,万变不离其宗;</script> </body> </html>
JS各种使用了继承库的类库推荐, 可以加深印象:
首先, JS.Class 是一个mootools式的类工厂 基于 lunereaper<![[dawid.kraczkowski[at]gmail[dot]com]]>的项目进行修改, 让子类的实现更简洁;
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>JS = {};// 这个是库的源代码;JS.Class = function(classDefinition) {//返回目标类的真正构造器function getClassBase() {return function() {//它在里面执行用户传入的构造器construct//preventJSBaseConstructorCall是为了防止在createClassDefinition辅助方法中执行父类的constructif (typeof this['construct'] === 'function' && preventJSBaseConstructorCall === false) {this.construct.apply(this, arguments);}};}//为目标类添加类成员与原型成员function createClassDefinition(classDefinition) {//此对象用于保存父类的同名方法var parent = this.prototype["parent"] || (this.prototype["parent"] = {});for (var prop in classDefinition) {if (prop === 'statics') {for (var sprop in classDefinition.statics) {this[sprop] = classDefinition.statics[sprop];}} else {//为目标类添加原型成员,如果是函数,那么检测它还没有同名的超类方法,如果有if (typeof this.prototype[prop] === 'function') {var parentMethod = this.prototype[prop];parent[prop] = parentMethod;}this.prototype[prop] = classDefinition[prop];}}}//其实就是这样的Base = function() {};别想太多了;var preventJSBaseConstructorCall = true;var Base = getClassBase();preventJSBaseConstructorCall = false;createClassDefinition.call(Base, classDefinition);//用于创建当前类的子类Base.extend = function(classDefinition) {//其实就是这样的Base = function() {};别想太多了;preventJSBaseConstructorCall = true;var SonClass = getClassBase();SonClass.prototype = new this();//将一个父类的实例当作子类的原型preventJSBaseConstructorCall = false;createClassDefinition.call(SonClass, classDefinition);SonClass.extend = this.extend;return SonClass;};return Base;};</script><script>//这是实际案例;var Animal = JS.Class({construct: function(name) {this.name = name;},shout: function(s) {console.log(s);}});var animal = new Animal();animal.shout('animal'); // animalvar Dog = Animal.extend({construct: function(name, age) {//调用父类构造器this.parent.construct.apply(this, arguments);this.age = age;},run: function(s) {console.log(s);}});var dog = new Dog("dog", 4);console.log(dog.name);dog.shout("dog"); // dogdog.run("run"); // runconsole.log(dog.constructor + "")var Shepherd = Dog.extend({statics: {//静态成员TYPE: "Shepherd"},run: function() {//方法链,调用超类同名方法this.parent.run.call(this, "fast");}});console.log(Shepherd.TYPE);//Shepherdvar shepherd = new Shepherd("shepherd", 5);shepherd.run();//fastvar a = new Animal("xx");console.log(a.run);</script></body> </html>
prototype这个( ▼-▼ )库以前牛逼着呢, 但是一百年河东一百年河西, prototype当前的版本是最新版本的1.7稳定版,prototype里面的类工厂创建的主代码也被我单独裁出来了, 源码也差不多, 源码有我加的注释,自己个人方便阅读, HTML代码的最后自己写的几种 创建类工厂的方式, 可以借鉴(我没看api, 源码就是api....)
运行下面代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>prototype Demo</title> </head> <body> <script>var Class = (function() {var IS_DONTENUM_BUGGY = (function(){for (var p in { toString: 1 }) {if (p === 'toString') return false;}return true;})();Object.keys = Object.keys || (function() {if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }var results = [];for (var property in object) {if (object.hasOwnProperty(property)) {results.push(property);}}return results;})();Object.isFunction = function(fn) {return typeof fn === "function";};function argumentNames() {var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1].replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '').replace(/\s+/g, '').split(',');return names.length == 1 && !names[0] ? [] : names;};function $A(iterable) {if (!iterable) return [];if ('toArray' in Object(iterable)) return iterable.toArray();var length = iterable.length || 0, results = new Array(length);while (length--) results[length] = iterable[length];return results;};function update(array, args) {var arrayLength = array.length, length = args.length;while (length--) array[arrayLength + length] = args[length];return array;};//wrapper表示的是现在的方法;function wrap(wrapper) {//this指向的返回的闭包;var __method = this;return function() {//a是指把orginalFu和当前的参数混合成一个数组;var a = update([__method.bind(this)], arguments);return wrapper.apply(this, a);};};Object.extend = extend;Function.prototype.argumentNames = argumentNames;Function.prototype.wrap = wrap;//把第二个参数的属性继承到第一个参数上;function extend(destination, source) {for (var property in source)destination[property] = source[property];return destination;};function subclass() {};function create() {//相当于对参数进行slice;var parent = null, properties = $A(arguments);//如果第一个是函数就把这个函数作为超类;if (Object.isFunction(properties[0]))parent = properties.shift();//这个是返回的构造函数;function klass() {this.initialize.apply(this, arguments);};//为klass添加addMethods方法;Object.extend(klass, Class.Methods);//保存超类;klass.superclass = parent;klass.subclasses = [];if (parent) {subclass.prototype = parent.prototype;klass.prototype = new subclass;//把当前的类保存到父级的超类, 有什么意义呢;parent.subclasses.push(klass);};for (var i = 0, length = properties.length; i < length; i++)klass.addMethods(properties[i]);//避免没有initialize而报错;if (!klass.prototype.initialize)klass.prototype.initialize = function() {};//这个避免constructor被properties覆盖了, 重新赋值;klass.prototype.constructor = klass;return klass;}function addMethods(source) {var ancestor = this.superclass && this.superclass.prototype,properties = Object.keys(source);//IE8以下可以遍历到toString和valueOf;if (IS_DONTENUM_BUGGY) {if (source.toString != Object.prototype.toString)properties.push("toString");if (source.valueOf != Object.prototype.valueOf)properties.push("valueOf");}for (var i = 0, length = properties.length; i < length; i++) {var property = properties[i], value = source[property];//没有父级就没必要走下去了 , 如果有父级, 而且value不是function那么就没必要走了;//如果你传:Class.create(Fn, {0:function($super){alert(1)},1:1,2:2,3:3,4:4})//那么$super这个方法是指向ancestor的闭包, 只要执行$super那么超类的同名方法会被执行;if (ancestor && Object.isFunction(value) &&value.argumentNames()[0] == "$super") {var method = value;//返回一个闭包;value = (function(m) {return function() { return ancestor[m].apply(this, arguments); };})(property).wrap(method);/*如果不用简写的话,写成这样子也是ok的,他那么写, 一下子高端起来了,我C, 就是两个闭包哇;*var closureFn = (function(m) {return function() { return ancestor[m].apply(this, arguments); };})(property);value = function() {var args = Array.prototype.slice.call(arguments);args.unshift( closureFn );//是谁执行了value,当前的this就是谁,刚好是实例对象;method.apply(this, args)};* *///修正这个闭包的valueOf为method这个方法....value.valueOf = method.valueOf.bind(method);value.toString = method.toString.bind(method);};//Class.create({0:0,1:1,2:2,3:3,4:4})会走这边;this.prototype[property] = value;};return this;};return {create: create,Methods: {addMethods: addMethods}};})(); </script> <script>//__________________________________________//创建一个类工厂var Fn = Class.create({method0 : function() {console.log("method0");console.log(arguments);},method1 : function() {console.log("method1");console.log(arguments);}});//实例化该类工厂;var fn = new Fn();fn.method0(); //输出 : method0 []fn.method1(0,1,2,3,4); //输出 : method1 , [0, 1, 2, 3, 4];//SubFn , 继承了Fn;var SubFn = Class.create(Fn, {method2 : function() {console.log(2)}});(new SubFn).method2() //输出 :2//__________________________________________//__________________________________________//子类调用超类的同名方法的使用;//子类;var Fn = Class.create({0:function(){console.log(arguments);return "hehe";},1:1,2:2,3:3,4:4});//超类;//Fn0继承了Fn;var Fn0 = Class.create(Fn, {0:function($super,arg0,arg1,arg2,arg3){console.log( $super( arguments ) );console.log(arg0+" "+arg1+" "+arg2+ " " +arg3);}});//实例化Fn0,执行0的方法传进去几个参数, 都会传到Fn的0方法里面, Fn0如果叫做$super的参数, $super代表超类的同名方法;(new Fn0)[0](10,9,8,7); //输出 三个字段,包含超类的的arguments和超类的返回,以及子类的输出: arguments5 , hehe , 10,9,8,7console.log(new Fn0); //输出一个实例;//__________________________________________//为Fn0的原型新加一个“nono”方法;Fn0.addMethods({"nono" : function() {console.log("you are super man");}});//实例化原型执行nono方法:var instance = new Fn0;instance.nono() //输出了:you are super man;//__________________________________________//我们现在的类方法和属性都是原型上继承下来的,只要超类的方法或者属性发生了改变, 那么子类的方法也发生了改变;//比如Fn0.prototype.nono = "someting wrong!!";instance.nono // 输出了== >"someting wrong!!" ...卧槽,这样可不行啊;//我们需要创建私有属性var FnBar = Class.create(Fn,{initialize : function() {this.hehe = "haha",this.say = function( words ) {console.log( words ||this.hehe );};}});var instanceBar = new FnBar();var FnFoo = Class.create(FnBar,{initialize : function() {this.say = function() {console.log("fuck! you duck!!!");};}});//prototype是基于原型继承原型的库, 而不是基于原型继承实例, 所以用起来也挺方便的;(new FnFoo).say(); </script> </body> </html>
这款继承创建的作者是jQ的作者,你懂的, 不绕;
https://github.com/html5crew/simple-inheritance 《《== 这个是代码的源地址:
运行下面代码
<!DOCTYPE html> <html> <head><title></title><meta charset="utf-8"> </head> <body><script>/* source: https://gist.github.com/shakyShane/5944153** Simple JavaScript Inheritance for ES 5.1 ( includes polyfill for IE < 9 )* based on http://ejohn.org/blog/simple-javascript-inheritance/* (inspired by base2 and Prototype)* MIT Licensed.*/(function (global) {"use strict";if (!Object.create) {Object.create = (function () {function F() {}return function (o) {if (arguments.length !== 1) {throw new Error("Object.create implementation only accepts one parameter.");}F.prototype = o;return new F();};})();}var fnTest = /xyz/.test(function () {/* jshint ignore:start */xyz;/* jshint ignore:end */}) ? /\b_super\b/ : /.*/;// The base Class implementation (does nothing)function BaseClass() {}// Create a new Class that inherits from this classBaseClass.extend = function (props) {var _super = this.prototype;// Instantiate a base class (but only create the instance,// don't run the init constructor)var proto = Object.create(_super);// Copy the properties over onto the new prototypefor (var name in props) {// Check if we're overwriting an existing functionproto[name] = typeof props[name] === "function" &&typeof _super[name] === "function" && fnTest.test(props[name]) ?(function (name, fn) {return function () {var tmp = this._super;// Add a new ._super() method that is the same method// but on the super-classthis._super = _super[name];// The method only need to be bound temporarily, so we// remove it when we're done executingvar ret = fn.apply(this, arguments);this._super = tmp;return ret;};})(name, props[name]) :props[name];}// The new constructorvar newClass = function () {if (typeof this.init === "function") {this.init.apply(this, arguments);}};// Populate our constructed prototype objectnewClass.prototype = proto;// Enforce the constructor to be what we expectproto.constructor = newClass;// And make this class extendablenewClass.extend = BaseClass.extend;return newClass;};// exportglobal.Class = BaseClass;})(this);</script><script>var Fn = function() {};//对继承的插件进行引用;Fn.extend = window.Class.extend;Fn.prototype.lala = 1;Fn.prototype.say = function() {alert( this.lala );};var Foo = Fn.extend({dudu:2,init:function(){this.name="nono"},dayDudu:function(){alert(this.dudu);}})</script></body> </html>
执行 new Foo打印出来对象结构如下:
pjs这个类工厂的github地址是:git://github.com/jayferd/pjs ,挺有名的js类工厂库, 写了源码分析, 关于代码我就不吐槽了,你看了就懂了, 不过真心挺方便的;
运行下面代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>无标题文档</title> </head> <body> <script> var P = (function(prototype, ownProperty, undefined) {// helper functions that also help minificationfunction isObject(o) { return typeof o === 'object'; }function isFunction(f) { return typeof f === 'function'; }// a function that gets reused to make uninitialized objectsfunction BareConstructor() {}function P(_superclass /* = Object */, definition) {// handle the case where no superclass is given//如果没有超类,那么超类就设置为Object,definition为对象或者是fn都行//是对象的话会把对象的所有属性和方法复制到返回的类原型,//是函数会传给函数有关返回类的原型(引用)等参数;if (definition === undefined) {definition = _superclass;_superclass = Object;};// C is the class to be returned.//// It delegates to instantiating an instance of `Bare`, so that it// will always return a new instance regardless of the calling// context.//// TODO: the Chrome inspector shows all created objects as `C`// rather than `Object`. Setting the .name property seems to// have no effect. Is there a way to override this behavior?//无论你是使用 new还是直接运行都会返回C的实例;function C() {var self = new Bare;//如果创建的实例有init会执行init方法, init这个函数里面放的是私有的属性;if (isFunction(self.init)) self.init.apply(self, arguments);return self;}// C.Bare is a class with a noop constructor. Its prototype is the// same as C, so that instances of C.Bare are also instances of C.// New objects can be allocated without initialization by calling// `new MyClass.Bare`.function Bare() {}C.Bare = Bare;// Set up the prototype of the new class.//指定超类的原型 到BareConstructor, 公用一个函数到BareConstructor,专门用来实例化超类;var _super = BareConstructor[prototype] = _superclass[prototype];//proto为Bare原型的引用;//实例化的超类的实例指向了C和Bare的Prototype,主要是Bare的prototype,因为类工厂返回的实例就是Bare的实例;var proto = Bare[prototype] = C[prototype] = new BareConstructor;// other variables, as a minifier optimizationvar extensions;// set the constructor property on the prototype, for convenienceproto.constructor = C;//这个超类的mixin会重新调用P返回原型重新赋值给Bare和C,返回C, 方便无new实例化;C.mixin = function(def) {Bare[prototype] = C[prototype] = P(C, def)[prototype];return C;};//C.open 这个方法可以用来为这个类添加原型return (C.open = function(def) {//重新定义extensions为一个空对象, C.open打开或者第一次打开的时候重新定义, 写成var extensions = {}; 也行,个人感觉没有问题;extensions = {};if (isFunction(def)) {// call the defining function with all the arguments you need// extensions captures the return value.//proto指向了C和Bare的原型,要添加原型就往这个对象extend方法即可;//也可以为这个函数返回对象作为原型的属性;extensions = def.call(C, proto, _super, C, _superclass);}else if (isObject(def)) {// if you passed an object instead, we'll take itextensions = def;};//继承属性到超类;// ...and extend itif (isObject(extensions)) {for (var ext in extensions) {if (ownProperty.call(extensions, ext)) {proto[ext] = extensions[ext];};};};//没有init就把init设置为超类;// if there's no init, we assume we're inheriting a non-pjs class, so// we default to applying the superclass's constructor.if (!isFunction(proto.init)) {proto.init = _superclass;}return C;})(definition);}// ship itreturn P;// as a minifier optimization, we've closured in a few helper functions// and the string 'prototype' (C[p] is much shorter than C.prototype) })('prototype', ({}).hasOwnProperty); </script><script> //直接创建一个类 var Fn = P({0:0,1:1 }); console.log( new Fn() ); //输出: Bare {0: 0, 1: 1, constructor: function, init: function} console.log( (new Fn()).init() ) //如果没有init, 那么init出来默认为 Object {}//让FnBar继承Fn var FnBar = P(Fn,{2:2,3:3,4:4 }); console.log( new FnBar ); //输出: Bare {2: 2, 3: 3, 4: 4, constructor: function, 0: 0, 1: 1, init: function}//为FnFoo添加额外的方法和属性; var FnFoo = FnBar.open({5:5,6:6 }); console.log( new FnFoo ); //输出: Bare {2: 2, 3: 3, 4: 4, 5: 5, 6: 6, constructor: function, 0: 0, 1: 1, init: function}//各种各样的继承方式 你都可以用 var FnFoo0 = FnFoo.mixin({7:7,8:8 }); console.log( new FnFoo0); //输出: Bare {7: 7, 8: 8, constructor: function, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 0: 0, 1: 1, init: function}//____________________________________-_-_______________ //直接创建一个类 var Fn = P({0:0,1:1,init : function() {this.name = "nono";console.log(this);} }); (new Fn).init(); // 输出: Bare {name: "nono", 0: 0, 1: 1, constructor: function, init: function};//通过C.open的方式为Fn添加超类 var FnBar = Fn.open(function( proto, _super, C, _superclass ) {proto.style = "gangnanStyle";proto.job = "superWebDevoloper";proto.say = function() {console.log( this.job + " __ " + this.job);}; }); (new FnBar).say(); //输出: superWebDevoloper __ superWebDevoloper//通过传入function的方式为构造函数添加超类; // 该方法不但继承了FnBar, 而且在第二个Fn的参数中为构造函数添加了sayWhat方法, 该函数的返回也会作为原型被添加到类原型上。。。。; var FF = P(FnBar, function( proto, _super, C, _superclass ) {proto.sayWhat = function() {console.log( "funky world!" );};return {sayHi : function() {console.log( "monkey world!" );}}; }); console.log( new FF );//输出: Bare {name: "nono", constructor: function, sayWhat: function, 0: 0, 1: 1, init: function, style: "gangnanStyle"…}; (new FF).sayWhat(); //输出: funky world! (new FF).sayHi(); //输出:monkey world! </script> </body> </html>
转载于:https://www.cnblogs.com/chinet/p/9285552.html
JS继承和继承基础总结相关推荐
- “约见”面试官系列之常见面试题第三十八篇之js常见的继承方式(建议收藏)
1.原型链继承 核心: 将父类的实例作为子类的原型 将构造函数的原型设置为另一个构造函数的实例对象,这样就可以继承另一个原型对象的所有属性和方法,可以继续往上,最终形成原型链 父类 // 定义一个动物 ...
- 封装 继承 多态_Java基础知识——封装、继承、多态
1 基本概括 2 详解 封装: 是将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问 封装的优点:只能通过规定的方式来访问数据:隐藏类的实现细节: ...
- 详解js中的继承(一)
详解js中的继承(一) 前言 准备知识 1.构造函数,实例 2.原型对象 3.构造函数,原型对象和实例的关系 继承 原型链 小结 前言 通过[某种方式]让一个对象可以访问到另一个对象中的属性和方法,我 ...
- js面试题继承的方法及优缺点解答
这篇文章主要为大家介绍了js面试题继承的方法及优缺点解答,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪 目录 说一说js继承的方法和优缺点? 一.原型链继承 二.借用构造 ...
- 关于js原型链继承机制
原型链继承是js的默认继承机制 当我们实例化一个自定义对象时,其实继承已经发生了 eg. //定义一个函数对象 function FF(){} //实例化一个对象 let a=new FF() 这里发 ...
- 浅谈js的常用继承封装和多态
好久没有写博客了,以前对js的继承都很模糊,最近重新整理了些资料重新温习了一下! 首先什么是构造函数?什么是原型?什么是实例化对像?,继承和实例化对象有什么区别?继承方式有哪些?各自有什么区别和优缺点 ...
- JS 高级(继承、函数)
JS 高级 继承 构造函数继承 <script>// 构造函数继承// 定义的Person类function Person(name, age) {this.name = name;thi ...
- 【前端知识之JS】JS如何实现继承
前言 本系列主要整理前端面试中需要掌握的知识点.本节介绍JS如何实现继承. 文章目录 前言 一.什么是继承 二.实现方式 1. 原型链继承 2. 构造函数继承 3. 组合继承 4. 原型式继承 5. ...
- 【前端面试之JS】js如何实现继承
继承(inheritance)是面向对象软件技术当中的一个概念. 如果一个类别B"继承自"另一个类别A,就把这个B称为"A的子类",而把A称为"B的父 ...
最新文章
- iOS开发UI篇—UIWindow简单介绍
- 我们来谈谈那些智能家居里的语音对话设备
- SAP Commerce Extension的Web应用启动问题
- socket 编程 基于 select 实现的回射客户端/服务程序
- 剑指offer(21)栈的压入、弹出序列
- mvc html.hidden,ASP.Net MVC Html.HiddenFor有错误的值
- python如何避免访问对象不存在的属性_Python3基础 setattr 设置对象的属性值,如果属性不存在就创建一个...
- 实现四台服务器的负载均衡
- 华表Cell应用 - 使用XML自动读入数据 | #报表 #华表Cell
- Dreamweaver 2020 安装教程
- 使用Robot Framework实现多平台自动化测试
- Ubuntu 常用小命令(持续更新~)
- python证件照_python opencv实现证件照换底的方法
- 如何做快手副业?怎么在快手上赚工资?快手发视频怎么赚钱?
- 处理win10系统自动休眠bug
- RabbitMQ六种工作模式
- Atari 2600 新书:主机游戏的一次黎明冒险
- Vue3计算属性和异步计算属性
- Obsidian利用插件Remotely-save实现超低成本全平台云笔记
- Spring Security OAuth2 单点登录和登出