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 ...
随机推荐
- 多线程与UI操作(二)
为了让程序尽快响应用户操作,在开发Windows应用程序时经常会使用到线程.对于耗时的操作如果不使用线程将会是UI界面长时间处于停滞状态,这种情况是用户非常不愿意看到的,在这种情况下我们希望使用线程来 ...
- 【转载】Stanford CoreNLP Typed Dependencies
总结自Stanford typed dependencies manual 原文链接:http://www.jianshu.com/p/5c461cf096c4 依存关系描述句子中词与词之间的各种语法 ...
- TP5 中的redis 队列
首先我们看一下自己的TP5的框架中的 TP5\vendor\topthink ,这个文件中有没有think-queue这个文件夹,如果没有请安装, 安装这个是要用到Composer的如果没有安装co ...
- zencart简单设置分类链接不同css样式
includes/templates/模板/sideboxes/tpl_categories.php $content .= '<a class="'.$new_style.'&quo ...
- Codeforces Codeforces Round #432 (Div. 2 D ) Arpa and a list of numbers
D. Arpa and a list of numbers time limit per test 2 seconds memory limit per test 256 megabyte ...
- 为了保护dll这么做吗?
生成dll时候 附带生成的lib文件
- 基于SDN4.2.4的neo4j实例
首先添加maven依赖 (1)SND的声明 <dependency> <groupId>org.springframework.data</groupId> < ...
- js获取前几个月的具体日期
// 往前数monthNum月份,不能往后数monthNum getPreMonthDay("2018-12-28",20) // 往前数monthNum月份,不能往后数month ...
- python 异常处理(五)
异常处理&异常基类 1.处理异常 try.....except 语法: 1) try: 放可能会出现问题的代码 except: 处理错误的方式 例如: try: print(ab) #无错执 ...
- 微信小程序data数组push和remove问题
因为在做一个小程序的demo时.由于不向后台请求数据,所以就涉及到对本地数据的操作,现在就做一些数组的增删 //添加新元素 addItemFn: function () { var { lists } ...