灏天阁

面向对象编程

· Yin灏
/* 封装 */

/**
 * 创建一个类
 */
var Book = function (id, bookname, price) {
  this.id = id;
  this.bookname = bookname;
  this.price = price;
};

Book.prototype.display = function () {
  // 展示这本书
};

// 或者
Book.prototype = {
  display: function () {},
};
var book = new Book(10, "JavaScript设计模式", 50);
console.log(book.bookname);

/* 属性与方法封装 */

var Book = function (id, name, price) {
  // 私有属性和方法,外部无法访问
  var num = 1;
  function checkId() {}
  // 特权方法
  this.getName = function () {};
  this.getPrice = function () {};
  this.setName = function () {};
  this.setPrice = function () {};
  // 公有属性和公有方法
  this.id = id;
  this.copy = copy;
  // 构造器
  this.setName(name);
  this.setPrice(price);
};

// 静态公有属性,对象不能访问
Book.isChinese = true;
Book.resetTime = function () {
  console.log("new Time");
};
Book.prototype = {
  // 公有属性
  isJSBook: false,
  // 公有方法
  display: function () {},
};
// 使用
var b = new Book(11, "JavaScript设计模式", 50);
console.log(b.num); // undefined
console.log(b.isJSBook); // false
console.log(b.id); // 11
console.log(b.isChinese); // undefined
console.log(Book.isChinese); // true
Book.resetTime(); // undefined

/**
 * 有时经常将类的静态变量通过闭包实现。
 */
// 利用闭包实现
var Book = (function () {
  // 静态私有变量
  var bookNum = 0;
  // 静态私有方法
  function checkBook(name) {}
  // 返回构造函数
  return function (newId, newName, newPrice) {
    // 私有变量
    var name, price;
    // 私有方法
    function checkID(id) {}
    // 特权方法
    this.getName = function () {};
    this.getPrice = function () {};
    this.setName = function () {};
    this.setPrice = function () {};
    // 公有属性
    this.id = newId;
    // 公有方法
    this.copy = function () {};
    bookNum++;
    if (bookNum > 100) {
      throw new Error("我们仅出版 100 本书");
    }
    // 构造器
    this.setName(name);
    this.setPrice(price);
  };
})();

Book.prototype = {
  // 静态公有属性
  isJSBook: false,
  // 静态公有方法
  display: function () {},
};

/**
 * 在闭包外部添加原型属性和方法看上去像脱离了闭包这个类,
 */
var Book = (function () {
  // 静态私有变量
  var bookNum = 0;
  // 静态私有方法
  function checkBook(name) {}
  // 返回构造函数
  function _book(newId, newName, newPrice) {
    // 私有变量
    var name, price;
    // 私有方法
    function checkID(id) {}
    // 特权方法
    this.getName = function () {};
    this.getPrice = function () {};
    this.setName = function () {};
    this.setPrice = function () {};
    // 公有属性
    this.id = newId;
    // 公有方法
    this.copy = function () {};
    bookNum++;
    if (bookNum > 100) {
      throw new Error("我们仅出版 100 本书");
    }
    // 构造器
    this.setName(name);
    this.setPrice(price);
  }
  // 构建原型
  _book.prototype = {
    // 静态公有属性
    isJSBook: false,
    // 静态公有方法
    display: function () {},
  };
  // 返回类
  return _book;
})();

/**
 * 创建对象安全模式
 * 避免忘记 new 关键字造成的问题
 */
// 图书类
var Book = function (title, time, type) {
  if (this instanceof Book) {
    this.title = title;
    this.time = time;
    this.type = type;
  } else {
    return new Book(title, time, type);
  }
};
var book = Book("JavaScript", "2014", "js");

/* 继承 */

/**
 * 类式继承
 */
