数据库需要双活,所以将两台服务器安装MySQL后,做主主复制同步,再配合Keepalived即可实现双活。

基于原型(prototype)创建类

ES6之前JavaScript没有类的概念,是基于原型(prototype)创建类,或者说是类型对象。

function Book(id, bookname, price) {
  this.id = id;
  this.bookname = bookname;
  this.price = price;
}

以上代码即创建了Book类,使用下面的方式实例化,创建一个book对象。

var book = new Book(1, 'javascript', 50);

这个Book类只包含属性,还没有方法。因为通过new实例化对象时,都会对类型对象中this指向的属性或方法复制到新的对象中,但方法通常不需要复制一份,通过prototype解决。

Book.prototype.display = function () {
  return {id: this.id, bookname: this.bookname, price: this.price};
};

上面创建的book对象如下图:

通过new实例化对象时,从Book中将id, bookname, price复制了一份到book,并将Book的prototype指向赋值给book对象的__proto__。

类属性作用域

类属性(包括方法)的作用域包括私有属性、公有属性和静态属性。

function Book(id, bookname, price) {
  // 公有属性
  this.id = id;
  this.bookname = bookname;
  this.price = price;

  // 私有属性
  var status;
  // 公有方法(status只能通过这两个方法访问)
  this.setStatus = function (_status) {
    status = _status;
  };
  this.getStatus = function () {
    return status;
  };
}

// 公有方法
Book.prototype.display = function () {
  return {id: this.id, bookname: this.bookname, price: this.price, status: this.getStatus()};
};

// 静态属性(对象不能访问)
Book.isChinese = true;

var book = new Book(1, 'javascript', 50);
book.setStatus('有货');

console.log('id: ' + book.id);
console.log('book info: ', book.display());
console.log('chinese book: ', Book.isChinese);

闭包

在一个函数(A)内部创建的另外一个函数(B)就是一个闭包,函数(B)有权访问函数(A)作用域中的变量,对作用域的访问权限类似于代码块。

function a() {
  var xa = 'function a';
  return function b() {
    var xb = 'function b';
    return function c() {
      console.log(xa);
      console.log(xb);
    }
  }
}

var b = a();
var c = b();
c();

如上代码,函数c可以访问函数b和函数a中的私有变量。

通过闭包创建类,可以增加一些额外功能。

var Book = (function() {
  var totalBook = 5;  // 最多5本书
  function _book(id, bookname, price) {
    if (totalBook === 0) {
      throw new Error('failed');
    }

    totalBook--;

    this.id = id;
    this.bookname = bookname;
    this.price = price;
  }
  _book.prototype = {
    display: function () {
      return {id: this.id, bookname: this.bookname, price: this.price};
    }
  };
  return _book;
})();

var book1 = new Book(1, 'book1', 10);
console.log(book1.display());	// { id: 1, bookname: 'book1', price: 10 }
var book2 = new Book(2, 'book2', 10);
console.log(book2.display());	// { id: 2, bookname: 'book2', price: 10 }
var book3 = new Book(3, 'book3', 10);
console.log(book3.display());	// { id: 3, bookname: 'book3', price: 10 }
var book4 = new Book(4, 'book4', 10);
console.log(book4.display());	// { id: 4, bookname: 'book4', price: 10 }
var book5 = new Book(5, 'book5', 10);
console.log(book5.display());	// { id: 5, bookname: 'book5', price: 10 }

var book6 = new Book(6, 'book6', 10);	// throw new Error('failed');

继承

类的继承需要同时继承父类的原型及复制父类this上的属性。

// 父类
function SuperClass(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}
SuperClass.prototype.getName = function() {
  return this.name;
};

// 子类
function SubClass(name, time) {
  // 构造函数式继承, 使用子类作用域调用父类构造函数,使父类属性都设置到子类的this上
  // 如果没有这一步,多个子类对象可能直接使用同一个父类实例化后的值
  SuperClass.call(this, name);
  
  // 子类特有属性
  this.time = time;
}

// 继承父类原型
SubClass.prototype = new SuperClass();

// 子类特有方法
SubClass.prototype.getTime = function() {
  return this.time;
};

// 测试
var instance1 = new SubClass('instance1', 2011);
var instance2 = new SubClass('instance2', 2012);

instance1.colors.push('green');

console.log(instance1.colors);  // [ 'red', 'blue', 'green' ]
console.log(instance2.colors);  // [ 'red', 'blue' ]

console.log(instance2.getName()); // instance2
console.log(instance2.getTime()); // 2012

上面的代码中,继承父类时使用了SubClass.prototype = new SuperClass();,当父类this属性较多时,会有很多不必要的复制,所以这边做一个改进,使用过渡函数的方式来继承父类的原型。完整代码如下:

// 继承原型
function inheritPrototype(subClass, superClass) {
  function F() {} // 过渡函数
  F.prototype = superClass.prototype;  // 仅提取父类原型属性
  var p = new F();
  p.constructor = subClass; // 使用子类构造器
  subClass.prototype = p;   // 子类继承父类原型, 却没有父类实体化后的多余数据
}

// 父类
function SuperClass(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}
SuperClass.prototype.getName = function() {
  return this.name;
};

// 子类
function SubClass(name, time) {
  // 构造函数式继承, 使用子类作用域调用父类构造函数,使父类属性都设置到子类的this上
  // 如果没有这一步,多个子类对象可能直接使用同一个父类实例化后的值
  SuperClass.call(this, name);
  
  // 子类特有属性
  this.time = time;
}

// 继承父类原型
inheritPrototype(SubClass, SuperClass);

// 子类特有方法
SubClass.prototype.getTime = function() {
  return this.time;
};

// 测试
var instance1 = new SubClass('instance1', 2011);
var instance2 = new SubClass('instance2', 2012);

instance1.colors.push('green');

console.log(instance1.colors);  // [ 'red', 'blue', 'green' ]
console.log(instance2.colors);  // [ 'red', 'blue' ]

console.log(instance2.getName()); // instance2
console.log(instance2.getTime()); // 2012