js创建对象
说到创建对象,我刚开始想到的就是构造函数创建和对象字面量创建,但今天看了书之后,发现创建对象不止这么简单,以下是我的看书后的理解和一些疑惑,在此纪录:
//构造函数 var obj1=new Object(); //对象字面量 var obj={ name:"ellen", age:"21" }
构造函数和对象字面量是常用的方法,但缺点是“使用同一个接口创建很多对象,会产生大量重复代码”(这句话感觉没理解清楚,现在理解为如果要创建几个相似对象,他们都有name,age,job,如果用这两种方法,会一直写name,age,job,100个对象就要写100次...)。
工厂模式
抽象了创建具体对象的过程,用函数来封装以特定接口创建对象
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("kun",33,"老师");
这样创建一个对象时,就已经带有name,age,job属性和sayName方法了。解决了之前会造成对个相似对象问题。但没有“解决识别问
构造函数模式
除了像Object,Array有原生的构造函数,我们也可以创建自定义的构造函数。
function Person(name,age,job) { this.name=name; this.age=age; this.job=job; this.sayName=function(){ alert(this.name); } return o; } var person1=new Person("kun",33,"老师")
与上一个例子相比,这里没有显式创建对象。
1.用new 操作符创建新实例,以这种方式来调用构造函数会经历以下步骤:
(1)创建新对象(2)将构造函数的作用域赋给新对象(3)执行构造函数中的代码,为对象添加属性(4)返回新对象。
2.创建出的对象有一个constructor属性,改属性指向Preson,即:person1.constructor==Person,对象的constructor属性最初就是用来标识对象类型的。
创建自定义的构造函数意味着以后可以将它的实例作为一种特定类型,可以用instanceof来识别,而工厂模式创建的对象就没有解决识别问题。
如果Person函数不用new,那就不是构造函数,和普通函数无异。用new时,this指向的是新对象;不用new时,this指向是调用这个函数的执行环境的对象。
3.但这种方法也会有问题,就是每个实例都要重新创建一遍,就是有多少实例就要执行构造函数内的代码多少遍。
原型模式
我们创建的每个函数都有一个prototype属性,也是原型,这个属性是一个指针,指向一个对象,而这个对象包含了特定类型所有的实例共享的属性和方法,即prototype是调用了构造函数创建出的对象实例的原型对象。原型对象的好处是可以让所有对象的实例共享它所包含的属性和方法。
如上图,构造函数是空的,我们将方法和属性添加到了Person的prototye中了。
1.理解原型对象
我们创建了一个新函数,按特定规则会给函数创建prototype属性,这个属性指向原型对象。原型对象里面有一个constructor属性,这个属性指向Person。
Person的每个实例里有一个指针(内部属性),指向构造函数的原型对象
我们无法访问Person内的内部属性prototype,但是我们可以通过isPrototypeOf()来确定对象是否存在这种关系,会返回布尔值。
我们可以通过对象实例访问保存在原型中的值,但是不能修改它。如果我们添加和原型中的一个属性同名的属性,相当于为实例添加了一个属性,与实例原型无关。而我们在访问时先访问实例属性,如果找到想要的属性就不再访问实例原型,如果没找到就再访问实例原型。
但是原型模式也有不足之处:它省略了为构造函数传递初始化参数这点,所有实例默认情况下都将取得相同属性值,而这些值如果是引用类型,属性值就只是一个指针,所有实例的这个引用值类型属性都共同指向一个数据,如果修改会出问题,而且有时候想修改某些属性也不方便。
组合使用构造函数模式和原型模式
我们可以将构造函数模式和原型模式来解决
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=['Zhang','wang']; } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } } var person1=new Person("Li",29,"student"); var person2=new Person("MO",21,"student"); person1.friends.push('MO'); alert(person1.friends);//Zhang,Wang,Mo alert(person2.friends);//Zhang,Wang
如上,每个实例向friends属性中添加值,不会影响其他实例的friends属性。
动态原型模式
我们看到将一个为实现一个目的的代码独立的写可能会觉得不好,所以我们可以尝试将构造函数与原型用一个函数解决
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=['Zhang','wang']; if(typeof(prototype.sayName)!="function"){ Person.prototype.sayName=function(){ alert(this.name); } } } }
这段代码只有初次调用构造函数时才会执行,如果sayName方法不存在,就会将它添加在原型中,此后,原型已初始化,不需修改。