在JS中,类的实现是基于其原型继承机制的。
如果两个实例都从同一个原型对象上继承了属性,我们就说它们是同一个类的实例。
如果两个对象继承自同一个原型,往往意味着它们是由同一个构造函数创建并初始化的。
使用关键字 new 来调用构造函数,使用 new 调用构造函数会自动创建一个新对象,因此构造函数本身只需初始化这个新对象的状态即可。
调用构造函数的一个重要特征是,构造函数的prototype属性被用作新对象的原型。这意味着通过同一个构造函数创建的所有对象都继承自一个相同的对象,因此它们都是同一个类的成员。
例如:
// 构造函数 // 并没有创建并返回一个对象,仅仅是初始化 function Range(from to){ this.from = from; this.to = to; } // 属性的名字必须是prototype Range.prototype = { includes: function (x) {return this.form <=x && x <= this.to;}, foreach: function (f) { for (var x = Math.ceil(this.for,);x <= this.to; x++)f(x); }, toString: function() {return "(" + this.from + "..." + this.to + ")";} }; // 使用 Range对象 var r = new Range(1,3); r.includes(2); r.foreach(console.log); <span style="font-family:SimSun;font-size:10px;"> </span>
一个常见的编程约定:定义构造函数既是定义类,并且类名首字母要大写;而普通的函数和方法都是首字母小写。
由于Range()构造函数是通过new 关键字调用的,因此不必调用其他什么逻辑来创建新对象。在调用构造函数之前就已经创建了新对象,通过this关键字可以获取这个对象。Range()构造函数只不过是初始化this而已。构造函数甚至不必返回这个新创建的对象,构造函数会自动创建对象,然后将构造函数作为这个对象的方法来调用一次,最后返回这个新对象。
构造函数必须通过关键字new调用,如果将构造函数用作普通函数的话,往往不会正常工作。
对Range()构造函数的调用会自动使用Range.prototype作为新Range对象的原型。
原型对象是类的唯一标识。
当且仅当两个对象继承自同一个原型对象时,它们才是属于同一个类的实例。而初始化对象的状态的构造函数则不能作为类的标识,两个构造函数的prototype属性可能指向同一个原型对象,那么这两个构造函数创建的实例是属于同一个类的。
JS中基于原型的继承机制是动态的:对象从其原型继承属性,如果创建对象之后原型的属性发生改变,也会影响到继承这个原型的所有实例对象。这意味着我们可以通过给原型对象添加新方法来扩充JS类。
检测任意对象的类的技术:
左操作数是待检测其类的对象,右操作数是定义类的构造函数。这里的继承可以不是直接继承。
instanceof运算符的缺点是,无法通过对象来获得类名,只能检测对象是否属于指定的类名。
例如:
switch(x.constructor){ case Number: case String: case Date: }
在JS中并非所有的对象都包含constructor属性。