Object Creation
Although using the object constructor or an object literal are convenient ways to create single objects, there is an obvious downside: creating multiple objects with the same interface requires a lot of code duplication. To solve this problem, developers began using a variation of the factory pattern.
The Factory Pattern
With no way to define classes in ECMAScript, developers created functions to encapsulate the creation of objects with specific interfaces, such as in this example:
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 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
Though this solved the problem of creating multiple similar objects, the factory pattern didn't address the issue of object identification(what type of object an object is).
The Constructor Pattern
constructors in ECMAScript are used to create specific types of objects. For instance, the previous example can be rewritten using the constructor pattern as following:
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");
By convention, constructor functions always begin with an uppercase letter, whereas nonconstructor functions begin with a lowercase letter.
To create a new instance of Person, use the new operator. Calling a constructor in this manner essentially causes the following four steps to be taken:
- Create a new object.
- Assign the this value of the constructor to the new object(so this points to the new object).
- Execute the code inside the constructor (adds properties to the new object).
- Return the new object.
Constructors as Functions
Any function that is called with the new operator acts as a constructor, whereas any function called without it acts just as you would expect a normal function call to act. For instance, the Person() function from the previous example may be called in any of the following ways:
// use as a constructor
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); // "Nicholas" // call as a function
Person("Greg", 27, "Doctor"); // adds to window
window.sayName(); // "Greg" // call in the scope of another object
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); // "Kristen"
Probles with Constructors
The major downside to constructors is that methods are created once for each instance. So, in the previous example, both person1 and person2 have a method called sayName(), but those methods are not the same instance of Function. Remember, functions are objects in ECMAScript, so every time a function is defined, it's actually an object being instantiated. Logically, the constructor actually looks like this:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function("alert(this.name)"); // logical equivalent
}
functions of the same name on different instances are not equivalent, as the following code proves:
alert(person1.sayName === person2.sayName); // false
It's possible to work around this limitation by moving the function definition outside of the constructor, as follow:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
} function sayName(){
alert(this.name);
}
This solves the problem of having duplicate functions that do the same thing but also creates some clutter in the global scope by introducing a function that can realistically be used only in relation to an object.
The Prototype Pattern
The benefit of using the prototype is that all of its properties and methods are shared among object instances.
function Person(){
} Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
}; var person1 = new Person();
person1.sayName(); // "Nicholas" var person2 = new Person();
person2.sayName(); // "Nicholas" alert(person1.sayName === person2.sayName); // true
How Prototypes work
Whenever a function is created, its prototype property is also created according to a specific set of rules. By default, all prototypes automatically get a property called constructor that points back to the function on which it is a property.
Each time the constructor is called to create a new instance, that instance has a internal pointer to the constructor's prototype. In ECMA-262 fifth edition, this is called [[Prototype]]. There is no standard way to access [[Prototype]] form script, but Firefox, Safari and Chrome all support a property on every object called __proto__; in other implementations, this property is completely hidden from script.
Even though [[Prototype]] is not accessible in all implementations, the isPrototypeOf() method can be used to determine if this relationship exists between objects;
alert(Person.prototype.isPrototypeOf(person1)); // true
ECMAScript 5 adds a new method called Object.getPrototypeOf(), which returned the value of [[Prototype]] in all supporting implementations. For example:
alert(Object.getPrototypeOf(person1) == Person.prototype); // true
alert(Object.getPrototypeOf(person1).name); // "Nicholas"
Although it's possible to read values on the prototype from object instances, it is not possible to overwrite them. If you add a property to an instance that has the same name as a property on the prototype, you create the property on the instance, which then masks the property on the prototype.
The delete operator completely removes the instance property and allows the prototype property to be access again as follows:
function Person(){} Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
}; var person1 = new Person();
var person2 = new Person(); person1.name = "Greg"; alert(person1.name); // "Greg" - from instance
alert(person2.name); // "Nicholas" - from prototype delete person1.name;
alert(person1.name); // "Nicholas" - from the prototype
The hasOwnProperty() method determines if a property exists on the instance or on the prototype.
Prototypes and the in Operator
There are two ways to use the in operator: on its own or as a for-in loop;
When used on its own, the in operator returns true when a property of the given name is accessible by the object, which is to say that the property may exist on the instance or on the prototype.
When using a for-in loop, all properties that are accessible by the object and can be enumerated will be returned, which includes properties both on the instance and on the property.
To retrieve a list of all enumerable instance properties on an object, you can use the ECMAScript 5 Object.keys() method, which accepts an Object as its argument and returns an array of strings containing the names of all enumerable properties. For example:
function Person(){} Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
}; var keys = Object.keys(Person.prototype);
alert(keys); // "name, age, job, sayName" var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); // "name, age"
If you'd like a list of all instance properties, whether enumerable or not, you can use Object.getOwnPropertyNames() in the same way:
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys); // "constructor, name, age, job, sayName"
Alternate Prototype Syntax
To limit this redundancy and to better visually encapsulate functionality on the prototype, it has become more common to simply overwrite the prototype with an object literal that contains all of the properties and methods, as in this example:
function Person(){} Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName: function(){
alert(this.name);
}
};
Keep in mind that restoring the constructor in this manner creates a property with [[Enumerable]] set to true. Native constructor properties are not enumerable by default, so if you're using an ECMAScript 5-compliant JavaScript engine, you may wish to use Object.defineProperty() instead;
function Person(){} Person.prototype = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName: function(){
alert(this.name);
}
}; // ECMAScript 5 only - restore the constructor
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
Problems with Prototypes
The prototype pattern isn't without its faults. For one, it negates the ability to pass initialization arguments into the constructor, meaning that all instances get the same property values by default. The main problem comes with their shared nature.
All properties on the prototype are shared among instances, which is ideal for function. Properties that contain primitive values also tend to work well, as shown in the previous example, where it's possible to hide the prototype property by assigning a property of the same name to the instance. The real problem occurs when a property contains a reference value. Consider the following example:
function Person(){} Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
friends: ["Shelby", "Court"],
sayName: function(){
alert(this.name);
}
}; var person1 = new Person();
var person2 = new Person(); person1.friends.push("Van"); alert(person1.friends); // "Shelby, Court, Van"
alert(person2.friends); // "Shelby, Court, Van"
alert(person1.friends === person2.friends); // true
function Person(){} Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
friends: ["Shelby", "Court"],
sayName: function(){
alert(this.name);
}
}; var person1 = new Person();
var person2 = new Person(); person1.friends = ["linxd"]; alert(person1.friends); // "linxd"
alert(person2.friends); // "Shelby, Court"
alert(person1.friends === person2.friends); // false
通过这两段代码的对比,我们可以知道,最开始的person1是不包含friends属性的。当执行person1.friends.push("Van")语句时,JavaScript引擎实际上先搜索friends属性,并且在Person.prototype中找到了该属性。需要注意的是,所有属性值是reference value(包括Array, Function, Object), 他们存储的都只是一个指向对象的指针,而不是对象的一个副本。因此,person1.friends和person2.friends实际上都查询到Person.prototype的同一个属性值。而通过person1.friends.push("Van")改变值后,在person2.friends中也能体现出来。
而在第二段代码中,Person1声明了自己的实例属性friends,这个实例属性覆盖了继承的Person.prototype.friends属性。同时,需要明白的是,Person1.friends的属性值也是一个指向Array对象的指针,并不是Array对象的副本。
Combination Constructor / Prototype Pattern
The most common way of defining custom types is to combine the constructor and prototype patterns. The constructor pattern defines instance properties, whereas the prototype pattern defines methods and shared properties. This pattern allows arguments to be passed into the constructor as well, effectively combining the best parts of each pattern. The previous example can now be rewritten as follows:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
} Person.prototype = {
constructor: Person,
sayName: function(){
alert(this.name);
}
}; var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); // "Shelby, Court, Van"
alert(person2.friends); // "Shelby, Court"
alert(person1.friends === person2.friends); // false
alert(person1.sayName === person2.sayName); // true
Object Creation的更多相关文章
- 《javascript高级程序设计》第六章 Object Creation VS Inheritance
6.1 理解对象 6.1.1 属性类型 6.1.2 定义多个属性 6.1.3 读取属性的特性6.2 创建对象 6.2.1 工厂模式 6.2.2 构造函数模式 6.2.3 原型模式 6.2.4 组合使用 ...
- What is happening in Crockford's object creation technique?
What is happening in Crockford's object creation technique? http://stackoverflow.com/questions/27660 ...
- Error creating bean with name 'com.cloud.feign.interfaces.xxxFeignClient': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalSt.PathVariable annotation was empty on
环境: Spring Cloud:Finchley.M8 Spring Boot:2.0.0.RELEASE 报错信息: Error creating bean with name 'com.clou ...
- Python笔记 #12# Dictionary & Pandas: Object Creation
Document of Dictionaries 10 Minutes to pandas tutorialspoint import pandas as pd data = [['Alex',10] ...
- JavaScript Patterns 3.1 Object Literal
Basic concept Values can be properties: primitives or other objects methods: functions User-defined ...
- JavaScript中Object的总结
基于原型继承,动态对象扩展,闭包,JavaScript已经成为当今世界上最灵活和富有表现力的编程语言之一. 这里有一个很重要的概念需要特别指出:在JavaScript中,包括所有的函数,数组,键值对和 ...
- impdp报错ORA-39083 ORA-02304 Object type TYPE failed to create
环境Red Hat Enterprise Linux Server release 5.8 (Tikanga)ORACLE Release 11.2.0.3.0 Production 我用expdp, ...
- 在开发中到底要不要用var?
var是.net的一个语法糖,在Resharper中推荐都使用这个关键字,平常我也是经常用:但是在跟其他程序员推广使用时,他的一些考虑引发了我的深思,到底该不该使用这个关键字呢? 我使用的理由 我使用 ...
- Design Patterns Simplified - Part 3 (Simple Factory)【设计模式简述--第三部分(简单工厂)】
原文链接:http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part3-factory/ Design ...
随机推荐
- PHP读取txt文件的内容并赋值给数组的代码
使用file_get_contents()获取txt文件的内容,然后通过explode()把获得的字符串转化为数组. 获得数组长度可以使用count()函数 <?php $file = 'key ...
- ubuntu下usb抓包方法步骤--usbmon
开发或者调试USB设备相关的工具或者驱动,一个调试的利器就是usbmon抓包. 在ubuntu下使用步骤如下: 1 运行命令 sudo mount -t debugfs none /sys/ke ...
- 第08课:【实战】Redis网络通信模块源码分析(1)
我们这里先研究redis-server端的网络通信模块.除去Redis本身的业务功能以外,Redis的网络通信模块实现思路和细节非常有代表性.由于网络通信模块的设计也是Linux C++后台开发一个很 ...
- [uboot] (番外篇)global_data介绍(转)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/ooonebook/article/det ...
- 关于LRU算法(转载)
原文地址: http://flychao88.iteye.com/blog/1977653 http://blog.csdn.net/cjfeii/article/details/47259519 ...
- k8sDeployment控制器
简写为deploy,是k8s控制器的另一种实现,它构建于ReplicaSet之上,可为pod和rs资源提供声明式更新. deploy控制器资源的大部分功能均可通过调用rs来实现,同时,还增添了部分特性 ...
- Spring中 aop的 xml配置(简单示例)
示例: aop,即面向切面编程,面向切面编程的目标就是分离关注点. 比如:小明(一位孩子)想吃苹果,首先得要有苹果,其次才能吃.那么妈妈负责去买水果,孩子负责吃,这样,既分离了关注点,也减低了代码的复 ...
- 解决 Maven项目进行编译( mvn compile )时出现的错误
错误信息: 在 pom.xml 文件 设置一下Maven的属性 <!--Maven 属性--> <properties> <!--项目的编码格式--> <pr ...
- sizeof运算符、字节对齐考点(面宝P50)
记住几句话: 结构体的长度一定是最长的数据元素类型的整数倍: 某数据元素的起始地址能被该类型所占的字节数整除: 静态变量是存放在全局数据区,而sizeof计算栈中分配的大小,不包括static变量: ...
- OpenCV笔记(4)(直方图、傅里叶变换、高低通滤波)
一.直方图 用于统计图片中各像素值: # 画一个图像各通道的直方图 def draw_hist(img): color = ('b', 'g', 'r') for i, col in enumerat ...