// 父类
function SuperClass() {
  this.superValue = true;
}
SuperClass.prototype.getSuperValue = function () {
  return this.superValue;
};
// 子类
function SubClass() {
  this.subValue = false;
}
// 继承父类
SubClass.prototype = new SuperClass();
// 为子类添加公有方法
SubClass.prototype.getSubValue = function () {
  return this.subValue;
};
var instance = new SubClass();
console.log(instance.getSuperValue()); // true
console.log(instance.getSubValue()); // false

/**
 * 构造函数继承
 * 解决共有属性中有引用类型,并且相互影响的问题
 * 通过下面的方式不能继承父类的原型上的属性和方法
 */
// 父类
function SuperClass(id) {
  // 引用类型共有属性
  this.books = ["JavaScript", "html", "css"];
  // 值类型共有属性
  this.id = id;
}
SuperClass.prototype.showBooks = function () {
  console.log(this.books);
};
// 子类
function SubClass(id) {
  // 继承父类
  SuperClass.call(this, id);
}
var instance1 = new SubClass(10);
var instance2 = new SubClass(11);
instance1.books.push("设计模式");
console.log(instance1.books); // ["JavaScript", "html", "css", "设计模式"];
console.log(instance1.id); // 10

console.log(instance2, books); // ["JavaScript", "html", "css"]
instance1.showBooks(); // TypeError

/**
 * 组合继承
 * 优势:解决上面两种结构出现的问题
 * 缺陷:使用构造函数继承时执行了一遍父类的构造函数,在实现子类原型的类式继承时又调用了一遍父类构造函数
 */
function SuperClass(name) {
  this.name = name;
  this.books = ["JavaScript", "html", "css"];
}
SuperClass.prototype.getName = function () {
  console.log(this.name);
};
function SubClass(name, time) {
  SuperClass.call(this, name); // 构造函数式继承 name 属性
  this.time = time;
}
SubClass.prototype = new SuperClass(); // 类式继承
SubClass.prototype.getTime = function () {
  console.log(this.time);
};

var instance1 = new SubClass("js book", 2014);
instance1.books.push("设计模式");
console.log(instance1.books); // ["JavaScript", "html", "css", "设计模式"]
instance1.getName(); // js book
instance1.getTime(); // 2014

var instance2 = new SubClass("css book", 2013);
console.log(instance1.books); // ["JavaScript", "html", "css"]
instance1.getName(); // css book
instance1.getTime(); // 2013

/**
 * 原型式继承
 * 跟类式继承一样,父类对象 book 中的值类型被复制,引用类型的属性被共用
 */
function inheritObject(o) {
  // 声明一个过渡函数对象
  function F() {}
  // 过渡函数的原型继承父对象
  F.prototype = o;
  // 返回过渡对象的一个实例,该实例的原型继承了父对象
  return new F();
}

var book = {
  name: "js book",
  alikeBook: ["css book", "html book"],
};

var newBook = inheritObject(book);
newBook.name = "ajax book";
newBook.alikeBook.push("xml book");

var otherBook = inheritObject(book);
otherBook.name = "flash book";
otherBook.alikeBook.push("as book");

console.log(newBook.name); // ajax book
console.log(newBook.alikeBook); // ['css book', 'html book', 'xml book', 'as book']

console.log(otherBook.name); // flash book
console.log(otherBook.alikeBook); // ['css book', 'html book', 'xml book', 'as book']

console.log(book.name); // js book
console.log(book.alikeBook); // ['css book', 'html book', 'xml book', 'as book']

/**
 * 寄生式继承
 */
var book = {
  name: "js book",
  alikeBook: ["css book", "html book"],
};
function createBook(obj) {
  // 通过原生继承方式创建新对象
  var o = new inheritObject(obj);
  // 拓展新对象
  o.getName = function () {
    console.log(name);
  };
  // 返回拓展后的新对象
  return o;
}

/**
 * 寄生组合式继承
 * subClass 子类、superClass 父类
 * 这里仅仅继承父类的原型,不再需要调用父类的构造函数
 */
