理解对象
对象包含数据属性和访问器属性,通过Object.defineProperty()函数定义
数据属性为设置属性的设置(删除、循环、是否可修改和默认值)
访问器属性为设置属性值的设置(删除、循环、读取和写入的函数)
数据属性
- configurable:能否使用delete删除属性。默认为true
- enumerable:能否使用for-in循环返回属性。默认为true
- writable:能否修改该属性。默认为true
value: 读取写入属性值,默认undefined
var person = {};Object.defineProperty(person, "name", {writable: false,value: "Nicholas"});alert(person.name); //"Nicholas"person.name = "Greg";alert(person.name); //"Nicholas"
访问器属性
configurable:能否使用delete删除属性。默认为true
- enumerable:能否使用for-in循环返回属性。默认为true
- get:读取属性时调用的函数,默认undefined
- set:写入属性时调用的函数,默认undefined
```javascript
var book = {
_year: 2004,
}; // 定义单个属性 Object.defineProperty(book, “year”, { get: function(){edition: 1
}, set: function(newValue){return this._year;
} }); book.year = 2005; alert(book.edition); //2if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; }
//定义多个属性 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”); alert(descriptor.value); //undefined alert(descriptor.enumerable); //false alert(typeof descriptor.get); //“function”
<a name="eY2gw"></a>
## 创建对象
<a name="fV1nN"></a>
### 工厂模式
```javascript
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 person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = 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");
//上述构造函数创建的每一个实例都包含一个不同的function实例
原型模式
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
//省略了参数初始化的环节,所有实例共享原型对象的属性值。实例会修改原型中的值,影响其他实例
组合使用构造函数和原型模式
function Person(name, age, job){
this.name = name; 3 this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
动态原型模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();
稳妥构造函数模式
function Person(name, age, job){
var o = new Object();
//定义私有变量和函数
o.sayName = function(){
alert(name)
};
return o;
}
继承
原型链

var obj = new Object();
// 对象是有原型对象的 原型对象也有原型对象
obj._proto_._proto_._proto_
// 原型对象也有原型对象,对象的原型对象一直往上找,会找到一个null
// 原型链示例
var arr = [];
arr -> Array.prototype ->Object.prototype -> null
var o = new Object();
o -> Object.prototype -> null;

实例对象的proto指向构造函数的prototype
构造函数的constructor指向实例对象
function SuperType(){
this.property = true;
}
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType();
重点:让新实例的原型等于父类的实例。
特点:1、实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!)
缺点:1、新实例无法向父类构造函数传参。
2、继承单一。
3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
构造函数继承
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
特点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。
2、解决了原型链继承缺点1、2、3。
3、可以继承多个构造函数属性(call多个)。
4、在子实例中可向父实例传参。
缺点:1、只能继承父类构造函数的属性。
2、无法实现构造函数的复用。(每次用每次都要重新调用)
3、每个新实例都有父类构造函数的副本,臃肿。
组合继承
function Person(name){
this.name = name;
}
Person.prototype.showName = function(){
console.log(this.name);
}
function Student(name,age){
Person.call(this,name);
this.age = age;
}
Student.prototype = new Person();
Student.prototype.contructor = Student;
Student.prototype.showAge = function(){
console.log(this.age);
}
var stu = new Student('张三',12);
stu.showName();
stu.showAge();
重点:结合了两种模式的优点,传参和复用
特点:1、可以继承父类原型上的属性,可以传参,可复用。
2、每个新实例引入的构造函数属性是私有的。
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
原型式继承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var o = new Object()
var o1 = object(o)
重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。
特点:类似于复制一个对象,用函数来包装。
缺点:1、所有实例都会继承原型上的属性。
2、无法实现复用。(新实例属性都是后面添加的)
寄生式继承
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"
重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。
寄生组合式继承(常用)
寄生:在函数内返回对象然后调用
组合:1、函数的原型等于另一个实例。2、在函数中用apply或者call引入另一个构造函数,可传参
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);
this.age = age;
}
function inherit(fa,son) {
let protoType = Object.create(fa.prototype)
protoType.custructor = son
son.prototype = protoType
}
inherit(SuperType,SubType)
SubType.prototype.sayAge = function(){
alert(this.age);
};
