哈哈哈万物皆对象,终于到了js的面向对象篇。
一、属性类型
(1)数据属性
数据属性包含一个数据值的位置,在这个位置可以写入和读取数值,数据属性有四个描述器行为的特性
- [[Configurable]]:表示能否通过 delete 删除属性而重新定义属性,默认值是ture
- [[Enumerable]]:表示能否通过 for-in 循环返回该属性,默认值true
- [[Writable]]:表示能否通过修改属性的值,默认值true
- [[Value]]:包含这个属性的数据值,默认值true
要修改默认属性的特性,必须使用ECMAScript的Objedt.defineProperty()方法,这个方法接受三个参数:函数所在对象、属性名字和一个描述对象。例
1 var person = {}; 2 Object.defineProperty(person,"name".{ 3 writable:false, 4 value:"Nicholas" 5 }); 6 alert(person.name); //Nicholas 7 person.name = "Greg"; 8 alert(person.name); //Nicholas
(2)访问器属性
访问器属性包括一对getter和setter函数,在读取访问器属性时,会调用getter函数,这个函数负责返回有效值,写入访问器属性的时候,会调用setter函数并传入新值,这个函数决定如何处理数值,访问器属性有如下4个特性
- [[Configurable]]:能否通过delete删除属性并进行重新定义,默认值时true
- [[Enumerable]]:表示能否通过for-in循环返回属性,默认值时true
- [[Get]]:在读取属性时调用的函数,默认值true
- [[Set]]:在写入属性时调用的函数,默认值true
访问器属性不能直接定义必须使用Obiect.defineProperty()来定义,例
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 } } }); var year = 2005; alert(book.edition); //2
二 创建对象
(1)工厂模式
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 person = createPerson("Greag", 29, "Software Engineer") person.sayName()
每次调用这个createPerson()都能返回一个三个属性一个方法的,工厂模式虽然解决了多个相似对象的问题,但却没有解决对象识别的问题,于是新的模式就出现了
(2)构造函数模式
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name) } }
var person = new Person("Nic", 22, "Software Engineer")
person.sayName()
构造函数虽然好用但并不是没有缺点,就是每个方法都要在实例上创建一遍,因为每定义了一个函数就相当于实例化了一个对象,所以构造函数的定义相当与
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName =new Function( "alert(this.name)" ) // 与声明函数在逻辑上是相等的 }
var person1 = new Person("Greg", 22, "Doctor");
var person2 = new Person("Nic", 22, "Software Engineer");
这样就会导致不同的实例上的同名函数是不同的 alert( person1.sayName == person2.sayName) //false 创建两个完成相同任务的Function完全没有必要,于是又出现了原型模式
(3)原型模式
我们每创建一个函数都会有一个property(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途就是包含可以由特定的类型的所有实例共享的属性和方法,也就是我们不必在构造函数中添加对象的信息,可以直接添加到原型对象上,
//原型模式 function Person(){} Person.prototype.name = "Nic" Person.prototype.age = 22 Person.prototype.sayName = function(){ alert(this.name) } var person1 = new Person(); var person2 = new Person(); alert(person1.sayName == person2.sayName) //true
简洁的原型语法
//简洁的原型语法 function Person(){} Person.prototype = { name:"Nic", age:22, sayName:function(){ alert(this.name) } }
原型模式也不是没有缺点,首先他省略了为构造函数传递初始化参数这一环节,结果所有实例在相同的情况下都取得相同的属性值,原型模式最大的问题就是其共享的本质所导致,对于包含引用属性的来说,问题就很突出。例
function Person(){}; Person.prototype = { constructor: Person, name: "nic", age: 23, friends: ["Court", "shelby"] } var person1 = new Person(); var person2 = new Person(); person1.friends.push("Van") alert(person1.friends) //Court, shelby, Van alert(person2.friends) //Court, shelby, Van alert(person1.friends === person2.friends) //true
我们的初衷往往不是这样,这也是我们很少看见有人直接用原型模式的原因。
(4)组合使用构造函数和原型模式
创建自定义类型最常见的方式就是组合使用构造函数模式和原型模式,构造函数定义实例属性,原型模式定义方法和共享属性,例
//组合构造函数模式和原型模式 function Person(name, age, job){ this.name = name, this.age = age, this.job = job, this.friends = ["Court", "Shelby"] } Person.prototype = { constructor: Person, sayName: function(){ alert(this.name) } } var person1 = new Person("Nic", 23, "Software Engineer") var person2 = new Person("Greg", 22, "Doctor") person1.friends.push("Van") alert(person1.friends) //"Court", "Shelby", "Van" alert(person2.friends) //"Court", "Shelby" alert(person1.friends === person2.friends) //false alert(person1.sayName === person2.sayName) //true