面向对象的JavaScript-小结

Javascript中的类

类是对象的模板,用于创建共享一系列属性和方法的类似对象。

使用new关键字调用函数,可以创建对象实例。

1 function Accommodation(){};
2 
3 var house = new Accommodation();
4 
5 var apartment = new Accommodation();

同一模板的对象实例之间互无关联,这些对象实例是完全独立的变量,只不过共享同一个模板结构而已。

类的扩充:

1.找出对象的构造器

通过new关键字创建的对象的实例,有一个额外的属性:constructor,该属性指向创建该对象时所使用的JavaScript构造函数。

1 house.constructor === Accommodation;//true
2 
3 apartment.constructor === Accommodation;//true

关键字instanceof也可以检查对象是否是某个构造函数的实例。

house instanceof Accommodation;

apartment instanceof Accommmodation;

2.通过原型添加属性和方法

Javascript中的每个函数(构造器)都有一个叫prototype的属性,这个属性指向一个对象,我们用关键字new来创建一个“类”的对象实例时,实例中包含的属性和方法都来自prototype所指向的这个对象。

//定义构造函数
function Accommodation(){}

//为这个类添加属性
Accommodation.prototype.floors = 0;
Accommodation.prototype.rooms = 0;
Accommodation.prototype.sharedEntrance = false;

//为这个类添加方法
Accommodation.prototype.lock = function(){};
Accommodation.prototype.unlock = function(){};

//创建对象实例
var house = new Accommodation();
var apartment = new Accommodation();

//读取对象实例的属性
console.log(house.floors);//0
console.log(apartment.rooms);//0

//修改对象属性的值
house.floors = 2;
apartment.sharedEntrance = true;

//调用对象实例的方法
house.unlock();
apartment.lock();

prototype属性本身是一个对象,这个对象被关联在扮演“类”这个角色的函数身上,因此,还可以用对象直接量标记法为构造函数添加属性和方法。

//定义构造函数
function Accommodation(){}

//为这个类添加属性和方法
Accommodation.prototype = {
  floors: 0,
  rooms: 0,
  sharedEntrance: false,
  lock: function(){},
  unlock: function(){}                
}

prototype这个关键字有一个强大的特性:允许在对象实例已经被创建后继续添加属性和方法,新增属性和方法会自动添加到所有对象实例中,不管是已创建的还是将要创建的。

3.通过作用域添加属性和方法

javascript函数体内定义的变量和函数,作用域都限于函数体内,在该函数体外无法访问这些变量和函数——对这些函数和变量来说,包裹他们的外层函数提供了一个沙箱般的编程环境,或者说一个闭包。

4.上下文和this关键字

javascript中this关键字代表的是一个函数的上下文环境,这个上下文环境大多数情况下指的是函数运行时封装这个函数的那个对象。

//在所有函数之外,this表示的是全局window对象
console.log(this === window);//true

//因为doSomething函数在对象外部被调用,this指向的是浏览器中的window对象
function doSomething(){
  console.log(this === window); //true     
}

doSomething();

var house = {
  floors: 2,
  isLocked = false,
  lock: function(){
    console.log(this === house);//true  因为this关键字代表的是包含这个方法的那个对象
    //也可以把this看做house对象的替身,可以使用点标记法
    this.isLocked = true;            
  },      
}

house.lock();
console.log(house.isLocked);//true

 对象中的嵌套函数其上下文环境是全局的window对象,而非包含它的那个对象。但我们可以在this指向包含这个函数的对象时,将this的值保存在一个变量中,在用到该对象时用这个变量代替。

var apartment = {
  islocked: false,
  lock: function(){
    var that = this;
    //设置isLocked属性
    this.isLocked = true;
    function doSomething(){
      console.log(this === apartment);//false
      console.log(this === window);//false
      console.log(that === apartment);//false
      //通过that变量来修改apartment对象的isLocked属性
      that.isLocked = false;
    }  
    doSomething();
  },    
}

apartment.lock();

console.log(apartment.isLocked);//false

在使用new关键字创建对象时,this指向的值和一般情况下又有区别,这种情况下this指向的是通过构造函数所创建的那个对象实例。也因为这个特性,得以在构造函数中通过this来设置所有对象实例的属性和方法,而非像之前那样使用prototype关键字。

//定义一个新的构造函数来表示一种住宅
function Accommodation(){
  //this关键字指向的是通过这个“类”创建的对象实例
  this.floors = 0;
  this.rooms = 0;
  this.sharedEntrance = false;
  this.isLocked = false;
  this.lock = function(){
    //函数中的this一般指向包含函数的那个对象,本例中的this指向的是创建的对象实例,因为这个函数是通过这个被创建的对象实例来调用的
    this.isLocked = true;  
  };
  this.unlock = function(){
    this.isLocked = false;
  }        
}

//通过构造函数来创建对象实例
var house = new Accommodation();
var apartment = new Accommodation();

//读取和修改属性值,调用方法等操作都和普通对象一样
console.log(house.floors);
house.floors = 2;
apartment.lock();

