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 ...
随机推荐
- Sql Server 常用日期格式
SQL Server中文版的默认的日期字段datetime格式是yyyy-mm-dd Thh:mm:ss.mmm 例如: select getdate() 2004-09-12 11:06:08.17 ...
- html和css牛刀小试
html和css网上教程很多,这里我也给大家一个网址:https://www.cnblogs.com/majj/ 今天心血来潮就模仿着小米的官网写了部分代码,效果图如下:(本人故意加了个华为广告栏在最 ...
- jenkins之Extended Choice Parameter多选插件
- Scale-up and Scale-out(转载)
原地址:http://www.cnblogs.com/spork/archive/2009/12/29/1634766.html 来自原小站,曾经迷糊过的东西,表上来,希望对正在迷糊或即将迷糊的人有帮 ...
- 关于ORACLE的串行化隔离级别--来自ORACLE概念手册
为了描述同时执行的多个事务如何实现数据一致性,数据库研究人员定义了被 称为串行化处理(serializability)的事务隔离模型(transaction isolation model).当所有 ...
- pidstat 命令详解(转载)
转自https://www.jianshu.com/p/3991c0dba094 pidstat 概述 pidstat是sysstat工具的一个命令,用于监控全部或指定进程的cpu.内存.线程.设备I ...
- k8spod探测
一.pod存活性探测 pod spec为容器列表中的相应容器定义其专用的探针即可启用存活性探测,目前,k8s的容器支持存活性探测的方法包含:ExecAction.TCPSocketActon和HTTP ...
- GitLab项目管理实践
群组 / 项目 群组和项目的关系我们可以简单的理解成文件夹和文件的关系.一个群组可以包含一个或多个项目. 使用群组,可以将相关的项目组合在一起,并允许成员同时访问多个项目. 群组也可以嵌套在子组中,建 ...
- DOM 修改与DOM元素
㈠HTML DOM - 修改 修改 HTML = 改变元素.属性.样式和事件. ①创建 HTML 内容 改变元素内容的最简单的方法是使用 innerHTML 属性. 下面的例子改变一个 <p ...
- 直接选择排序(Straight Selection Sort)
1.定义 选择排序(Selection Sort)的基本思想是:每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子文件的最后,直到全部记录排序完毕. 常用的选择排序方法有直接选择排序和堆 ...