封装对象之前,我们得知道什么是原型对象。就好比我们吃饭要准备一副碗筷一样的道理。
原型对象
只要创建一个新函数,该函数会自动获得俩个属性,一个prototype属性,这个属性指向函数的原型对象
,原型对象也会有一个consctructor属性,
这俩个属性是我们理解原型继承的关键点所在。
prototype: 函数创建后,这个属性指向函数的原型对象。
constructor: 函数创建后,这个属性存在于函数的原型对象中,该属性的指针指向 prototype所在的构造函数。
自己画了一幅图方便理解。
[Prototype]
调用构造函数创建一个实例后,该实例的内部也包含一个指针叫做[Prototype]。这个连接存在于实例与构造函数的原型对象之间。
function F(name) { this.name = name; this.sayName = function() { console.log("hello"); } }
可以通过
isPrototypeOf()
这个方法来是否存在这种关系。
var t = new F("二狗"); F.prototype.isPrototypeOf(t); //true
也可以用 ES5的一个方法:Object.GetPrototypeOf()
Object.getPrototypeOf(t) === F.prototype //true
这个方法还可以用来访问对象的原型属性以及方法。
F.prototype.sex = "男"; Object.getPrototypeOf(t).sex //男
constructor属性
constructor: 这个属性是共享的, 也就是说每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性
F.prototype.constructor === t.constructor //true
工厂模式
用函数来封装数据,根据接受的参数来构建信息对象。
function person(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function () { conosle.log(this.name); }; return o; } var person1 = person("张三", 25, "产品");
工厂模式优点: 解决了创造多个相似对象的问题。
缺点: 无法识别一个对象的类型。因为最后返回都是一个对象?
构造函数模式
构造函数名首字母应大写,用来别于普通函数;使用new
实例化一个新对象。函数内部用this
绑定新对象。
构造函数模式的缺点 :
每实例化一次,构造函数中的数据就是等于重新拷贝了一份副本给实例对象。
function Person(name, age) { this.name = name; this.age = age; this.job = "前端"; this.getName = function() { console.log(this.name); }; } var person1 = new Person("李四", 25); var person2 = new Person("张三", 25);
对于引用类型的值,就是等于重新创建了一个名字一样,而完全不同的一个对象。
person1.job === person2.job //true person1.getName === person2.getName //false
原型模式
使用原型模式的好处:
可以让所有的对象实例共享它所包含的属性和方法。不必在构造函数中定义对象实例的信息,直接把所有的数据全部放到原型对象中。
function Person () {};
Person.prototype = { name: "张三", age: 25, job: "前端", sayName: function() { console.log(this.name) } };
像这样用一个对象字面形式的对象重写了Person
原型对象之后,原型对象的constructor
属性的指向不再是Person
了。而现在指向的是内建构造函数Object();我们重新赋值给Person
原型对象,是一个匿名对象,匿名对象的原型对象就是最高级的Object.所以这里指向的就是Object()
;
可以通过下面这样的方式手动修正consctructor属性:
Person.prototype = { constructor: Person, name: "张三", //属性
这样我们通过constructor
该属性就能访问正确的值了。
Person.prototype.constructor //Person
原型对象的缺点:
- 实例默认情况下属性值都是相同的。
2.原型对象中引用类型值的修改,会在所有的原型对象中反应出来。
Person.prototype.color = ["red", "blue"];
var person3 = new Person(); var person4 = new Person();
person3.color.push("yellow"); console.log(person3.color) console.log(person4.color) // ["red", "blue", "yellow"] // ["red", "blue", "yellow"]
构造函数与原型混成模式
构造函数用于定义实例的自有的属性(通过传参来设置不同的值),而在原型上定义所有共享的属性和方法。
function Person(name, age) { this.name = name; this.age = age; this.hobby = ["swimming", "games"]; }
Person.prototype = { constructor: Person, job: "前端", getName: function() { console.log(this.name); }; };
var person1 = new Person("张三", 24); var person2 = new Person("李四", 25);
person1.hobby.push("football"); console.log(person1.hobby) //["swimming", "games", "football" ]
console.log(person2.hobby) ["swimming", "games"]
每个实例拥有不一样的hobby
,因为实例中的数组都有各自独立的副本。但是方法又是一样的。
person1.getName === person1.getName //true
稳妥构造函数模式
稳妥对象: 没有公共属性,其方法不引用this
对象, 不使用new
操作符调用构造函数。适合禁止数据被其他应用程序改动时使用。
function Person(name, age) { var o = new Object(); o.name = name; o.age = age; o.getName = function() { console.log(name); }; return o; } var yerson = Yerson("老王", 25); yerson.getName(); //老王
除了调用sayName()
方法外没有别的方式可以访问其数据成员。