对象:
创建对象的方式:
- 实例化对象:
- 对象字面量:
属性类型:
数据属性:
包含一个数据值的位置。在这个位置可以读取和写入值。
4个描述其行为的特性:
- [ [ Configurable ] ]:表示能否通过delete删除属性,能否修改属性的特性,能否把属性修改为访问器属性。默认为true。
- [ [ Enumerable ] ] :表示能否通过for-in循环返回属性。默认为true。
- [ [ Writable ] ] :表示能否修改属性的值。默认为true。
- [ [ Value ] ] :属性的数据值。默认为undefined。
修改属性默认的特性:
Object.defineProperty( Object , propertyName, obj );
/********************************************
Object: 属性所在的对象,
propertyName: 属性名,
obj: 描述符对象 { Writable : false, ..... }
********************************************/
使用创建新属性时,如果不指定,则
Object.defineProperty()
,
Configurable
,
Enumerable
默认为 false
Writable
访问器属性:
不包含数值。不能直接定义,智能通过
Object.defineProperty()
定义。
- [ [ Configurable ] ]:表示能否通过delete删除属性,能否修改属性的特性,能否把属性修改为访问器属性。
- [ [ Enumerable ] ] :表示能否通过for-in循环返回属性。默认为true。
- [ [ Get ] ] :读取属性时调用的函数。默认为undefined。
- [ [ Set ] ] :在写入属性时调用的函数。默认为undefined。
定义多个属性:
Object.defineProperties( Object , {
propertyName_1: {
writable: ... ,
...
},
propertyName_2: {
writable: ... ,
...
},
....
});
读取属性的特性:
var obj = Object.getOwnPropertyDescriptor( Object , propertyName );
//返回一个对象,如果是数据属性,则对象属性为数据属性的特性,访问器属性同理。
创建对象:
工厂模式:
用函数封装对象:
function createPerson (name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
a.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
// 实例化一个对象
var person_1 = createPerson("Xuchao", 23, "Web Security Engineer");
缺点:没有解决对象识别问题
构造函数模式:
创造自定义的函数构造属性和方法:
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert(this.name);
};
}
// 实例化一个对象
var person_1 = new Person("Xuchao", 23, "Web Security Engineer");
实例化对象后会产生一个(构造函数)属性,该属性指向原型,既
constructor
person_1.constructor = Person
优点:解决工厂模式的缺点。
缺点:每个方法需要在每个实例上重新创建一遍。
原型模式:
让所有对象实例共享原型对象的属性和方法:
function Person () {
Person.prototype.name = "Xuchao";
Person.prototype.age = 23;
Person.prototype.job = "Web Security Engineer";
Person.prototype.sayName = function () {
alert(this.name);
};
}
//实例化一个对象
var person_1 = new Person();
person_1.sayName(); // "Xuchao"
alert(person_1.sayName() == person_2.sayName()); // "true"
理解原型对象:
1、
Obj.prototype.isPrototypeOf( obj_x );
判断obj_x中是否有Obj.prototype的指针
2、
返回obj_x中[ [ prototype ] ]的值
Object.prototype.getPrototypeOf( obj_x );
为实例添加名字相同的属性,会将指针指向实例属性而屏蔽掉原型属性上的值,若想让指针重新指向原型属性,需用
delete
删除实例属性。
Obj.hasOwnPrototype( propertyName )
判断属性值是实例属性还是原型属性
原型与in操作符:
单独使用
in
操作符,可以判断指定属性是否可以通过对象访问到(与实力属性、原型属性无关,只要能访问该属性都返回
true
)
与 hasOwnProperty()
方法结合使用可以判断该属性是存在于实例对象中,还是存在于原型中。
Object.keys( Element )
Object.getOwnPropertyNames( Element )
更简单的原型语法:
通过对象字面量重写原型对象:
function Person(){
}
Person.prototype = {
name: 'Xuchao',
age: 23,
job: 'Web',
sayName: function(){
alert(this.name);
}
};
使用该方法重写原型,将不再指向
constructor
,而是指向
Person
,解决方法如下:
Object
Person.prototype = {
constructor: Person, //解决constructor不指向Person的问题
...
};//此方法会将constructor变为可枚举对象,即[ [ Enumberable ] ]特性被设置为true
原型的动态性:
对原型对象所进行的任何修改都能够立即从实例上反映出来,因为指向原型的是一个指针而不是一个副本
var friend = new Person();
Person.prototype.sayHi = function(){
console.log("Hi");
};
friend.sayHi(); // "Hi"
重写原型对象后,实例指向的依旧是原来的原型对象而非新的原型对象
原型对象的问题:
- 省略了为构造函数传递初始化参数的环节,结果会导致所有的实例具有相同的属性值。
- 原型中所有的属性都是被很多实例共享的,在一个实例中修改原型属性其他实例也同样会修改。
组合使用构造函数模式和原型模式:
构造函数模式用于定义实例属性,原始模式用于定义方法和共享的属性
> 每一个实例都会有一份实例属性的副本,但又同时共享着对方法的引用
寄生构造函数模式:
创建一个函数,封装创建对象的代码,再返回新创建的对象。
function Person (name, age, job) { //一般情况下不建议使用
var o = new Object();
o.name = name;
o.age = age;
a.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
// 实例化一个对象
var person_1 = new Person("Xuchao", 23, "Web Security Engineer");
与工厂模式区别:使用 new
操作符,包装函数叫做构造函数
稳妥构造函数模式:
- 没有公共属性
- 其方法不引用
this对象
与其他模式不同:
- 新建方法不引用
this
- 不使用
操作符调用构造函数new
适合在安全执行环境下使用
继承
利用原型链实现继承。
原型链
一个实例指向原型对象,而原型对象又是另一个原型的实例,层层叠进,构成原型链。
默认原型:
所有的引用类型都继承了
Object
,所有函数都是
Object
的实例。因此默认原型都会包含一个指针指向
Object.prototype
。
原型和实例的关系:
-
操作符instanceof
-
方法isPrototypeOf
原型链定义需要注意的问题:
- 给原型添加方法的代码一定要放在替换原型的语句以后
- 通过原型链事先继承时,不能使用对象字面量创建原型方法。因为这样会重写原型链。
借用构造函数(伪造对象、经典继承)
在子类型构造函数的内部调用超类型构造函数
function SuperType(){
this.colors = ['red','green','blue'];
}
function SubType(){
// 继承了SuperType
SuperType.apply(this);
}
不会共享属性,而是会通过apply()方法产生一个副本
- 可以在子类型构造函数中向超类型构造函数传递参数
function SuperType( name ){
this.name = name;
}
function SubType(){
// 继承了SuperType,同时还传递了参数
SuperType.apply(this, 'Xuchao');
}
- 方法函数无法复用,在超类型原型定义的方法,对子类型也不可见。
组合继承(伪经典继承)
将原型链和借用构造函数的技术结合到一块
function SuperType(name){
// 利用构造函数模式创建原型属性
this.name = name;
}
SuperType.prototype.sayName = function(){
// 利用原型链的方式创建原型方法
console.log(this.name);
}
原型式继承
借助原型中可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
function object(o){
function F(){} // 创建一个临时性的构造函数
F.prototype = o; // 将传入的对象作为这个构造函数的原型
return new F(); // 返回了这个临时类型的一个新实例
} // 其本质是object对传入的对象进行了一次浅复制
ES5通过新增
Object.create()
方法规范了原型式继承:
包含引用类型值得属性始终都会共享相应的值,就像是用原型模式一样
寄生类继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象
function createAnother( func_1 ){
var clone = object( func_1 ); // 通过调用函数创建一个新对象[可以用其他方式]
clone.sayHi = function(){ // 以某种方式来增强这个对象
alert("Hi");
};
return clone; // 返回这个对象
}
寄生组合式继承
通过借用构造函数来继承属性,通过原型链的混合形式来继承方法。
function inheritPrototype( subType, superType ){
var prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 指定对象
}// 只调用一次SuperType构造函数,避免了在SubType.prototype上创建不必要的、多余的属性,且原型链不变