面向对象的程序设计-2-创建对象
该文为阅读高级程序设计(第三本)p144-p164的理解与总结! 接受指导与批评。
对于我,我一直是使用字面量的方式创建对象,然而体系上的创建对象的方法却并不局限于此。
创建对象的方法
- 工厂模式
- 构造函数模式
- 原型模式
- 组合使用构造模式和原型模式
- 动态原型模式
- 寄生构造函数模式
- 稳妥的构造函数模式
1 工厂模式:
定义工厂函数创建并返回包含特定属性的对象,
function createPerson(name, age, job) { var o = new Object(); // var o = {}; //一样 o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name); } return o; } var person0 = createPerson('jody', 22, 'wife');
2 构造函数模式:
先贴出代码
function Person(name) { this.name = name; this.sayName = function() { console.log(this); } } var person1 = new Person('xxx'); var person2 = new Person('bbb'); console.log(person1)
2.1 new Person() 的发生了什么?步骤分解:这是重点(new的工作原理)
- 新建一个对象: instance = new Object();
- 设置原型链: instance.prototype = Person.prototype
- 任意函数在创建的时候都会创建prototype对象,并自动为它添加constructor属性
- 让 Person 中的 this 指向 instance, 然后执行函数:
- 相当于在instance中定义属性(name,sayName)
- 判断 Person 函数的返回 :
- Person 中没有写return,相当于return undefined(值类型),因此返回instance
- 如果返回值类型,则丢弃该值类型, 然后返回 instance。
- 如果返回引用类型,则丢弃instance, 返回该引用类型。
2.2 构造函数也是函数,
它是可以直接执行的,然后给执行作用域绑定属性。
任何函数,只要使用new 操作符,这个函数函数就变成了构造函数
2.3 构造函数的优缺点
1.优点:
person1 instanceof Person person2 instanceof Object //都为ture,用于判断类型(工厂模式不行)
2.缺点:公共属性(如sayName)在每一个Person实例中都创建了,不能做到复用
3 原型模式
function Person() { }; Person.prototype.sayName = function() { console.log(this.name); } Person.prototype.name = 'miaowwwwww'; var person1 = new Person(); var person2 = new Person(); person1.sayName(); console.log(person1)
3.1 什么是原型对象??
当创建一个函数的时候,js根据一组特定的规则自动地为函数创建 prototype 属性 和 一个原型对象。
prototype 属性是一个指针,指向原型对象。
原型对象自动获得一个 constructor(构造函数) 指针属性,该属性指向 被创建的函数。
3.2 实际应用重点:
使用构造函数创建的每一个实例,都自动包含一个[[prototype]] 指针属性。(在浏览器prototype是隐藏的,一些浏览器提供__proto__)。
并且这个属性指向,构造函数的 prototype (即原型对象, 所以每一个实例共享一个原型对象)
3.3 构造函数-原型-实例的关系如图:
3.4 注意:
- 原型对象上的可遍历属属性将会出现在 实例的 for-in 遍历中
- 构造函数 Person.prototype 是一个指针
- 若使用 Person.prototype.sayName = function() {...} 表示在原 原型中添加属性
- 若使用如下方式:表示 改变了 Person.prototype 的指针, 让它指向了一个新的对象,该对象的属性被实例共享,当缺失了constructor属性
Person.prototype = { constructor: Person, // 可手动添加,补全constructor属性 name: 'miaowwwww', sayName: function() { console.log(this.name); } }
3.5 原型模式的优缺点
1.优点:
- 完善构造函数模式的缺点,所有实例可以共享原型的方法,而不需要是个实例创建副本,提高复用性
- 类型判断
person1 instanceOf Person Person.isPrototypeOf(person1) Object.getPrototypeOf(person1) === Person // 都可以正确判断结果
2.缺点:若原型中使用引用类型,则实例可以修改原型中的
function Person() {}; Person.prototype = { constructor: Person, name: 'miaowwwww', friends: ['jody', 'robin'], sayName: function() {} } var person1 = new Person(); var person2 = new Person(); person1.friends.push('mike'); // person2.friends : ['jody', 'robin', 'mike'];
4. 组合构造函数模式与原型模式
function Bird(name) { this.name = name; this.friends = ['mike', 'jody']; } Bird.prototype = { sayName: function() { console.log(this.name); } }
4.1 优点: 结合构造函数模式与原型模式的优点,把引用类型的属性,放到构造函数中,为每一个实例创建副本,同时复用原型上的方法
5.动态原型模式
function Cat(name) { this.name = name; this.friends = ['a', 'b'], if(typeof this.sayName !== 'function') { Cat.prototype.sayName = function() { console.log(this.name); } } }
5.1 解读:就是把第四种模式,变异一下,在判断实例没有 sayName 方法的时候才添加 该方法到 原型上
每次新建对象的时候都会执行判断代码, 然后只有第一次执行, if 才为true
6.寄生构造函数模式
function Book(name) { var o = new Array(); o.name = name; o.sayName = function() { console.log(this.name); } return o; } var book1 = new Book('javascript');
6.1 请回顾上面说过的 2.1 new 的工作原理
6.2 理解:
- 这种方法除了 return 以及 new Book() 之外,跟工厂函数基本没有区别。
- 可以理解为对已有对象的增强(如这里的Array,因为不能直接修改Array对象)
6.3 缺点:
- book1 的原型中不包含 Book, 而是Array, 所以叫寄生
- 与构造函数一样,每一个实例都有 sayName.. 的副本,复用性降低
7 稳妥的构造函数模式
function Fish(_name){ var o = new Object(); // new Person(); 或其他对象 // 私有变量和函数 var name = _name; function sayHi() { console.log('hi') }; // ... // 添加方法 o.sayName = function() { console.log(name); sayHi(); return name; } o.setName = function(value) { name = value; } return o; } var aa = Fish('miaowwwww'); var bb = Fish('jody'); aa.sayName(); bb.sayName();
7.1 这个竟然是平时一直在用的闭包。
7.2 优点:变量私有化,并且只能通过特定接口访问
缺点: 又是每一个实例一个函数副本,复用性降低了;