面向对象设计

面向对象的程序设计
面向对象语言有一个标志,那就是都有类的概念,通过类可以创建任意多个相同的属性和方法的对象。
一、理解对象
var person = new Object();
person.name = 'winerss';
person.age = 29;
person.job = 'font-end';
person.sayName = function () {
  console.log(this.name);
}
person.sayName();
对象字面量语法
var person = {
  name: 'winerss',
  age: 43,
  job: 'font-end',
  sayName: function () {
    console.log(this.name);
  }
}
person.sayName();

 

属性类型
var person = {};
Object.defineProperty(person, 'name', {
  writable: false,
  value: 'winerss'
});
console.log(person.name); // winerss
person.name = 'john';
console.log(person.name); // winerss

 

var person = {};
Object.defineProperty(person, 'name', {
  configurable: false,
  value: 'winerss'
});
console.log(person.name); // winerss

 

// 报错
Object.defineProperty(person, 'name', {
  configurable: true,
  value: 'winerss'
});

 

访问器属性
var book = {
  _year: 2004,
  edition: 1
};
Object.defineProperty(book, 'year', {
  get: function () {
    return this._year;
  },
  set: function (newValue) {
    if (newValue > 2004) {
      this._year = newValue;
      this.edition += newValue - 2004;
    }
  }
});

 

book.year = 2005;
console.log(book.edition);

 

var book = {
  _year: 2004,
  edition: 1
};
book.__defineGetter__('year', function () {
  return this._year;
});
book.__defineSetter__('year', function (newValue) {
  if (newValue > 2004) {
    this._year = newValue;
    this.edition += newValue - 2004;
  }
});
book.year = 2005;
console.log(book.edition);

 

定义多个属性
var book = {};
Object.defineProperties(book,{
  _year: {
    value: 2004
  },
  edition: {
    value: 1
  },
  year: {
    get: function () {
      return this._year;
    },
    set: function (newValue) {
      if (newValue > 2004) {
        this._year = newValue;
        this.edition += newValue - 2004;
      }
    }
  }
});

 

读取属性的特性
var book = {};
Object.defineProperties(book,{
  _year: {
    value: 2004
  },
  edition: {
    value: 1
  },
  year: {
    get: function () {
      return this._year;
    },
    set: function (newValue) {
      if (newValue > 2004) {
        this._year = newValue;
        this.edition += newValue - 2004;
      }
    }
  }
});
var descriptor = Object.getOwnPropertyDescriptor(book, '_year');
console.log(descriptor.value); // 2004
console.log(descriptor);

 


二、创建对象
 
1、工厂模式
 
function createPerson(name, age, job) {
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function () {
    console.log(this.name);
  }
  return o;
}
var person1 = createPerson('winerss', 24, 'frontEnd');
person1.sayName(); // winerss
 
工厂模式虽然解决了创建多个相似对象问题,但却没有解决对象识别的问题,随着JavaScript发展,又出现新模式 ==>> 构造函数模式。
 
2、构造函数模式
 
function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function () {
    console.log(this.name);
  }
}
var person2 = new Person("winerss", 24, 'frontEnd');
person2.sayName(); // winerss
 
constructor 构造函数
console.log(person2.constructor == Person); // true
console.log(person2 instanceof Object); // true
console.log(person2 instanceof Person); // true
 
 (1)、将构造函数当作函数
 当作构造函数使用
 
var person3 = new Person("Winerss", 24, 'java');
person3.sayName(); // Winerss
 作为普通函数调用
Person("Alex", 18, 'Doctor');
global.sayName(); // Alex
 在另一个对象的作用域调用
var o = new Object();
Person.call(o, 'Jane', 24, 'Nurse');
o.sayName(); // Jane
 
 (2)、构造函数的问题
 构造函数模式虽然好用,但是缺点就是每个方法都要在每个实例上重新创建一遍。
