面向对象的程序设计
什么是对象?
无序属性的集合,其属性可以包含基本值、对象或者函数。
创建对象、添加属性和方法
①通过构造函数
var person = new Object()
person.name = "Nicholas"
person.age = 29
person.job = "Software Engineer"
person.sayName = function(){
alert(this.name)
}
console.log(person)
②对象字面量的方式
var person2 = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName: function () {
alert(this.name)
}
}
属性的类型
1. 数据属性
①[[Configurable]] 表示能否通过 delete 删除属性。
Object.defineProperty(person,'age',{
configurable:false,//不可删除
})
delete person.age //age 属性没被删除
②[[Enumerable]] 表示能否通过 for-in 循环返回属性。
Object.defineProperty(person,'name',{
enumerable:false,//不能被for in 遍历
})
for (const key in person) {
console.log(person[key])
//50
//Software Engineer
//[Function (anonymous)]
}
③[[Writable]] 表示能否修改属性的值。
Object.defineProperty(person,'name',{
writable:false
})
person.name = 'zs'
console.log(person.name)//Nicholas
④[[Value]]:包含这个属性的数据值。
Object.defineProperty(person,'name',{
value:'ls'
})
console.log(person.name)//ls
要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty()方法。这个方法
接收三个参数:属性所在的对象、属性的名字和一个描述符对象。
2. 访问器属性
访问器属性不包含数据值;它们包含一对儿 getter 和 setter 函数。
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);
优雅的创建对象
虽然 Object构造函数或对象字面量都可以用来创建单个对象但这些方式有个明显的缺点:
使用同一个接口创建很多对象,会产生大量的重复代码。
工厂模式
function createPerson(name, age, job) {
var o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function () {
alert(this.name)
}
return o
}
var person11 = createPerson("Nicholas", 29, "Software Engineer")
var person12 = createPerson("Greg", 27, "Doctor")
每次它都会返回一个包含三个属性一个方法的对象。
构造函数模式
function Person(name, age, job){
this.name = name
this.age = age
this.job = job
this.sayName = function(){
alert(this.name)
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer")
var person2 = new Person("Greg", 27, "Doctor")
new Person()发生了什么
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
工厂模式和构造函数模式的区别
创建自定义的构造函数意味着将来可以将它的实标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。
构造函数的问题
构造函数模式虽然好用,但也并非没有缺点。使用构造函数的主要问题,就是每个方法都要在每个
实例上重新创建一遍。即不同实例上的同名函数是不相等的
console.log(person1.sayName === person2.sayName)//false
原型模式
创建的函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,
而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
function Person(){
}
Person.prototype.name = "Nicholas"
Person.prototype.sayName = function(){
console.log(this.name)
}
var person1 = new Person()
person1.sayName()
var person2 = new Person()
person2.sayName()
console.log(person1.sayName == person2.sayName)
console.log(Person.prototype.constructor === Person)
console.log(Person.prototype === person1.__proto__ && Person.prototype === person2.__proto__ )//true
原型模式的缺点
在实例上修改属性和方法会影响其他实例
组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,
但同时又共享着对方法的引用,最大限度地节省了内存。
function Person(name, age, job){
this.name = name
this.age = age
this.job = job
this.friends = ["Shelby", "Court"]
}
Person.prototype = {
constructor : Person,
sayName : function(){
console.log(this.name)
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer")
var person2 = new Person("Greg", 27, "Doctor")
person1.friends.push("Van")
console.log(person1.friends)
console.log(person2.friends)
console.log(person1.friends === person2.friends)
console.log(person1.sayName === person2.sayName)