第六章 面向对象的编程
6.2 创建对象
起因:使用Object和字面量使用同一个接口创建很多个对象,会产生大量的代码重复。
基础模式
工厂模式
构造函数模式
原型模式
升级模式(见笔记03)
组合使用构造函数和原型模式
动态原型模式
寄生构造函数模式
稳妥构造函数模式
1.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("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
优点:可多次调用;
缺点:对象类型无法识别;
类型:Object。
1.2 构造函数模式
因原生构造函数,在运行时会自动出现在执行环境中,由此创建自定义的构造函数,从而定义自定义对像类型的属性和方法。
function Person(name, age, job){
this.name = name ;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 =new Person("Nicholas", 29, "Software Engineer");
var person2 =new Person("Greg", 27, "Doctor");
优点:对象类型可识别;
缺点:构造函数中的方法在实例化时重复创建;
类型:Object/Person 实例(可标志为特定类型)。
创建构造函数实例的步骤:
(1):创建一个新对象;
(2):将构造函数的作用域赋给新对象;
(3):执行构造函数中的代码;
(4):返回新对象。
检测对象类型
(1) : constructor;
person1.constructor
(2): instanceof 。
person1 instanceof Object;
person1 instanceof Person;
1.2 原型模式
通过在prototype中定义对象实例的信息,从而让所有对象共享它所包含的方法和属性。
function Person(){
}
Person.prototype.name = "Grey";
Person.prototype.age = "29";
Person.prototype.job = "Doctor";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName();//"Grey"
var person2 = new Person();
person2.sayName();//"Grey"
console.log(person1.sayName == person2.sayName);//true
原型模式工作原理
理解原型对象
函数在创建时自动生成一个prototype属性,该属性指向函数的原型对象。
Person.prototype
原型对象中存在一个constructor属性,该属性包含一个指向 prototype所在函数 的指针。
Person.prototype.constructor == Person
构造函数创建实例后,该实例内部包含一个指针([[Prototype]]),指向构造函数的原型对象。
Object.getPrototypeOf(person1) == Person.prototype;
Person.prototype.isPrototypeOf(person1);
实例函数的原型均指向Person.prototype,与构造函数本身没有直接关系属性读取顺序:
对象实例本身 – >原型对象;
hasOwnProperty()方法,用于检测属性存在于原型还是实例中,存在于实例中返回true;
Object.getOwnPropertyDescriptor()方法,只用于获取实例属性。
- 原型与in 操作符
in | 判断运算符 | prop in objectName (符合则true,范围为整个原型链) | 勿用in判断String,Array,Number等用字面量创建的实例 |
---|---|---|---|
for-in | 循环语句 | for (variable in object)(遍历自定义属性包括原型链上的) | String,Array,Number中少用 |
Object.key() | 方法 | Object.keys(obj)(返回一个所有元素为字符串的数组) | ES6:非对象的参数将被强制转换为一个对象 |
Object.getOwnPropertyNames() | 方法 | Object.getOwnPropertyNames(obj)(返回一个数组含有指定对象可枚举/不可枚举属性的名称) | ES6:非对象的参数将被强制转换为一个对象 |
- 更简单的原型语法
起因:Person.prototype的重复输入
方法:用一个包含所有属性和方法的对象字面量来重写整个原型对象。
function Person (){
}
Person.prototype = {
name : "Grey",
age : 29,
job : "Doctor",
sayName : function (){
alert(this.name);
}
};
var friend = new Person();
alert (friend instanceof Object); //true
alert (friend instanceof Person); //true
alert (friend .constructor == Object); //true
alert (friend .constructor == Person); //false
Person.prototype等于以对象字面量的形式创建了一个新对象;
该对象的constructor属性的指向为Object.可在Person.prototype中重写constructor:Person,
;
constructor的[[Enumerable]]变为true。
原型的动态性
对原型对象所做的任何修改都会反映到实例上;
实例与原型间的连接是一个指针的松散连接;
实例中的指针仅指向原型,而非构造函数。
重写原型对象后,已存在的对象实例仍指向最初的原型,与现有原型没有任何关系。
- 原生对象的原型
原生的引用类型,可以通过原型模式获取和定义新的方法。
String.prototype.startsWith = function(text){
return this.indexOf(text) == 0;
}
var mst = "Hello World";
alert (mst.startsWith('Hello')); //true
//最好不修改原生对象的原型
原型对象的问题
所有实例在默认情况下取得的属性值都相同;
对于引用类型,获取的是地址,当一个实例修改引用类型属性时,其他实例所获取的属性值也会随之发生变化。
(发现写错的地方,请留言,看到会立即修改,谢谢大牛们)