console.log(test1.sayName == test2.sayName) // false
 如果把sayName函数拿到构造函数外部,test1 test2共享作用域中定义的sayName函数,
 全局作用域的函数只构造函数使用就无全局函数可言,这种也无封装可言了好在原型模式可以解决。
 
3、原型模式
function Person() {}
Person.prototype.name = 'winerss';
Person.prototype.age = 24;
Person.prototype.job = 'Java';
Person.prototype.sayName = function () {
  console.log(this.name);
}
var person1 = new Person();
person1.sayName(); // winerss
var person2 = new Person();
person2.sayName(); // winerss
console.log(person1.sayName === person2.sayName); // true
 
 
(1)、理解原型对象
 
isPrototypeOf()方法
console.log(Person.prototype.isPrototypeOf(person1)); // true
Object.getPrototypeOf()方法,返回这个对象的原型
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
person1.name = 'Jane';
console.log(person1.name); // Jane => 来自实例
console.log(person2.name); // winerss => 来自原型
delete person1.name;
console.log(person1.name); // winerss
// hasOwnProperty()方法可以检测一个属性是存在于实例中。
function Person() {}
Person.prototype.name = "winerss";
Person.prototype.age = 24;
Person.prototype.job = 'javascript';
Person.prototype.sayName = function () {
  console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
console.log(person1.hasOwnProperty("name")); // false
person1.name = "Greg";
console.log(person1.hasOwnProperty("name")); // true
 
 (2)、原型与in操作符
有两种方式使用in操作符:单独使用和在for-in循环使用.
在单独用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例还是原型中。
function Person() {}
Person.prototype.name = "winerss";
var person1 = new Person();
console.log("name" in person1); // true
person1.name = 'Gray';
console.log("name" in person1); // true

function hasPrototypeProperty(object, name) {
  return !object.hasOwnProperty(name) && (name in object);
}

console.log(hasPrototypeProperty(person1, "name")); // false
Object.key()方法,接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
function Person() {}
Person.prototype.name = "winerss";
Person.prototype.age = 24;
var keys = Object.keys(Person.prototype);
console.log(keys); // [ 'name', 'age ]

var person1 = new Person();
person1.name = "Rob";
var person1Keys = Object.keys(person1);
console.log(person1Keys); // [ 'name' ]
Object.getOwnPropertyNames() 获取所有实例属性,无论是否是可枚举。
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys); // [ 'constructor', 'name', 'age' ]
 
(3)、更简单的原型语法
function Person() {}
Person.prototype = {
  name: 'winerss',
  age: 25
}
将Person.prototype设置为等于一个以对象字面量形式创建的新对象。
最终结果相同,但是constructor属性不在指向Person。
var friend = new Person();
console.log(friend instanceof Object); // true
console.log(friend instanceof Person); // true
console.log(friend.constructor === Person); // false
console.log(friend.constructor === Object); // true
 
指向Person
function Person() {}
Person.prototype = {
  constructor: Person,
  name: "winerss"
}
但是重设constructor属性会导致[[Enumerable]] 特性设置为true,原生constructor属性是不可枚举的。
因此使用Object。defineProperty()。
function Person() {}
Person.prototype = {
  name: 'winerss',
  age: 26
}
Object.defineProperty(Person.prototype, "constructor", {
  enumerable: false,
  value: Person
})
(4)、原型的动态性
注意:实例中的指针仅指向原型,而不是指向构造函数.
重写原型对象
function Person() {}
var friend = new Person();
Person.prototype = {
  name: 'winerss',
  age: 24
}
console.log(friend.age); // undefined
 
 

 

4、组合使用构造函数模式和原型模式
function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
}
Person.prototype = {
  constructor: Person,
  sayName: function () {
    console.log(this.name);
  }
}
var person = new Person('winerss', 24, 'java');
person.sayName();