开发者一般会结合使用prototype和this关键字来定义对象实例的属性和方法,其中前者用来定义方法,后者用来定义属性。每次通过构造器创建一个新的对象实例,构造函数都会被执行一次。之所以结合使用这两个关键字,是为了避免每次初始化一个对象实例时都要执行那些对方法进行初始化的代码。通过prototype关键字定义的方法只需定义一次,然后就可以为所有通过这个构造函数创建的对象所用,这使得对象的创建变得更加高效;

 

开发者们喜欢在构造函数中使用this关键字来设置属性的另一个原因是可以给构造函数传递参数,这样我们就能在调用构造函数时通过传递参数来对某些属性进行初始化了。

 

我们也可以向构造函数传递一个对象直接量作为唯一的参数,这个对象直接量包含了进行属性设置所需的所有初始值。

 

5.方法的链式调用

要实现链式调用,只需在“类”中的每个方法最后通过this关键字返回对象实例的引用即可。

6.继承

传统编程语言的一项关键功能就是可以创建一些新的类来继承或者扩展某个父类的属性和方法,这些新的类和该父类都有某种类似的逻辑关联,这些新的类被称为子类。

Javascript中也可以实现这种继承,不过是通过Javascript的原型链来实现,被称为原型继承。

//定义一个有两个方法的类
function Accommodation(){}

Accommodation.prototype.lock = function(){}
Accommodation.prototype.unlock = function(){}

//定义一个构造函数,它将成为我们的子类
function House(defaults) {
  defaults = defaults || {};
  //将本类所有实例的floors属性初始化为2
  this.floors = 2;

  this.rooms = defaults.rooms ||7;
}

//将House类的原型设为Accommodation“类”的一个实例,使用关键字new来调用Accommodation的构造函数,这样就能创建并返回一个包含所有属性和方法的对象。这个对象被传递给House"类"的原型,这样House"类"就得以继承Accommodation 的所有内容
House.prototype = new Accommodation();

//对象实例的constructor属性指向创建该对象的那个构造函数,然而由于House继承了Accommodation的所有内容,constructor值也被复制了,所以我们现在需要重设constructor值,使其指向新的子类。如果没有这一步,通过House"类"创建的对象就会报告说他们是通过Accommodation“类”创建的。
House.prototype.constructor = House;

封装:

当通过继承对已有的类进行改变或特殊化时,父类的所有属性和方法对子类都是可用的,在子类中不需要额外声明或定义任何东西就能使用父类的属性和方法,这种特性被称为封装。子类之需要定义那些在父类基础上新增的属性和方法即可;

多态:

在构造一个新的子类来继承并扩展一个“类”的时候,你可能需要将某个方法替换为一个同名的新方法,新方法和原方法功能类似,但对子类做出了针对性的改变,这就是多态,Javascript中实现多态,只需重写一个函数并给它一个和原方法形同的方法即可;

call和apply

arguments对象

公有,私有以及受保护的属性和方法

在构造函数中通过var定义的变量其作用域局限于该构造函数内——在prototype上定义的方法无法访问这个变量,因为这些方法有自己的作用域。要想通过公有的方法来访问私有的变量,需要创建一个同时包含两个作用域的新作用域,为此,我们可以创建一个自我执行的函数,称为闭包。该函数完全包含了类的定义,包括所有私有变量以及原型方法。

javascript有一个非强制性的但很有用的编程惯例,就是对所有私有变量或函数名加一个下划线(_)作为前缀,以标识他们是私有的,这有助于你及项目组的其他成员更好的理解每个类的作者的意图。

// 我们将类定义包在一个自我执行的函数里,这个函数返回我们所创建的类并将其保存在一个变量中,一边在后面的代码中使用
  var Accommodation = (function(){
    //定义类的构造函数,因为处于一个新的函数内,我们也切换到了一个新的作用域中,所以可以使用与保存函数返回值得那个变量相同的名字
    function Accommodation(){
      //此处定义的变量都是私有的,这些变量在当前作用域之外不可用,可以通过变量名添加下划线前缀来标识这一点
      var _isLocked = false,
          _isAlarmed = false,
          _alarmMessage = "Alarm activated!";
      //仅在当前作用域中定义的函数(未在构造函数上定义的)也是私有的
      function _alarm(){
        _isAlarmed = true;
        alert(_alarmMessage);
      }

      function _disableAlarm(){
        _isAlarmed = false;
      }

      //所有定义在原型上的方法都是公有的,当我们在此处创建爱你的类在闭包结束处被返回后,就可以在当前作用域之外访问这些方法了
      Accommodation.prototype.lock =function(){
        _isLocked = true;
        _alarm();
      }

      Accommodation.prototype.unlock = function(){
        _isLocked = false;
        _disableAlarm();
      }

      //定义一个getter函数来对私有变量_islocked的值进行只读访问——相当于把该变量定义为了受保护的
      Accommodation.prototype.getIsLocked = function(){
        return _isLocked;
      }

      //定义一个setter函数来对私有变量_alarmMessage进行只写访问——相当于将其定义为了受保护的
      Accommodation.prototype.setAlarmMessage = function(message){
        _alarmMessage = message;
      }

      //返回在这个作用域中创建的类,使之在外层作用域中即后面的代码中的所有位置都可用,只有公有的属性和方法是可用的
      return Accommodation;
    }
  }());
  

 

posted on 2019-02-10 16:17  m合肥  阅读(135)  评论(0编辑  收藏  举报

导航