JavaScript学习笔记-实例详解-类(一)

实例详解-类(一):
//每个javascript函数(除了bind())都自动拥有一个prototype对象
// 在未添加属性或重写prototype对象之前,它只包含唯一一个不可枚举属性constructor
// constructor的值是一个函数对象,指代函数对象自身(构造函数)
//原型对象是类的'唯一标识',而构造函数是类的'公共标识',因此这个constructor属性为对象提供了类
function Range(from,to){
this.from = from||0;
this.to = to||0;
}
console.log(Range.prototype.constructor); //[Function: Range] //未重写原型之前,它的constructor指代它自身
//==第一种方式:重写Range的原型对象prototype,若没有指定constructor属性,那么类将采用原型对象的默认原型Object类的constructor
Range.prototype = {
//====constructor:Range; //可以显式的给原型添加一个constructor构造函数,这里反向引用自身的构造函数
includes: function(x){return this.from<=x && x<=this.to;},
foreach: function(f){
for(var x = Math.ceil(this.from);x<=this.to;x++) f(x);
},
toString: function(){return '('+this.from+'...'+to+')';}
};
/*==第二种方式:除了显式的添加constructor,还可以使用预定义原型对象,它包含属性consturctor,然后逐个的给原型添加自定义的属性:
Range.prototype.includes = function(){...};
Range.prototype.foreach = function(){...};
Range.prototype.toString = functon(){...};
*/
var r = new Range(2,4);
console.log(r.constructor); //[Function: Object]
console.log(r.constructor === Object); //true

//====这里的类F1和上面的类Range比较,
var F1 = function(){};
var p = {constructor:F1,a:1,b:2,c:function(){}}; //显式指定对象p的构造函数constructor为F1,若未指定则p.constructor===Object;
F1.prototype = p;
var p2 = new F1();
console.log(p2.constructor === p.constructor); //true 对象p2是类F1的实例,类F1重写了它的原型为对象p,所以对象p2继承了对象p的constructor;
console.log(p2.constructor === F1); //true
//====
var F2 = function(){};
//F2.sum = function(x,y){return x+y;}; //无效,不能直接为方法添加属性,只能通过它的原型对象为它添加属性
F2.prototype.sum = function(x,y){return x+y;};
var o = new F2();
console.log(o.constructor === F2); //true 因为F2使用自身的默认原型,它的constructor属性(构造函数)就是指代F2自身;
console.log(o.sum(1,2)); //3
//==========创建一个定义类的函数defineClass(函数式编程风格)=========================
function extend(o,p){ //定义一个复制对象属性的函数
for(var x in p){
o[x] = p[x];
}
return o;
}
function defineClass(constructor,methods,statics){ //该函数接收3个对象参数
if(methods) extend(constructor.prototype,methods); //添加所有实例属性
if(statics) extend(constructor,statics); //添加所有类属性
return constructor; //最后要返回构造函数
}
var Range1 = defineClass(
function(f,t){this.f=f;this.t=t;},
{
includes:function(x){return this.f<=x&&x<=this.t;},
toString:function(){return this.f+'...'+this.t;}
},
{
upto:function(t){return new Range1(0,t);}
}
);
var r3 = new Range1(5,7);
r3.includes(6);
console.log(r3.toString());
//==普通方法创建一个类:(调用时首先查找实例中是否直接定义了这个属性,有则返回实例属性;如果实例属性中没有就去构造函数中查找,有则返回;如果前面两者都没有就去原型对象中查找,如果没有则返回undefined)
//1.构造函数,定义对象属性
var Range2 = function(x,y){
if(isNaN(x)||isNaN(y)) throw new TypeError('not a number');
var self = this;
this.r = x;
this.i = y;
};
//2.原型属性(实例共享的属性、方法,通过实例对象来调用),也可将原型属性写进构造函数内动态的添加,但没创建一个实例就会执行一次,需要加判断是否已经创建;
Range2.prototype.add = function(that){return new Range2(this.r+that.r,this.i+that.i);};
Range2.prototype.mul = function(that){return new Range2(this.r*that.r,this.i*that.i);};
Range2.prototype.mag = function(){return Math.sqrt(this.r*this.r + this.i*this.i);};
Range2.prototype.neg = function(){return new Range2(-this.r,-this.i);};
Range2.prototype.toString = function(){return '{'+this.r+','+this.i+'}';};
Range2.prototype.equals = function(that){
return that != null &&
that.constructor === Range2 &&
this.r === that.r &&
this.i === that.i;
};
//3.类属性,方法(通过类直接调用)
Range2.ZERO = new Range2(0,0);
Range2.ONE = new Range2(1,0);
Range2.I = new Range2(0,1);
Range2._format = /^{([^,] +),([^}] +)}$/;
Range2.parse = function(s){
try{
var m = Range2._format.exec(s);
return new Range2(parseFloat(m[1],parseFloat(m[2])));
}catch(x){
throw new TypeError("can not parse");
}
};
//4.创建实例
var c = new Range2(2,3);
var d = new Range2(c.i, c.r);
//==可以随时为类(包括内置类)的原型扩展或更改属性,类的所有实例对象将随之改变==
Range2.prototype.divide = function(){return this.r-this.i;};
var str = c.add(d).toString();
console.log(Range2.ONE); //{ r: 1, i: 0 }
console.log(str); //{5,5}
//Range2.parse(c.toString()).add(c.neg()).equals(Range2.ZERO); //函数返回自身,可实现链式调用
//=========检测对象的类==========
//(一)o instanceof p 检测对象o是不是p得实例 ,
// (二) p.isPrototypeOf(o)检测p是不是o的原型,
// (三)有constructor的函数可通过他来判断属于哪个类
// 他们只能检测对象是否属于指定的类,而无法通过对象获得类名
//在客户端JS中,每个窗口/框架子页面都具有单独的执行上下文,每个上下文都包含独立的全局变量和一组独立的构造函数
// 他们之间的实例即使继承自相同的原型对象,但也是相互独立的原型对象,他们之间互不为实例
function typeAndValue(x){
if(x == null)return ''; //Null,undefined类型没有构造函数
switch (x.constructor){
case Number:return 'Number: '+x;
case String:return 'String: '+x;
case Date: return 'Date: ' +x;
case RegExp:return 'RegExp: '+x;
case Range2:return 'Renge2: '+x; //处理自定义类型
}
}
//(四)通过构造函数名字来识别对象的类(但并不是所有对象都有构造函数,并不是所有函数有名字)
function classof(o){
return Object.prototype.toString.call(o).slice(8,-1); //获取类名
}
Function.prototype.getName = function(){ //返回函数名字,可能为空,非函数返回null
if('name' in this) return this.name;
return this.name = this.toString().match(/functions*([^(] *)(/)[1];
};
function type(o){
var t, c,n;
if(o === null)return 'null'; //处理null值
if(o !== o ) return 'nan'; //处理NaN
if((t = typeof o) !== 'object') return t; //typeof可以辨认除了object之外的类型
if((c = classof(o)) !== 'Object') return c;//识别出大部分内置对象的类名,排除值为'Object'
if(o.constructor && typeof o.constructor === 'function' && //若构造函数名字存在则返回它
(n = o.constructor.getName())) return n;
return 'Object'; //其它无法判别的一律返回'Object'
}
//=========鸭式辩型:不需要特意去检测它是什么类,只检测它是否具备我们所需要的特性就行!
// "把会游泳,会嘎嘎叫的鸟都当着是鸭子对待!"
原文地址:https://www.cnblogs.com/susufufu/p/5705781.html