一,理解对象
1,属性类型
1)数据属性(为了表示数据是内部值放在[[]]之内)
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特征,能否把属性修改为访问器属性
[[Enumerable]]:表示能否通过for-in循环返回属性
[[Writable]]:表示能否修改属性的值
[[Value]]:包含这个属性的数据值,默认值为undefined
object.defineProperty(属性所在的对象,属性的名字,描述符对象)
修改属性默认的特性,
el:var person =
{};
Object.defineProperty(person,name,{writable:false;value:zhang;enumerable:false;});
一旦属性定义为不可配置的,就不能再改变回来为可配置的了
2)访问器属性
访问器属性不包含数据值,包含一对getter和setter函数(不是必须的)读取时调用getter函数,写入时调用setter函数
[[Configurable]]:表示能否通过delete删除属性,能否修改属性值,能否把属性修改为数据属性
[[Enumerable]]:表示能否通过for-in循环返回属性
[[Get]]:读取属性时调用的函数,默认为undefined
[[Set]]:写入属性时调用的函数,默认为undefined
访问器属性不能直接调用,必修使用Object.defineProperty()来定义
_属性名:表示只能通过对象方法访问的属性,访问器属性最常见就是设置属性的值,改变其他属性的变化
创建访问器属性使用_defineGetter_()和_defineSetter_(),也可使用Object.defineProperty()
el:var
book = {_year : 2004,edition :
1};
book._defineGetter_("year",function(){return
this._year});
book._defineSetter_("year",function(newValue){if(new Value
> 2004){this._year = newValue;this.edition += newValue -
2004;}});
2,定义多个属性
Object.defineProperties()可以一次性定义多个属性,
接收两个对象参数:要添加和修改其属性的对象,和描述符对象,要与第一个对象要添加和修改的属性相对应
3,读取属性的特性
Object.getOwnPropertyDescriptor(属性所在对象,读取其描述符的属性名称),获取给定属性的描述符,
返回值为一个对象,对于数据属性,返回对象的属性有configuraable,enumerable,writable,value
对于访问器属性,返回对象的属性有configurable,enumerable,get,set
二,创建对象
1,工厂模式
function
createPerson(name,age,job){
var o = new Object();
o.name =
name;
o.age = age;
o.job = job;
o.sayName =
function(){
alert("");
};
return 0;
}
var
person1 =
createPerson("asdg",29,"dasgj");
....
解决了创建多个相似对象的问题,没有解决对象识别的问题
2,构造函数模式
function
Person(name,age,job){
this.name = name;
this.age =
age;
this.job = job;
this.sayName =
function(){};
}
var person1 = new
Person("dasg",20,"sdag");
....
没有显示的创建对象,没有return,将属性和方法直接赋给了this对象
1)将构造函数当做函数
任何函数,只要通过new操作符调用都可以看做构造函数
2)构造函数的问题
每个方法都在每个实例上重新创建一遍
构造函数中的方法可以定义在外面成为全局函数,当需要定义的方法很多的时候通过原型模式了
3,原型模式
每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,包含可以由特定类型的所有实例共享和属性和方法
1)理解原型对象
function
Person(){}
Person.prototype.name = "";
Person.prototype.age =
29;
...
只要创建了一个函数,就会为该函数创建一个prototype属性,指向函数的原型对象
所有的原型对象都会获得一个constructor(构造函数)属性,包含一个指向prototype属性所在函数的指针
实例的内部包含一个指针指向构造函数的原型对象
[[Prototype]]
所有实例都无法访问[[Prototype]] ,通过isPrototypeOf(),
Person.prototype.isPrototypeOf(Person实例)
返回true
Object.getPrototypeOf(实例对象)
返回[[Prototype]]的值(实例所指向的构造函数的原型对象)
hasOwnProperty()
在给定属性存在于对象实例中,返回true,参数传进字符串形式属性名
Object.getOwnPropertyDescript()
在原型对象上调用返回原型属性
2)原型与in操作符
单独使用in操作符,对象能够访问的属性
都返回true,实例和原型都可
for-in
返回所有能够通过对象访问的,可枚举的,屏蔽了原型中不可枚举的([[Enumerable]]标记为false的)实例属性也可以返回
Object.keys()
方法接收一个对象作为参数,返回所有可枚举属性的字符串数组
Object.getOwnPropertyNames()
返回实例的所有属性,包括原型中的属性和不可枚举的属性
3)更简单的原型语法;
function
Person(){}
Person.prototype = {
name : "",
age :
310,
...
};相当于重写原型函数
此时原型对象的constructor属性不指向Person,而是指向Object了
可以指定constructor的值为Person
Object.defineProperty(Person.prototype,"constructor",{enumerable:false,value:Person;});
4)原型的动态性
可以随时为原型添加属性和方法,,能够立即在实例中反映出来
重写原型函数,需要放在实例之前,否则无法访问到构造函数中的属性和方法
重写原型函数,切断了实例和原型之间的联系,实例[[prototype]]仍然指向最初的原型
5)原生对象的原型
可以给原生对象的原型添加属性和方法,也可以修改其属性和方法
6)原型对象的问题
通过在原型上添加一个同名的属性,可以屏蔽原型中的属性
4,组合使用构造函数模式和原型模式
构造函数模式用于定义实例,原型模式用于定义方法和共享属性,结果每个实例都有一份实例属性的副本,又共享方法的引用
改变实例的属性不会影响其他实例的属性
function
Person(name,age,job){
this.name = name;
this.age =
age;
this.job = job;
this.friends =
["",""];
}
Person.prototype = {
constructor :
Person,
sayName :
function(){};
}
定义实例可以分别使用自己的属性和共享方法
5,动态原型模式
function
Person(name,age,job){
this.name = name;
this.age =
age;
this.job = job;
if(typeof this.sayName !=
‘function‘){
Persong.prototype.sayName =
function(){};
}
}
只有在sayName()
方法不存在的时候才能将其初始化,初次调用构造函数的时候才会使用,对原型所做的修改能立刻反映在实例中
6,寄生构造函数模式
function
Person(name,age,job){
var o = new Object();
o.name = name
;
o.age = age;
o.job = job;
o.sayName =
function(){};
return
o;
}
函数中创建对象,并返回该函数
返回的对象与构造函数和构造函数的原型之间没有关系
7,稳妥构造函数模式
遵循与寄生构造函数类似的模型,区别在于创建对象的实例方法不引用this,不使用new操作符调用构造函数
三,继承
1,原型链
1)概念
原型链是实现基础的主要方法,利用原型让一个引用类型继承另一个引用类型的属性和方法
一个对象的原型
= 另个对象的实例
搜索时(实例-原型-继承原型)
2)别忘记默认的原型
所有函数的默认原型都是object的实例,默认原型都都包含一个内部指针,指向object.prototype
3)确定原型和实例的关系
instanceof,只要检测实例与原型链中出现的构造函数,结果就返回true
(实例 instanceof 对象)
isPrototypeOf() ,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型
(实例.prototype.isPrototypeOf(实例))
4)谨慎的定义方法
继承了一个实例后,才能重写其中的方法
使用原型链继承不能使用字面量方法创建原型对象
5)原型链的问题
原型变成了
实例
则实例属性也就变成了原型属性了
创建子类的实例时,不能向超类型的构造函数传递参数
2,借用构造函数
解决原型中包含引用类型值所带来的问题
使用call()
apply() 方法在将来新创建的对象上执行构造函数
1)传递参数
function
SuperType(name){this.name = name; }
function
SubType(){SuperType.call(this,"asdgasd");this.age =
20;}
传递了新的参数
2)借用构造函数的问题
无法避免构造函数模式存在的问题
3,组合继承
将原型链和借用构造函数结合
原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承
function
SuperType(name){this.name = name;this.colors =
["","",""];}
SuperType.prototype.sayName = function(){alert("");};
添加原型函数
function SubType(name,age){SuperType.call(this,name);this.age =
age;} 继承name属性
SubType.prototype = new SuperType();
原型链
SubType.prototype.constructor =
SubType;
SubType.prototype.sayAge = function(){};
instanceof
isPrototypeof()
也用于判断组合继承的对象
缺点:调用两次超类型构造函数,一次是创建子类型原型的时候,二次是子类型构造函数的内部
4,原型式继承
function
object(o){
function F(){}
F.prototype = o;
传人的对象作为临时构造函数的原型
return new F(); 返回临时对象的实例
}
var Person
= {name:"",friend:["","",""]};
var anotherPerson =
object(person);
anotherPerson.name =
"";
anotherPerson.friend.push("");
var yetanotherPerson =
object(person);
yetanotherPerson.name =
"";
yetanotherPerson.friend.push("");
1,Object.create(用作新对象原型的对象,为新对象定义额外属性的对象),规范化了原型继承
var
person = {
name:"Nicholas",friends:["shelby","",""]
};
var
anotherPerson =
Object.create(person,{
name:{
value:"Greg"
}
});
5,寄生式继承
function
createAnother(original){
var clone = object(original);
clone.sayHi
= function(){};
return
clone;
}
6,寄生组合式继承
1,思想:借用构造函数来继承属性,不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非只是超类型的副本而已
本质,使用寄生式继承来继承超类型的原型,将结果指定给子类型的原型
function
inheritPrototype(subType,superType){
var prototype =
object(superType,prototype); 创建对象
prototype.constructor =
subType; 增强对象
subType.prototype = prototype; 指定对象
}
原文:http://www.cnblogs.com/b0xiaoli/p/3620141.html