JS面向对象之创建对象模式
虽然Object构造函数或对象字面量都可以用来创建单个对象,但都有一个缺点,使用同一个接口来创建对象,会产生大量重复的代码,为解决这个问题,引出下列方法
1.工厂模式
抽象了创建具体对象的过程,用函数来封装以特定接口创建对象的细节。比如
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('alice', 29, 'teacher');
var person2 = createPerson('bob', 11, 'student');
解决了创建多个相似对象代码重复的问题,但是没有解决对象识别的问题,无法知道对象的类型
2.构造函数模式
ECMAScript 中的构造函数可以用来创建特定类型的对象,改写之前方法
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
alert(this.name);
}
}
var person1 = new Person('alice', 29, 'teacher');
var person2 = new Person('bob', 11, 'student');
使用new操作符调用构造函数的实际过程如下:
(1)创建一个新对象
(2)将构造函数的作用域赋给新对象(此时this就指向这个新对象)
(3)执行构造函数中代码(为新对象添加属性)
(4)返回新对象
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型
此处解释一下构造函数:
构造函数也是函数!通过new操作符调用就作为构造函数来使用,而没有new,和普通函数没什么两样,同样可以正常调用,例如:
Person('alice', 29, 'teacher'); //当做普通函数调用,添加到window
window.sayName();
// 在另一个对象作用域调用
var o = new Object();
Person.call(o, 'bob', 11, 'student');
o.sayName(); // 'bob'
构造函数模式的问题:
每个方法都要在每个实例上重新创建一遍,如sayName
会导致不同的作用域链和标识符解析
alert(person1.sayName == person2.sayName) // false
可以通过把函数定义转移到构造函数外解决这个问题
写为:
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName () {
alert(this.name);
}
在构造函数内部,我们把sayName属性设置成了全局的sayName函数,由于sayName包含的是一个指向函数的指针,因此
person1 和 person2 对象就共享了在全局作用域中定义的同一个sayName()函数。确实解决了两个函数做同一件事的问题,
新的问题又来了,在全局作用域定义的函数只能被某个对象调用,这让全局作用域有点名不副实。而且如果对象需要定义很多方法,就要定义很多个全局函数,那这个自定义的引用类型就没封装性可言了
3.原型模式
简单来说就是我们创建的每个函数都有一个prototype(原型)属性,该属性是一个指针,指向一个对象,而这个对象的用途是可以包含由特定类型的所有实例共享的属性与方法。好处是让所有对象实例共享它所包含的属性和方法
例如:
function Person () {}
Person.prototype.name = 'bob';
Person.prototype.age = 29;
Person.prototype.job = 'teacher';
Person.prototype.sayName = function() {
alert(this.name)
}
var person1 = new Person();
person1.sayName(); // 'bob'
var person2 = new Person();
person2.sayName() // 'bob'
alert(person1.sayName == person2.sayName) // true
原型对象
无论什么时候,只要创建了一个新函数,就会为该函数创建一个prototype属性;这个属性指向函数的原型对象
默认下,所有原型对象会获得一个constructor属性,指向prototype属性所在函数的指针
Person.prototype.constructor === Person // true
alert(Person.prototype.isPrototypeOf(person1)); // true;
alert(Person.prototype.isPrototypeOf(person2)); // true;
alert(Object.getPrototypeOf(person1) == Person.prototype); // true
alert(Object.getPrototypeOf(person1).name); // 'bob'
每当代码读取某个对象时,都会进行搜索,从实例对象开始,如果在实例中找到了具有给定名字的属性,则返回该属性的值;
如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找给定名字的属性,一直下去..
!虽然可以通过对象实例访问保存在原型中的值,但是不能通过实例对象重写原型中的值
如果我们在实例中添加了与原型中同名的属性值,那么就在实例中创建该属性,会屏蔽原型中那个属性
通过delete操作符删除实例属性,可以让我们重新访问原型中的属性
person1.name = 'alice';
alert(person1.name); // 'alice' 来自实例
alert(person2.name) // 'bob' 来自原型
delete person1.name;
alert(person1.name); // 'bob' 来自原型
我们还可以通过hasOwnProperty()检测一个属性是否存在于实例中,还是存在于原型中
person1.name = 'alice';
alert(person1.hasOwnProperty('name')) // true 来自实例
delete person1.name;
alert(person1.hasOwnProperty('name')) // false 来自原型
in 操作符
(1)单独使用时,in操作符会在通过对象能够访问给定属性时返回true,不管属性存在于实例还是原型中
function hasPrototypeProperty(object,name) {
return !object.hasOwnProperty(name) && (name in object);
}
上述方法返回true 说明属性存在于原型中
(2)在使用for-in循环时,返回的是所有能够通过对象访问的、可枚举的属性,既包括实例中的,也包括原型中的
屏蔽了原型中不可枚举属性的实例属性也会在for-in循环时返回,所有开发人员定义的属性都是可枚举的。
IE中不显示,是一个bug
Object.keys()
要取得对象上所有可枚举的实例属性,可以使用ECMAScript5的Object.keys()方法,接收一个对象为参数,返回一个包含可枚举属性的字符串数组
var p1 = new Person();
p1.name = 'asd';
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); // ['name', 'age']
如果还要取得不可枚举的属性,可以用Object.getOwnPropertyNames()方法
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys) // "constructor,name,age,job,sayName"
重写原型对象
function Person() {
}
Person.prototype = {
name: 'bob';
age: 29;
job: 'teacher';
sayName: function () {
alert(this.name)
}
}
上述将原型对象设置成等于一个以对象字面量形式创建的新对象,最终结果一样
但有一个例外:constructor 不再指向Person了
每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性;这里本质上完全重写了默认的prototype对象,因此constructor属性也变成了新的constructor属性,指向Object构造函数
var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); // true
原型的动态性
由于在原型中查找值的过程是一次搜索,因此我们对原型对象的任何修改都能够立即从实例上反映出来,即使是先创建了实例后修改原型也照样如此
var friend = new Person();
Person.prototype.sayHi = function() {
alert('Hi')
};
friend.sayHi(); 'hi' 因为实例与原型之间的连接不过是一个指针
但如果是重写整个原型对象就不一样了
function Person() {
}
var friend = new Person();
Person.prototype = {
name: 'bob';
age: 29;
job: 'teacher';
sayName: function () {
alert(this.name)
}
}
friend.sayName(); // error
重写原型对象切断了现有原型和任何之前已经存在的对象实例之间的联系;它们的引用仍然是最初的原型
原型对象的问题
由共享的本质导致,对于基本值来说,修改实例属性会覆盖原型属性,
但对于引用类型值的属性就不一样了,如下:
function Person() {
}
Person.prototype = {
constructor: Person,
name: 'bob',
age: 29,
job: 'teacher',
friends: ['a','b'],
sayName: function() {
alert(this.name)
}
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push('Van');
alert(person1.friends);// 'a,b,Van'
alert(person2.friends); // 'a,b,Van'
alert(person1.friends === person2.friends) //true
因为friends数组存在于Perosn.prototype上,所有修改后,person2去调用也是同样的结果,
可是我们希望的是实例一般要有自己的全部属性的,所以也很少有人单独使用原型模式
4.组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ['a', 'b', 'c']
}
Person.prototype = {
constructor: Person,
sayName: function() {
alert(this.name)
}
}
5.动态原型模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
//方法,这里只在sayName方法不存在的情况下加到原型上
if(typeof this.sayName != 'function') {
Person.prototype.sayName = function () {
alert(this.name)
}
}
}
if 语句检查的可以是在初始化之后应该存在的任何属性或方法
6.寄生构造函数模式
function Person(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 friend = new Person('bob',29,'teacher');
friend.sayName() // 'bob'
除了使用new操作符并把使用的包装函数叫做构造函数之外,这其实和工厂模式没有区别。
构造函数在默认情况下,会返回新对象实例,而通过return语句,可以重写返回值
注意: 寄生构造函数模式,返回的对象与构造函数以及其原型属性没有任何关系,不能依赖instanceof操作符来确定对象类型

7.稳妥构造函数模式
特点:
(1)实例方法不引用this
(2)不使用new操作符调用构造函数
function Person(name, age, job) {
var o = new Object();
o.sayName = function () {
alert(name)
};
return o;
}
在这种模式创建的对象里,除了使用sayName()方法外,无法访问name的值,安全性很强
JS面向对象之创建对象模式的更多相关文章
- js面向对象 多种创建对象方法小结
转自js面向对象 多种创建对象方法小结 1.对象字面量 var clock={ hour:12, minute:10, second:10, showTime:function(){ alert(th ...
- js面向对象、创建对象的工厂模式、构造函数模式、原型链模式
JS面向对象编程(转载) 什么是面向对象编程(OOP)?用对象的思想去写代码,就是面向对象编程. 面向对象编程的特点 抽象:抓住核心问题 封装:只能通过对象来访问方法 继承:从已有对象上继承出新的对象 ...
- 浅谈JS面向对象之创建对象
hello,everybody,今天要探讨的问题是JS面向对象,其实面向对象呢呢,一般是在大型项目上会采用,不过了解它对我们理解JS语言有很大的意义. 首先什么是面向对象编程(oop),就是用对象的思 ...
- JS面向对象之工厂模式
js面向对象 什么是对象 "无序属性的集合,其属性可以包括基本值.对象或者函数",对象是一组没有特定顺序的的值.对象的没个属性或方法都有一个俄名字,每个名字都映射到一个值. 简单来 ...
- js面向对象之创建对象
工厂模式 function createPerson(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = ...
- js面向对象编程——创建对象
JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象. 当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找 ...
- JS面向对象设计-创建对象
Object构造函数和对象字面量都可以用来创建单个对象,但是在创建多个对象时,会产生大量重复代码. 1.工厂模式 工厂模式抽象了创建具体对象的过程.由于ECMAScript无法创建类,我们用函数来封装 ...
- JavaScript---正则使用,日期Date的使用,Math的使用,JS面向对象(工厂模式,元模型创建对象,Object添加方法)
JavaScript---正则使用,日期Date的使用,Math的使用,JS面向对象(工厂模式,元模型创建对象,Object添加方法) 一丶正则的用法 创建正则对象: 方式一: var reg=new ...
- js面向对象学习 - 对象概念及创建对象
原文地址:js面向对象学习笔记 一.对象概念 对象是什么?对象是“无序属性的集合,其属性可以包括基本值,对象或者函数”.也就是一组名值对的无序集合. 对象的特性(不可直接访问),也就是属性包含两种,数 ...
随机推荐
- 回到未来123Back To The Future
或许,决定着现在的过去已经无法改变,但决定着未来的现在,却在我们每个人的手里. 路?我们要去的地方不需要路.(Roads? Where we're going we don't need roads) ...
- python 垃圾回收
# 垃圾回收 # 小整数对象池 # a = 100# python对小整数的定义是[-5,257],这些证书对象是提前创建好的,不会被垃圾回收,再一个python的程序中,所有位于这个范围内的正式使用 ...
- error: each element of 'ext_modules' option must be an Extension instance or 2-tuple
在编译cython扩展时出现. 解决办法: 必须先import setup再import extension,否则报错 from setuptools import setup from distut ...
- Nignx添加proxy_pass可能造成DNS解析超时的问题解决
resolver 219.149.194.55; location ^~ /bigdata { proxy_set_header Host $host; ...
- 一脸懵逼学习Struts数据校验以及数据回显,模型驱动,防止表单重复提交的应用。
1:Struts2表单数据校验: (1)前台校验,也称之为客户端校验,主要是通过Javascript编程的方式进行数据的验证. (2)后台校验,也称之为服务器校验,这里指的是使用Struts2通过xm ...
- [转] 用webpack的CommonsChunkPlugin提取公共代码的3种方式
方式一,传入字符串参数 new webpack.optimize.CommonsChunkPlugin(‘common.js’), // 默认会把所有入口节点的公共代码提取出来,生成一个common. ...
- 使用jquery获取父元素或父节点
使用jquery获取父元素或父节点,比较简单,jquery提供了丰富的方法来让我们使用jquery获取父元素或父节点 jquery获取父元素方法比较多,比如parent(),parents(),c ...
- noip2017逛公园
题解: 之前知道正解并没有写过.. #include <bits/stdc++.h> using namespace std; #define rint register int #def ...
- BETA准备
过去存在的问题 文档问题 未能事先做好设计文档,且小程序与后端数据库接口存在问题 界面问题 原型图设计花的时间较少,小程序设计界面仍相对粗糙,前端成员css元素应用经验不足 小程序界面之间对接存在问题 ...
- python全栈开发day61-django简单的出版社网站展示,添加,删除,编辑(单表的增删改查)
day61 django内容回顾: 1. 下载: pip install django==1.11.14 pip install -i 源 django==1.11.14 pycharm 2. 创建项 ...