function inheritPrototype(subClass, superClass) {
  // 复制一份父类的原型副本保存在变量中
  var p = inheritObject(superClass.prototype);
  // 修正因为重写子类原型导致子类的 constructor 属性被修改
  p.constructor = subClass;
  // 设置子类的原型
  subClass.prototype = p;
}

/**
 * 测试代码
 */
// 定义父类
function SuperClass(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
// 定义父类原型方法
SuperClass.prototype.getName = function () {
  console.log(this.name);
};
// 定义子类
function SubClass(name, time) {
  // 构造函数式继承
  SuperClass.call(this, name);
  // 子类新增属性
  this.time = time;
}
// 寄生式继承父类原型
inheritPrototype(SubClass, SuperClass);
// 子类新增原型方法
SubClass.prototype.getTime = function () {
  console.log(this.time);
};
// 创建两个测试方法
var instance1 = new SubClass("js book", 2014);
var instance2 = new SubClass("css book", 2013);
instance1.colors.push("black");
console.log(instance1.colors); // ['red', 'blue', 'green', 'black']
console.log(instance2.colors); // ['red', 'blue', 'green']
instance2.getName(); // css book
instance2.getTime(); // 2013

/**
 * 多继承
 */

// 下面是一个用来继承单对象属性的 extend 方法
// 单继承 属性复制
// 这个 extend 方法是一个浅复制过程,只能复制值类型的属性,对于引用类型的属性它无能为力。
var extend = function (target, source) {
  // 遍历源对象中的属性
  for (var property in source) {
    // 将源对象中的属性复制到目标对象
    target[property] = source[property];
  }
  // 返回目标对象
  return target;
};

// 测试
var book = {
  name: "JavaScript设计模式",
  alike: ["css", "html", "JavaScript"],
};
var anotherBook = {
  color: "blue",
};
extend(anotherBook, book);
console.log(anotherBook.name); // JavaScript设计模式
console.log(anotherBook.alike); // ['css', 'html', 'JavaScript']

anotherBook.alike.push("ajax");
anotherBook.name = "设计模式";

console.log(anotherBook.name); // 设计模式
console.log(anotherBook.alike); // ['css', 'html', 'JavaScript', 'ajax']

console.log(book.name); // JavaScript设计模式
console.log(book.alike); // ['css', 'html', 'JavaScript', 'ajax']

/**
 * 多继承
 * 同时对多个对象进行复制
 */
var mix = function () {
  var i = 1, // 从第二个参数起为被继承的对象
    len = arguments.length, // 获取参数长度
    target = arguments[0], // 第一个对象为目标对象
    arg;
  // 遍历被继承对象
  for (; i < len; i++) {
    arg = arguments[i];
    for (var property in arg) {
      target[property] = arg[property];
    }
  }
  return target;
};

/**
 * 将上面的方法绑定到原生对象 Object 上,这样所有对象就可以拥有这个方法了
 */
Object.prototype.mix = function () {
  var i = 0, // 从第二个参数起为被继承的对象
    len = arguments.length, // 获取参数长度
    arg;
  // 遍历被继承对象
  for (; i < len; i++) {
    arg = arguments[i];
    for (var property in arg) {
      this[property] = arg[property];
    }
  }
  return target;
};

/**
 * 多态
 * 就是同一个方法多种调用方式,根据传入的参数做区分
 */
function add() {
  var arg = arguments,
    len = arg.length;
  switch (len) {
    case 0:
      return 10;
    case 1:
      return 10 + arg[0];
    case 2:
      return arg[0] + arg[1];
  }
}

// 转换形式
function Add() {
  function zero() {
    return 10;
  }
  function one(num) {
    return 10 + num;
  }
  function two(num1, num2) {
    return num1 + num2;
  }
  this.add = function () {
    var arg = arguments,
      len = arg.length;
    switch (len) {
      case 0:
        return zero();
      case 1:
        return one(zrg[0]);
      case 2:
        return two(arg[0], arg[1]);
    }
  };
}
var A = new Add();
A.add();
A.add(5);
A.add(6, 7);

- Book Lists -