JavaScript_类、构造函数和原型;

时间:2022-08-24 23:13:20
1、如何定义自己的构造函数?
引言:只需要编写一个为this添加属性的函数即可;
// define the constructor:
function Rectangle(w, h) {
// 定义不共享的属性:
this.width = w;
this.height = h;
}
// the prototype object holds methods and other properties that should be shared by each instance:
Rectangle.prototype.area = function() {
return this.width * this.height;
}
//Invoke(引用) the constructor to create two Rectangle objects;
var rectangleOne = new Rectangle(3, 4); // rectangleOne = {width: 3, height: 4};
var rectangleTwo = new Rectangle(8.5, 11); // rectangleTwo = {width: 8.5, height: 11};


2、如何扩展内建类型
引言:所谓的内建类型,就像String、Date这些内建类型;
(1)扩展String对象:
// returns true if the last character is c:
String.prototype.endWith = function(c) {
return (c == this.charAt(this.length - 1));
}
// 使用endWith()方法:
var message = "hello world";
message.endWith('h'); // Returns false;
message.endWith("d"); // Returns true;


3、在javascript中模拟类:
(1)类成员:
a、实例属性_(由构造函数创建和初始化的属性);
b、实例方法-(由实例本身调用的方法,可以是从构造函数原型处继承,或者是本身自定义该方法;);
c、类属性(只有类本身能访问的属性,由构造函数本身进行属性定义);
d、类方法(只能通过类本身来调用);


4、Circle类例子:
// constructor构造函数:
function Circls(radius) { // radius半径;
// 实例属性:
this.r = radius;
}
// prototype原型:
Circle.prototype.area = function() { // 实例方法;
return Circle.PI * this.r * this.r;
}
// 类属性:
Circls.PI = 3.14159; 
// 类方法:
Circls.max = function(a, b) { // a、b为两个圆实例,max()方法则是比较它们谁的半径大并返回此对象;
if(a.r > b.r) {
return a;
} else {
return b;
}
}
// 类的实例:
var c = new Circle(2.3); // 创建一个Circle类的实例;
c.r = 1.1; // 设置c实例的半径属性;
var areaOfc = c.area(); // 调用c的实例方法area();
var result = Math.exp(Circle.PI); // 调用类属性得到e的PI次幂的值;
var d = new Circle(1.2);
var bigger = Circle.max(c, d); // 调用类方法判断哪个实例大;


5、类定义步骤:
(1)定义类的构造函数_定义、初始化实例属性;
(2)定义原型里边共享的实例方法(可能还有共享的属性);
(3)定义类属性、类方法;


6、私有成员
引言:如何像Java一样把某个属性声明为私有?看下面代码:
function ImmutableRectangle(w, h) {
this.getWidth = function() { return 2;},
this.getHeight = function() { return 3;}
}
ImmutableRectangle.prototype.area = function() {
return this.getWidth * this.getHeight();
}


7、JavaScript类如何进行子类化
// 父类Rectangle:
function Rectangle(w, h) {
this.width = w;
this.height = h;
}
Rectangle.prototype.area = function() {
return this.width * this.height;
}


// 子类PositionedRectangle:
function PositionedRectangle(x, y, w, h) {
Rectangle.call(this, w, h); // 引用父类的call()方法来初始化相应实例属性,如有需要,可传相应参数;

this.x = x; // rectangle左上角坐标;
this.y = y;
}
PositionedRectangle.prototype = new Rectangle(); // 显示创建子类原型为父类;
// 删除不需要继承的属性,如width、height这些实例属性;
delete PositionedRectangle.prototype.width;
delete PositionedRectangle.prototype.height;
// 给原型constructor重新指向:指向子类构造函数;
PositionedRectangle.prototype.constructor = PositionedRectangle;
// 创建contains()方法以判断所给的点是否在rectangle内:
PositionedRectangle.prototype.contains = function(x, y) {
return (x>this.x && x<this.x+this.weight && y>this.y && y<this.y*this.height);
}


