这就证明了如果创建两个对象同时也在每个对象中又各自创建了一个函数对象,但是创建两个完成同样任务的Function实例的确没有必要(况且内部有this对象,只要创建一个对象,this便会指向它)。这就造成了内部方法的重复造成资源浪费。
E 解决方法。
如果我们将构造函数内部的方法放到构造函数的外部,那么这个方法便会被person1和person2共享了,于是,在每次创建新对象时就不会同时创建这个方法对象了。如下:
1 2 3 4 5 6 7 8 9 10 11
function Person(name,age,school){ this.name=name; this.age=age; this.school=school; this.sayName=sayName; } <strong>function sayName(){ console.log(this.name); }</strong> var person1=new Person("zzw","21","xjtu"); var person2=new Person("ht","18","tjut");
person1.sayName();//zzw
应当注意:this.sayName=sayName;中这里等式右边的sayName是一个指针,所以在创建新对象的时候只是创建了一个指向共同对像那个的指针而已,并不会创建一个方法对象。这样便解决了问题。 而外面的sayName函数在最后一句中是被对象调用的,所以其中的this同样是指向了对象。
F新的问题
如果这个构造函数中需要的方法很多,那么为了保证能够解决E中的问题,我们需要把所有的方法都写在构造函数之外,可是如果这样:
在全局作用域中定义的函数从未在全局环境中调用,而只会被某个对象调用,这样就让全局作用域有点名不副实。
如果把所有构造函数中的方法都放在构造函数之外,这样就没有封装性可言了。
由此,为了解决F中的问题,接下来不得不提到JavaScript语言中的核心原型模式了。
第四部分:原型模式为什么会出现原型模式呢?这个模式在上面讲了是为了解决自定义构造函数需要将方法放在构造函数之外造成封装性较差的问题。当然它又要解决构造函数能够解决的问题,所以,最终它需要解决以下几个问题。其一:可以直接识别创建的对象的类型。其二:解决工厂模式解决的创建大量相似对象时产生的代码重复的问题。其三:解决构造函数产生的封装性不好的问题。由于这个问题比较复杂,所以我会分为几点循序渐进的做出说明。
A 理解原型对象首先,我们应当知道:无论什么时候,只要创建了一个新函数(函数即对象),就会根据一组特定的规则创建一个函数(对象)的prototype属性(理解为指针),这个属性会指向函数的原型对象(原型对象也是一个对象),但是因为我们不能通过这个新函数访问prototype属性,所以写为[[prototype]]。同时,对于创建这个对象的构造函数也将获得一个prototype属性(理解为指针),同时指向它所创建的函数(对象)所指向的原型对象,这个构造函数是可以直接访问prototype属性的,所以我们可以通过访问它将定义对象实例的信息直接添加到原型对象中。这时原型对象拥有一个constructor属性(理解为指针)指向创建这个对象的构造函数(注意:这个constructor指针不会指向除了构造函数之外的函数)。
你可能会问?所有的函数都是由构造函数创建的吗?答案是肯定的。函数即对象,我在博文《JavaScript函数之美~》中做了详尽介绍。对与函数声明和函数表达式这样建立函数的方法本质上也是由构造函数创建的。
上面的说法可能过于抽象,我们先写出一个例子(这个例子还不是我们最终想要的原型模式,只是为了让大家先理解原型这个概念),再根据代码作出说明:
1 2 3 4 5 6 7 8 9 10 11 12
<strong> function Person(){}</strong> Person.prototype.name="zzw"; Person.prototype.age=21; Person.prototype.school="xjtu"; Person.prototype.sayName=function(){ console.log(this.name); }; var person1=new Person(); var person2=new Person(); person1.sayName();//zzw person2.sayName();//zzw console.log(person1.sayName==person2.sayName);//true