【js学习笔记-051】类和模块

如果两个对象继承自同一个原型,往往意味着(但不是绝对)它们是由同一个构造函数创建并初始化的。

在javascript中,类的所有实例对象都从同一个原型对象上继承属性。因此,原型对象是类的核心

js学习笔记】类和原型

 

回顾inherit(p)

function inherit(p){

  if(p==null){throw TypeError();}

  if(Object.create)return Object.create(p);

  var t = type of p;

  if(t!="object" && t!="function") throw TypeError();

 function f(){};

 f.prototype = p;

 return new f();

}

这个函数返回一个新对象,该对象继承自某个原型对象。

如果定义一个原型对象,然后通过inherit()函数创建一个继承自它的对象,这样就定义了一个javascript 类。通常类的实例还需要进一步的初始化,通常是通过定义一个函数来创建并初始化这个新对象。下面创建的一个表示范围的原型对象,并定义了一个“工厂”函数用以创建并初始化类的实例

//工厂方法返回一个“新的范围对象”

function range(from,to){

  var r = inherit(range.methods);

  //这两个属性是不可继承的,每个对象都拥有唯一的属性

 r.from = from;

 r.to = to;

 return r;

}

//原型对象定义方法,这些方法为每一个范围对象所继承

range.methods = {

 includes:function(s){ return this.from <=x && x<=this.to},

 foreach:function(f){

   for(var x=Math.ceil(this.form);x<this.to;x++){f(x);}

  },

  toString:fucntion(){

   return "("+this.from+"..."+this.to+")";

  }

};

 

var r = range(1,3)

r.includes(2); //=>true

r.foreach(console.log); // 1 2 3

console.log(r); //=>(1...3)

 

js学习笔记】类和构造函数

定义原型对象并使用“工厂”方式是定义类的一种方法。但这种方法并不常用,毕竟它没有定义构造函数。构造函数是用来初始化新创建的对象的。使用关键字new来调用构造函数

1.使用new调用构造函数会自动创建一个对象,因此构造函数本身只需要初始化这个新对象的状态即可。

2.调用构造函数的一个重要特征是构造函数的prototype属性被用做新对象的原型。即通过同一个构造函数创建的所有对象都继承自一个相同的对象,因此它们都是同一个类的成员。

改造上节中的“范围类”用构造函数代替工厂函数:

function Range(from,to){

  //此构造函数,用以初始化新创建的“范围对象”,注意这里并没有返回一个对象仅仅初始化

  this.from = from;

  this.to = to;

}

//原型对象

//所有对象都继承自这个对象

Range.prototype = { //注意属性的名字必须是prototype

  includes:function(s){return this.from <=x && x<=this.to},

 foreach:function(f){

   for(var x=Math.ceil(this.form;x<this.to;x++)){f(x);}

  },

  toString:fucntion(){

   return "("+this.from+"..."+this.to+")";

  }

};

var r = new Range(1,3)

r.includes(2); //=>true

r.foreach(console.log); // 1 2 3

console.log(r); //=>(1...3)

 

两种方式的区别:

1.命名区别:工厂函数首字母小写range(),而构造函数首字母大写。这里遵循了一个常见的编程约定:从某种义意上讲构造函数即是定义类,类名首字母大写;

2.调用方式:构造函数使用new关键字调用,工厂函数使用普通的函数调用。使用构造函数调用方式,在调用构造函数之前就已经创建了新对象,通过this可以获取这个新对象。然后把构造函数做为这个对象的方法调用一次,最后返回这个新对象。

注意:构造函数和普通函数定义不尽相同的原因是:构造函数就是用来“构造新对象”的,它必须通过关键字new调用,如果将构造函数用做普通函数的话,往往不会正常工作。所以可以通过命名约定来判断是否应当在函数之前冠以关键字new.

3.原型对象的命名:第一段代码中的原型是range.methods(具有很好的语义,但过于随意)。第二段强制命名为 Range.prototype。对构造函数的调用,会自动使用Range.prototype作为新对象的原型

js学习笔记】构造函数和类的标识

原型对象是类的唯一标识,当且仅当两个对象继承自同一个原型对象时,它们才是属于同一个类的实例,而初始化对象的状态和构造函数则不能做为类的标识,两个构造函数的prototype属性可能指向同一个原型对象。那么这两个构造函数创建的实例属于同一类

 

 

 

尽管构造函数不像原型那样基础,但构造函数是类的“外在表现”。很明显,构造函数的名字通常用做类名。更具体的讲,在检测对象是否属于某个类时会用到构造函数。假如判断r是否是Range对象

r instanceof Range //如果r继承自Range.prototype,则返回true

即instanceof 不会检查r是由Range()构造函数初始化而来的,而会检查r是否继承自Range.prototype。不过instanceof的语法则强化了“构造函数是类的公有标识”的概念。

 

js学习笔记】constructor属性

Range.prototype定义了一个新对象,这个对象包含类所需要的方法。

其实没有必要新创建一个对象,用单个对象直接量的属性就可以方便地定义原型上的方法。

任何js函数都可以用做构造函数,并且调用构造函数是需要用到一个prototype属性的

因此每个js函数(除ECMAScript 5的Function.bind()外)都自动拥有一个prototype属性。这个属性的值是一个对象。该对象包含唯一一个不可枚举的属性constructor。

 constructor属性值是一个函数对象:

var F = fucnction(){}; //函数对象

var p = F.prototype; //F相关联的原型对象

var c = p.constructor; //与原型相联的函数

c===F //=>true 对于任意函数的F.prototype.constructor===F

可以看到构造函数的原型中存在预定义好的constructor属性,这意味着对象通常继承的constructor属性均指代它们的构造函数。由于构造函数是类的“公共标识”,因此这个constructor属性为对象提供了类。

因此

var o = F();

o.constructor === F; //=>true,

 

构造函数          原型                实例

   原型---->                     <------- new 构造函数()

      <-----       constructor    <------- new 构造函数()

 

注意:我们之前提到的Range构造函数示例,它使用了一个新的对象,来重新定义了Range.prototype。这个新定义的原型对象不含有constructor属性。因此在Range实例上也无这个属性。

我们可以通过显示定义constructor属性

Range.prototype = {

  constructor:Range

  ...

}

另一种方法是使用预定义的原型属性,因为预定义的原型属性包含constructor属性

Range.prototype.include = ..

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值