// 实例化PositionedRectangle类:
var rectangle = new PositionedRectangle(2, 2, 2, 2);
console.log(rectangle.contains(3, 3)); // 调用本身实例方法;
console.log(rectangle.area()); // 调用继承得来的实例方法;


8、构造函数链
// 如果只有一层子类,可为PositionedRectangle的原型添加名为superclass属性,可简化构造函数链:
PositionedRectangle.prototype.superclass = Rectangle;


// 和前边属性定义相比,构造函数链的语法更简单:
function PositionedRectangle(x, y, w, h) {
this.superclass(w, h);
this.x = x;
this.y = y;
}
特别说明:这种技术只在简单的继承层次中才起作用。比如A=>B=>C,当B和C都使用这种Superclass方法,当创建C的实例时,this.superclass会应用B(),B()构造函数将无限
递归;


9、如何调用被覆盖的方法?
// 定义PositionedRectangle的toString()方法覆盖其父类对应方法:
PositionedRectangle.prototype.toString = function() {
return "(" + this.x + ", " + this.y + ") " + // PositionedRectangle属性;
Rectangle.prototype.toString.apply(this); // 调用父类的toString()方法,apply()不可少;
}
或者:
PositionedRectangle.prototype.toString = function() {
return "(" + this.x + ", " + this.y + ") " + this.superclass.prototype.toString.apply(this);
}


10、如何进行非继承的扩展?
(1)=>// 从某个类借用方法供另一个类使用;
function borrowMethods(borrowForm, addTo) {
var from = borrowForm.prototype;
var to = addTo.prototype;


for(var m in from) {
if(typeof from[m] != "function") {
continue; // 不是函数,则忽略此此循环,进入下一循环;
}
to[m] = from[m];
}
}


(2)=>// 带有通用的供借用的方法的混入类(mixin classes 或 mixins):
function GenericToString() {}; // 带有通用toString()方法的混入类;
GenericToString.prototype.toString = function() {
var props = [];
for(var name in this) { // 循环所有属性;
if(!this.hasOwnProperty(name)) continue;


var value = this[name];
var s = name + ":";


switch(typeof value) {
case 'function':
s += "function";
break;
case 'object': 
if(value instanceof Array) {
s += "array";
} else {
s += value.toString();
}
break;
default: 
s += String(value);
break;
}
props.push(s);
}
return "{" + props.join(",") + "}";
}


function GenericEquals() {}; // 带有通用equals()方法的混入类;
GenericEquals.prototype.equals = function(that) {
if(this == that) return true;


var propsInThat = 0;
for(var name in that) {
propsInThat++;
if(this[name] !== that[name]) {
return false;
}
}


var propsInThis = 0;
for(var name in this) {
propsInThis++;
}


if(propsInThis != propsInThat) {
return false;
}
return true;
}


//如何借用:
// Here is a simple(简单的) Rectangle class;
function Rectangle(x, y, w, h) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
}
Rectangle.prototype.area = function() {
return this.width * this.height;
}
// 借用方法:
borrowMethods(GenericEquals, Rectangle);
borrowMethods(GenericToString, Rectangle);


(3)=>//借用Colored混入类的构造函数和方法;
// This mixin has a method that depends on this constructor.So both the constructor and the method must be borrowed.
function Colored(c) { this.color = c; }
Colored.prototype.getColor = function() {
return this.color;
}


// Define the constructor for a new class.
function ColoredRectangle(x, y, w, h, c) {
this.superclass(x, y, w, h); // 调用父类的构造函数;
Colored.call(this, c); // 借用Colored类构造函数;
}
// 从PositionedRectangle类那继承方法来设置原型对象;
ColoredRectangle.prototype = new Rectangle();
ColoredRectangle.prototype.constructor = ColoredRectangle;
ColoredRectangle.prototype.superclass = Rectangle
// 借用方法;
borrowMethods(Colored, ColoredRectangle);


