内容简介:创建自定义对象最简单的方式就是创建一个**缺点:**红宝书都说这个方案完美了。。。。
创建自定义对象最简单的方式就是创建一个 Object
的实例,然后再为它添加属性和方法,早期的开发人员经常使用这种模式来创建对象,后来对象字面量的方法成了创建对象的首选模式。虽然 object构造函数
或者 对象字面量
的方法都可以用来创建对象,但是这些方法使用同一个接口创建很多对象,会产生大量的重复代码。为了解决这个问题,人们开始使用各种模式来创建对象,在这些模式中,一般推荐使用四种方式,包括 构造函数模式
、 原型模式
、 构造函数和原型组合模式
, 动态原型模式
,其他的方式,包括 工厂模
、 寄生构造函数模
、 稳妥构造函数模式
平时使用的较少。而这些方式中,用的最多最推荐的应是 组合模式和动态原型模式
。下面先介绍下各个模式的优缺点:
-
工厂模式的优点就是确实是解决了创建多个相似对象的问题,但是却没有解决对象识别的问题,即不知道创建实例的是哪一个对象(构造函数),因为所有的实例都是object创造的;
-
构造函数模式最大的优点就是可以通过contructor或者instanceof可以识别对象实例的类别(即它的构造函数),但是每次创建实例时,每个方法都要被创建一次;
-
原型模式的优点是实例的方法是共享的,所有的实例的同一个方法都指向同一个,这样使得每次创建实例时方法不会重新创建,但是这也是缺点,原型模式中所有的属性和方法都共享,这样造成两个问题一是没有办法创建实例自己的属性和方法,二是实例改变属性里的数据时其他实例也会跟着改变,另外它也不能像创建构造函数那样可以传递参数;
-
构造函数和原型组合模式优点就是该共享的共享,该私有的私有,即用构造函数定义对象的所有非函数属性,用原型方式定义方法和共享的属性,结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;这种构造函数和原型混成的模式,是目前在ECMAScript中使用最广泛、认同度最高的一种创建定义类型的方法。
注意组合模式解决了原型模式没有办法传递参数的缺点,也解决了构造函数模式不能共享方法的缺点。
-
动态原型模式是目前极为推荐的一种形式,但是在使用时要注意不能用对象字面量重写原型
-
寄生构造函数模式和工厂模式基本一样,除了多了个new操作符,因此它也不能识别对象,或者说不能识别实例的类别,该方法不推荐使用
-
稳妥构造函数模式适合在一些安全的环境中,稳妥对象是指没有公共属性,而且其方法也不引用this的对象,它也无法识别对象的所属类型,该方法不推荐使用
工厂模式
function Person(name) { var o = new Object(); o.name = name; o.say = function() { alert(this.name); } return o; } var person1 = Person("yawei"); 复制代码
缺点:
- 对象无法识别,所有实例都指向一个原型;无法通过constructor识别对象,因为都是来自Object。
- 每次通过Person创建对象的时候,所有的say方法都是一样的,但是却存储了多次,浪费资源。
构造函数模式
function Person() { this.name = 'hanmeimei'; this.say = function() { alert(this.name) } } var person1 = new Person(); 复制代码
优点:
- 通过constructor或者instanceof可以识别对象实例的类别
- 可以通过new 关键字来创建对象实例,更像OO语言中创建对象实例
缺点:
- 多个实例的say方法都是实现一样的效果,但是却存储了很多次(两个对象实例的say方法是不同的,因为存放的地址不同)
注意:
return this return this
原型模式
function Person() {} Person.prototype.name = 'hanmeimei'; Person.prototype.say = function() { alert(this.name); } Person.prototype.friends = ['lilei']; var person1 = new Person(); 复制代码
优点:
-
say方法是共享的了,所有的实例的say方法都指向同一个。
-
可以动态的添加原型对象的方法和属性,并直接反映在对象实例上。
var person1 = new Person() Person.prototype.showFriends = function() { console.log(this.friends) } person1.showFriends() //['lilei'] 复制代码
缺点:
-
出现引用的情况下会出现问题具体见下面代码:
var person1 = new Person(); var person2 = new Person(); person1.friends.push('xiaoming'); console.log(person2.friends) //['lilei', 'xiaoming'] 复制代码
因为js对引用类型的赋值都是将地址存储在变量中,所以person1和person2的friends属性指向的是同一块存储区域。
-
第一次调用say方法或者name属性的时候会搜索两次,第一次是在实例上寻找say方法,没有找到就去原型对象(Person.prototype)上找say方法,找到后就会在实力上添加这些方法or属性。
-
所有的方法都是共享的,没有办法创建实例自己的属性和方法,也没有办法像构造函数那样传递参数。
注意:
-
优点②中存在一个问题就是直接通过对象字面量给
Person.prototype
进行赋值的时候会导致constructor
改变,所以需要手动设置,其次就是通过对象字面量给Person.prototype
进行赋值,会无法作用在之前创建的对象实例上var person1 = new Person() Person.prototype = { name: 'hanmeimei2', setName: function(name){ this.name = name } } person1.setName() //Uncaught TypeError: person1.set is not a function(…) 复制代码
这是因为对象实例和对象原型直接是通过一个指针链接的,这个指针是一个内部属性[[Prototype]],可以通过
__proto__
访问。我们通过对象字面量修改了Person.prototype指向的地址,然而对象实例的__proto__
,并没有跟着一起更新,所以这就导致,实例还访问着原来的Person.prototype
,所以建议不要通过这种方式去改变Person.prototype
属性
构造函数和原型组合模式
function Person(name) { this.name = name this.friends = ['lilei'] } Person.prototype.say = function() { console.log(this.name) } var person1 = new Person('hanmeimei') person1.say() //hanmeimei 复制代码
优点:
- 解决了原型模式对于引用对象的缺点
- 解决了原型模式没有办法传递参数的缺点
- 解决了构造函数模式不能共享方法的缺点
缺点:
- 和原型模式中注意①一样
动态原型模式
function Person(name) { this.name = name // 检测say 是不是一个函数 // 实际上只在当前第一次时候没有创建的时候在原型上添加sayName方法 //因为构造函数执行时,里面的代码都会执行一遍,而原型有一个就行,不用每次都重复,所以仅在第一执行时生成一个原型,后面执行就不必在生成,所以就不会执行if包裹的函数, //其次为什么不能再使用字面量的写法,我们都知道,使用构造函数其实是把new出来的对象作用域绑定在构造函数上,而字面量的写法,会重新生成一个新对象,就切断了两者的联系! if(typeof this.say != 'function') { Person.prototype.say = function( alert(this.name) } } 复制代码
优点:
- 可以在初次调用构造函数的时候就完成原型对象的修改
- 修改能体现在所有的实例中
**缺点:**红宝书都说这个方案完美了。。。。
注意:
-
只用检查一个在执行后应该存在的方法或者属性就行了 假设除了:say方法外,你还定义了很多其他方法,比如:sayBye、cry、smile等等。此时你只需要把它们都放到对sayName判断的if块里面就可以了。
if (typeof this.sayName != "function") { Person.prototype.sayName = function() {...}; Person.prototype.sayBye = function() {...}; Person.prototype.cry = function() {...}; ... } 复制代码
-
不能用对象字面量修改原型对象
寄生构造函数模式
function Person(name) { var o = new Object() o.name = name o.say = function() { alert(this.name) } return o } var peron1 = new Person('hanmeimei') 复制代码
优点:
- 和工厂模式基本一样,除了多了个new操作符
缺点:
- 和工厂模式一样,不能区分实例的类别
稳妥构造模式
function Person(name) { var o = new Object() o.say = function() { alert(name) } } var person1 = new Person('hanmeimei'); person1.name // undefined person1.say() //hanmeimei 复制代码
优点:
- 安全,那么好像成为了私有变量,只能通过say方法去访问。所谓稳妥对象,指的是没有公共睡醒,而且其方法也不引用this的对象。
缺点:
- 跟工厂模式一样,不能区分实例的类别
以上所述就是小编给大家介绍的《JavaScript对象与创建对象的方式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【9】JavaScript 面向对象高级——对象创建模式
- JS学习笔记(第六章)(面向对象之创建对象)
- JavaScript基础学习——面向对象(对象创建之工厂模式)
- 创建对象、原型、原型链
- Javascript创建对象方式总结
- 从创建对象到 ConcurrentHashMap
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First Design Patterns
Elisabeth Freeman、Eric Freeman、Bert Bates、Kathy Sierra、Elisabeth Robson / O'Reilly Media / 2004-11-1 / USD 49.99
You're not alone. At any given moment, somewhere in the world someone struggles with the same software design problems you have. You know you don't want to reinvent the wheel (or worse, a flat tire),......一起来看看 《Head First Design Patterns》 这本书的介绍吧!