web前端开发必懂之一:JS继承和继承基础总结
首先,推荐一篇博客豪情的博客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(); //输出了true
subFn.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辅助方法中执行父类的construct
if (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'); // animal var 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"); // dog
dog.run("run"); // run
console.log(dog.constructor + "")
var Shepherd = Dog.extend({
statics: {//静态成员
TYPE: "Shepherd"
},
run: function() {//方法链,调用超类同名方法
this.parent.run.call(this, "fast");
}
});
console.log(Shepherd.TYPE);//Shepherd
var shepherd = new Shepherd("shepherd", 5);
shepherd.run();//fast
var 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,7
console.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 class
BaseClass.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 prototype
for (var name in props) {
// Check if we're overwriting an existing function
proto[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-class
this._super = _super[name]; // The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp; return ret;
};
})(name, props[name]) :
props[name];
} // The new constructor
var newClass = function () {
if (typeof this.init === "function") {
this.init.apply(this, arguments);
}
}; // Populate our constructed prototype object
newClass.prototype = proto; // Enforce the constructor to be what we expect
proto.constructor = newClass; // And make this class extendable
newClass.extend = BaseClass.extend; return newClass;
}; // export
global.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 minification
function isObject(o) { return typeof o === 'object'; }
function isFunction(f) { return typeof f === 'function'; } // a function that gets reused to make uninitialized objects
function 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 optimization
var extensions; // set the constructor property on the prototype, for convenience
proto.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 it
extensions = def;
}; //继承属性到超类;
// ...and extend it
if (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 it
return 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>
每一个人都要有一片属于自己的宁静天空, 在那里没有压力, 只有自己 , 和自己身体的对话;
最后提供一些参考的链接:
javascript 类属性、类方法、类实例、实例属性、实例方法、prototype、__proto__ 测试与小结:
http://www.cnblogs.com/mrsunny/archive/2011/05/09/2041185.html
JS的构造函数:
http://www.cnblogs.com/jikey/archive/2011/05/13/2045005.html
浅析Javascript原型继承:
http://blog.csdn.net/kittyjie/article/details/4380918
web前端开发必懂之一:JS继承和继承基础总结的更多相关文章
- 前端开发必须知道的JS(二) 闭包及应用
http://www.cnblogs.com/ljchow/archive/2010/07/06/1768749.html 在前端开发必须知道的JS(一) 原型和继承一文中说过下面写篇闭包,加之最近越 ...
- 前端开发必须知道的JS之闭包及应用
本文讲的是函数闭包,不涉及对象闭包(如用with实现).如果你觉得我说的有偏差,欢迎拍砖,欢迎指教. 在前端开发必须知道的JS之原型和继承一文中说过下面写篇闭包,加之最近越来越发现需要加强我的闭包应用 ...
- 【读书笔记】读《编写高质量代码—Web前端开发修炼之道》 - JavaScript原型继承与面向对象
JavaScript是基于原型的语言,通过new实例化出来的对象,其属性和行为来自于两部分,一部分来自于构造函数,另一部分是来自于原型.构造函数中定义的属性和行为的优先级比原型中定义的属性和优先级高, ...
- 前端开发必须知道的JS(一) 原型和继承
原型和闭包是Js语言的难点,此文主要讲原型及原型实现的继承,在(二)中会讲下闭包,希望对大家有所帮助.若有疑问或不正之处,欢迎提出指正和讨论. 一. 原型与构造函数 Js所有的函数都有一个protot ...
- 2019最新WEB前端开发小白必看的学习路线(附学习视频教程)
2019最新WEB前端开发小白必看的学习路线(附学习视频教程).web前端自学之路:史上最全web学习路线,HTML5是万维网的核心语言,标准通用标记语言下的一个应用超文本标记语言(HTML)的第五次 ...
- 25个Web前端开发工程师必看的国外大牛和酷站
逛了一周国外大牛们的博客与酷站,真是满满的钦佩.震撼.羡慕.惊喜………… Web设计是一个不断变化的领域,因此掌握最新的发展趋势及技术动向对设计师来说非常重要.无论是学习新技术,还是寻找免费资源与工具 ...
- 十分钟看懂,未来Web前端开发最新趋势
首先,展望未来趋势我们就要弄懂过去的一年,也就是18年,web前端开发的重要新闻.重要事件和JavaScript的各种流行框架.模式发展趋势. 我们来快速回顾一下. NPM热门前端框架下载 先来看最热 ...
- 4. web前端开发分享-css,js工具篇
web前端开发乃及其它的相关开发,推荐sublime text, webstorm(jetbrains公司系列产品)这两个的原因在于,有个技术叫emmet, http://docs.emmet.io, ...
- web前端开发分享-css,js工具篇
web前端开发乃及其它的相关开发,推荐sublime text, webstorm(jetbrains公司系列产品)这两个的原因在于,有个技术叫emmet, http://docs.emmet.io, ...
随机推荐
- OAuth2授权原理
最近在做第三方接入的,初步定下使用OAuth2协议,花了些时间对OAuth2的授权方式做了些了解. 我还记得一两年前,跟一位同事聊起互联网时,当时我说过一个想法: 目前不少较为稀有的资源,很多都是论坛 ...
- [No000009]学习重要还是经营人脉重要?
大说数人的朋友圈都是这样的:雪中送炭的寥寥无几,锦上添花的大多数人连你自己都不认识,碰到倒霉的时候还能遇到落进下石的.人脉是很重要,可相对自身的学习来说,就没有想象中那么重要了. 有一次在北大讲座,遇 ...
- 向数据库中插入一个DateTime类型的数据到一个Date类型的字段中,需要转换类型。TO_DATE('{0}','YYYY-MM-DD'))
需要指出的是,C#中有datetime类型,但是这个类型是包括小时,分钟,秒的.这个格式与数据库中的Date类型不符,如果将now设为datetime类型插入数据会失败. 需要通过TO_DATE('字 ...
- Nginx+keepalived双机热备(主从模式)
负载均衡技术对于一个网站尤其是大型网站的web服务器集群来说是至关重要的!做好负载均衡架构,可以实现故障转移和高可用环境,避免单点故障,保证网站健康持续运行.关于负载均衡介绍,可以参考:linux负载 ...
- Java核心技术点之注解
本博文是对Java中注解相关知识点的简单总结,若有叙述不清晰或是不准确的地方,希望大家可以指正,谢谢大家:) 一.什么是注解 我们大家都知道Java代码中使用注释是为了向以后阅读这份代码的人解释说明一 ...
- php中Jpgraph的运用
用Jpgraph,只要了解它的一些内置函数,可以轻松得画出折线图.柱形图.饼状图等图表. 首先要保证PHP打开了Gd2的扩展: 打开PHP.ini,定位到extension=php_gd2.dll,把 ...
- BZOJ 1024 【SCOI2009】 生日快乐
Description windy的生日到了,为了庆祝生日,他的朋友们帮他买了一个边长分别为 X 和 Y 的矩形蛋糕.现在包括windy,一共有 N 个人来分这块大蛋糕,要求每个人必须获得相同面积的蛋 ...
- performSelector的原理以及用法
一.performSelector调用和直接调用区别下面两段代码都在主线程中运行,我们在看别人代码时会发现有时会直接调用,有时会利用performSelector调用,今天看到有人在问这个问题,我便做 ...
- Hibernate大福利 下载链接
正在学马士兵Hibernate的同学来看这里,这里提供了他视频里需要的JAR包,请尽情下载,给好评喔. 一.Hibernate 3.3.2 核心JAR包 http://pan.baidu.com/s/ ...
- CodeSmith操作Access时字段的排序问题
最近在用CodeSmith操作写ACCESS数据库的代码模版,发现CodeSmith默认的字段顺序与ACCESS中表的字段顺序不一致. 首先在ACCESS数据库中建一个测试表Test,并添加ID.Na ...