10、如何确定对象类型;
(1)、typeof
说明:typeof主要用途还是从对象中区分出基本类型;
注意:typeof null是"object", 而typeof undefined是"undefined";任何数组的类型都是"object",因为所有数组都是对象;任何函数的类型都是"function",尽管函数也是
对象;
(2)、instanceof
说明:一旦确定某个值是对象而不是基本类型值或者函数,就可以使用instanceof运算符来详细了解它;
(3)、扩展typeof测试
function getType(x) {
// if x is null, return "null";
if(x == null) {
return "null";
}


// Next try the typeof operator;
var t = typeof x;
if(t != "object") {
return t;
}


// Otherwise, x is an object. Use the default toString() methed to get the class value of the object;
var c = Object.prototype.toString.apply(x); // Returns "[object class]";
c = c.substring(8, c.length-1); // 截取class部分;
if(c != "Object") {
return c;
}


// If we get here, c is "Obeject". Check to see if the value x is really just a generic object;
if(x.constructor == Object) {
return c; // ok, the type really is "Object";
}


// For user-defined(用户自定义的) classes. look for a string-valued property named classname, that is inherited from the object's prototype.
if("classname" in x.constructor.prototype && // inherits classname 
typeof x.constructor.prototype.classname == "string") {// its a string
return x.constructor.prototype.classname;
}

// 如果找不到
return "<unknown type>";
}
(4)、鸭子类型识别:
//示例:测试某个对象是否借用某一个类的方法;
// Return true if each of the method properties in c.prototype hava been borrowed by o;
function borrows(o, c) {// o为对象,c为类;
if(o instanceof c) {
return true;
}


if(c == Array || c == Boolean || c == Date || c == Error || c == Function || c == Number || c == RegExp || c == String) {
return undefined;
}


if (typeof o == 'function') {
o = o.prototype;
}
var proto = c.prototype;
for(var p in proto) {
if(typeof proto[p] != "function") {
continue;

if(o[p] != proto[p]) {
return false;
}
}
return true;
}
(5)、示例:测试某个对象是否提供了方法;
function provides(o, c) {
if(o instanceof c) return true;


// If a constructor(Function) was passed instead of an object, use its prototype;
if(typeof o == "function") o = o.prototype;


// The methods of built-in types(内建类型)  are not enumerable(不可数的), and we return undefined;
if(c == Array || c == Boolean || c == Date || c == Error || c == Function || c == Number || c == RegExp || c == String) {
return undefined;



var proto = c.prototype;
for(var p in proto) {
if(typeof proto[p] != "function") continue;


if(!(p in o)) return false;


if(typeof o[p] != "function") return false;


if(o[p].length != proto[p].length) return false;
}
return true;
}
(6)、示例:测试类似数组的对象
function isArrayLike(x) {
if(x instanceof Array) return true;
if(!("length" in x)) return false;
if(typeof x.length != "number") return false;
if(x.length < 0) return false;
if(x.length > 0) {
// If the array is nonempty, it must at a minimun have a property defined whose name is the number length-1;
if(!((x.length-1) in x)) return false;
}
return true;
}
11、defineClass()工具方法_200_184
// 示例:用来定义类的一个工具函数;
/*
* defineClass()_It is a utility function for defining JavaScript classes.

* This function expects a single object as its only argument. //传入的参数希望为一个简单的对象;
* It defined a new JavaScript class based on the data in that object and returns the constructor function of the new class.
* This function handles(处理) the repetitive(重复的) tasks of defining classes: setting up the prototype object for correct(正确的) inheritance,copying
  methods from other types, and so on.

* The object passed as an argument should hava some or all of the following properties:
* name: The name of the class being defined.If specified, this value will ba stored in the classname property of the prototype object.
*
* extend: The constructor of the class to be extend. If omitted(忽略), the Object() constructor will be used. This value will be stored
in the superclass property of the prototype object.
*
* construct: The constructor function for the class. If omitted, a new empty function will be used. This value becomes the return value
  of the function, and is also stored in the constructor property of the prototype object.
*
* methods: An object that specifies the instance methods (and other shared properties) for the class. The propertied of this object are
copied into the prototype object of the class. If omitted, an empty object is used instead. Properties named "classname",
"superclass", and "constructor" are reserved and should not be used in this object.
*
* statics: An object that specifies the static methods (and other static properties) for the class. The properties of this object become
properties of the constructor function. If omitted, an empty object is used instead.
*
* borrows: A constructor function or array of constructor functions. The instance methods of each of the specified classes are copied
into the prototype object fo this new class so that the new class borrows the methods of each specified class. Constructors 
are processed(处理) in the order they are specified. so the methods of a class listed at the end of the array may overwrite
the methods of those specified earlier. Note that borrowed methods are stored in the prototype object before the properties
of the methods object above. Therefore(因此), methods specified in the methods object can overwrite borrowed methods. If this
property is not specified, no methods are borrowed.
*
* provides: A constructor function or array of constructor function. After the prototype object is fully initialized, this function verifies
   (核实) that the prototype includes methods whose names and number of arguments match the instance methods defined by each of 
 these classes. No methods are copied, this is simply an assertion(声明) that this class "provides" the functionality of the
 specified classes. If the assertion fails, this method will throw an exception. If no exception is thrown, any instance of the new
 class can also be considered (using "duck typing") to be an instance of these other types. If this property is not specified, no
 such verification(验证) is performed(执行).
*/
function defineClass(data) {
// Extract(提取) the fields we will use form the argument object. Set up default values.
var classname = data.name;
var superclass = data.extend || Object;
var constructor = data.construct || function() {};
var methods = data.methods || {};
var statics = data.statics || {};
var borrows;
var provides;


// Borrows may be a single constructor or an array of them.
if(!data.borrows) { 
borrows = []; 
} else if(data.borrows instanceof Array) {
borrows = data.borrows;
} else {
borrows = [data.borrows];
}


if(!data.provides) {
provides = [];
} else if(data.provides instanceof Array) {
provides = data.provides;
} else {
provides = [ data.provides ];
}


// Create the object that will become the prototype for our class;
var proto = new superclass();


// Delete any noninherited properties of this new propotype object. // 删除不继承的属性;
for(var p in proto) {
if(proto.hasOwnProperty(p)) { delete proto[p]; }
}


// Borrow methods from "mixin"(混入类) classes by copying to our prototype.
for(var i=0; i<borrows.length; i++) {
var c = data.borrows[i];
borrows[i] = c;
// Copy method properties from prototype of c to our prototype;
for(var p in c.prototype) {
if(typeof c.prototype[p] != "function") { continue; }
proto[p] = c.prototype[p];
}
}


// Copy instance methods to the prototype object; This may overwrite methods of the mixin classes;
for(var p in methods) { proto[p] = methods[p]; }


// Set up the reserved "constructor", "superclass", and "classname" properties of the prototype;
proto.constructor = constructor;
proto.superclass = superclass;
// classname is set only if a name was actually specified.
if(classname) { proto.classname = classname; }


// Verify(核实) that our prototype provides all of the methods it is supposed to.
for(var i=0; i<provides.length; i++) { // for each class
var c = provides[i];
for(var p in c.prototype) {
if(typeof c.prototype[p] != "function") { continue; }
if(p == "constructor" || p == "superclass") { continue; }


// Check that we have a method with the same name and that it has the same number of declared(声明) argument. If so, move on;
if(p in proto && typeof proto[p] == "function" && proto[p].length == c.prototype[p].length ) {concinue; }
// Otherwise, throw an exception
throw new Error("class " + classname + " does not provide method " + c.classname + "." + p);
}
}


// Associate(关联) the prototype object with the constructor function
constructor.protorype = proto;


// Copy static properties to the constructor
for (var p in statics) { constructor[p] = data.statics[p]; }


// Finally,  return the constructor function
return constructor;
}