// 5、动态原型模式
function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  if(typeof this.sayName !== 'function') {
    Person.prototype.sayName = function () {
      console.log(this.name);
    }
  }
}
var person = new Person('Jane', 24, 'java');
person.sayName();
 
 
三、继承
1、原型链
function SuperType() {
  this.property = true;
}
SuperType.prototype.getSuperValue = function () {
  return this.property;
}
function SubType() {
  this.subproperty = false;
}
继承SuperType
SubType.prototype = new SuperType();
  SubType.prototype.getSubValue = function () {
  return this.subproperty;
}

var instance = new SubType();
console.log(instance.getSuperValue()); // true
确定原型和实例的关系
console.log(instance instanceof Object); // true
console.log(instance instanceof SubType); // true
console.log(instance instanceof SuperType); //true

console.log(Object.prototype.isPrototypeOf(instance)); // true
console.log(SuperType.prototype.isPrototypeOf(instance)); // true
console.log(SubType.prototype.isPrototypeOf(instance)); //true
谨慎地定义方法
子类型有时需要重写超类型中的某个方法,或者需要添加超类型中不存在方法。
但不管怎样,给原型添加方法的代码一定要放在替换原型的语句之后。
function SuperType() {
  this.property = true;
}
SuperType.prototype.getSuperValue = function () {
  return this.property;
}
function SubType() {
  this.subproperty = false;
}
继承新方法
SubType.prototype = new SuperType();
添加新方法
SubType.prototype.getSubValue = function () {
  return this.subproperty;
}
重写超类型中的方法
SubType.prototype.getSuperValue = function () {
  return false;
}
var instance = new SubType();
console.log(instance.getSuperValue()); // false

原型链的问题
function SuperType() {
  this.colors = ["red", "blue", "green"];
}
function SubType() { }
继承了SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // [ 'red', 'blue', 'green', 'black' ]
var instance2 = new SubType();
console.log(instance2.colors); // [ 'red', 'blue', 'green', 'black' ]

2、借用构造函数
function SuperType() {
  this.colors = ["red", "blue", "green"];
}
function SubType() {
  SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // [ 'red', 'blue', 'green', 'black' ]
var instance2 = new SubType();
console.log(instance2.colors); // [ 'red', 'blue', 'green' ]
传递参数:
相对原型链而言,借用构造函数有一个很大优势,即可在子类型构造函数中像超类型构造函数传递参数。
function SuperType(name) {
  this.name = name;
}
function SubType() {
  继承了 SuperType,同时还传递了参数
  SuperType.call(this, "Nicholas");
  实例属性
  this.age = 29;
}
var instance = new SubType();
console.log(instance.name); //"Nicholas";
console.log(instance.age); //29
借用构造函数的问题:
如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定
义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结
 果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的。

 3、组合继承
function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
  console.log(this.name);
};
function SubType(name, age) {
继承属性
SuperType.call(this, name);
  this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
  console.log(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
console.log(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

4、原型式继承
function object(o) {
function F() { }
  F.prototype = o;
  return new F();
}
var person = {
  name: 'Nicholas',
  friends: ['Shelly', 'Court', 'van']
}
var anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一
个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,
Object.create()与 object()方法的行为相同。
var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相
同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属性
var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person, {
  name: {
    value: "Greg"
  }
});
alert(anotherPerson.name); //"Greg"

5、寄生式继承
function createAnother(original) {
  var clone = object(original); //通过调用函数创建一个新对象
  clone.sayHi = function () { //以某种方式来增强这个对象
    alert("hi");
  };
  return clone; //返回这个对象
}
var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"

// 6、寄生组合式继承
function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
  alert(this.name);
};
function SubType(name, age) {
  SuperType.call(this, name); //第二次调用 SuperType()
  this.age = age;
}
SubType.prototype = new SuperType(); //第一次调用 SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
  alert(this.age);
};
 

posted on 2018-03-20 14:16  winerss  阅读(103)  评论(0编辑  收藏  举报

导航