Object.defineProperty的作用
对象是由多个名/值对组成的无序的集合。对象中每个属性对应任意类型的值。
定义对象可以使用构造函数或字面量的形式:
var obj = new Object; //obj = {}
obj.name = "张三"; //添加描述
obj.say = function(){}; //添加行为
除了以上添加属性的方式,还可以使用Object.defineProperty定义新属性或修改原有的属性。
Object.defineProperty(obj, prop, descriptor)
//obj:必需。目标对象
//prop:必需。需定义或修改的属性的名字
//descriptor:必需。目标属性所拥有的特性
//返回值:传入函数的对象。即第一个参数obj
6.2 创建对象
虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。因此人们就开始使用工厂模式的一种变体。
6.2.1 工厂模式
考虑到ECMAScript中无法创建类,开发人员就发明了一种函数,用函数封装以特定接口创建对象的细节,例:
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("kathy",18,"student");
var person2 = createPerson("stephen",19,"student");
函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的person对象,可以无数次的调用函数。
虽然工厂模式解决了创建多个相似对象的问题,但却无法解决识别对象的类型问题。
6.2.2 构造函数模式
ECMAScript中的构造函数可以用来创建特定类型的对象。像Object和Array这样的原生构造函数,在运行的时候会自动出现在执行环境中。
此外也可以创建自定义的构造函数,从而自定义对象类型的属性和方法。
使用构造函数模式,例:
function Person = (name,age,job){//Person()函数取代了createPerson()函数
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var person1 = new Person("kathy",18,"student");
var person2 = new Person("stephen",19,"student");
//person1和person2分别保存着Person的一个不同的实例
//这两个对象都是有一个constructor(构造函数)属性,该属性指向Person
Person()与createPerson()有几处不同的是:
◆ 没有显式的创建对象;
◆ 直接将属性和方法赋给了this对象;
◆ 没有return语句
注意:这里的Person()函数使用的是大写的P。按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则以一个小写字母开头。这个大小写的区分主要是为了区别于ECMAScript中的其他函数。因为构造函数本身也是函数,只不过可以用来创建对象而已。
要创建Person的新实例,必须使用new操作符(必定会经历以下4个步骤)。
① 创建一个新对象;
② 将构造函数的作用于赋给新对象(因此this就指向了这个新对象);
③ 执行构造函数中的代码(为这个新对象添加属性);
④ 返回新对象
对象的constructor属性最初是用来表示对象类型的。
我们在这个例子中创建的所有对象既是Object的实例,同时也是Person的实例。
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。
1、将构造函数当做函数
构造函数和其他函数唯一的区别,就是调用的方式不同。
构造函数:通过new来调用
例:
//当做构造函数使用
var person = new Person ("kathy",18,"student");
person.sayname();//kathy
//作为普通函数调用
Person("kathy",19,"student");
window.sayName();//kathy
//在全局作用域中调用一个函数时,this对象总是指向Global对象。
//在另一个对象的作用域中调用
var o = new Object();
Person.call(o,"kahy",18,"student");
o.sayName();//kathy
2、构造函数的问题
使用构造函数的主要问题:就是每个方法都要在每个实例上重新创建一遍。
ECMAScript中的函数是对象,因此每定义一个函数,也就是实例化了一个对象。
因此从逻辑角度讲,此时的构造函数也可以这样定义:
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=new Function("alert(this.name)")//与声明函数在逻辑上是等价的
}
//以这种方式创建函数会导致不同的作用域和标识符解析,但创建Function新实例的机制仍然是相同的。
因此,不同实例上的同名函数时不相等的。
alert(person1.sayName == person2.sayName);//false
创建两个完成同样任务的Function实例是没有必要的,况且this对象在,根本不用在执行代码钱就把函数绑定在对象上面。因此可以像下面这样,通过函数定义转义到构造函数外部来解决问题:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
//在构造函数内部,将sayName属性设置成等于全局的sayName函数
}
function sayName(){
alert(this.name);
}
var person1 = new Person("kathy",18,"student");
var person2 = new Person("stephen",19,"student");
这样讲函数定义转义到了构造函数的外部,但是有一个新问题,就是在全局作用域中定义的函数实际上只能被某个对象调用。
如果对象需要定义很多方法,那么就要定义很多歌全局函数,于是这个自定义的引用类型就没有封装性可言了。
网友评论