JavaScript 对象的创建
1. Object构造函数
var person = new Object();
person.name = "Brittany";
person.age = 23;
person.job = "web front-end engineer";
person.sayName = function() {
console.log(this.name);
};
person.sayName(); //Brittany
2. 对象字面量模式
var person = {
name: "Brittany",
age: 23,
job: "web front-end engineer",
sayName: function() {
console.log(this.name);
}
};
person.sayName();
虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,可以使用工厂模式的一种变体。
3. 工厂模式
function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
console.log(this.name);
};
return o;
}
var person1 = createPerson("Brittany", 23, "Software Engineer");
var person2 = createPerson("Sam", 26, "Software Engineer");
console.log(typeof person1); //Object
工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。如代码中只能检测出person1为Object类型。随着JavaScript的发展,又一个新模式出现了。
4. 构造函数模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
}
}
var person1 = new Person("Brittany", 23, "Web front-end engineer");
var person2 = new Person("Closure", 26, "Manager");
person1.sayName();
person2.sayName();
console.log(person1.sayName == person2.sayName); //false
使用构造函数的主要问题:每个方法都要在每个实例上重新创建一遍。如代码中所示,person1的sayName和person2的sayName不相等。可以将函数定义转移到构造函数外部来解决。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName() {
console.log(this.name);
}
sayName函数的定义转移到了构造函数外部。而在构造函数内部,我们将sayName属性设置成等于全局的sayName函数。这样一来,由于sayName包含的是一个指向函数的指针,因此person1和person2对象就共享了在全局作用域中定义的同一个sayName()函数。这的确解决了两个函数做同一件事的问题,可是新问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让让人无法接受的是:如果需要定义很多方法,那就要定义很多个全局函数,于是这个自定义的引用类型就无封装性可言。这些问题可通过使用原型模式来解决。
5. 原型模式
1)对象创建
function Person() {}
Person.prototype.name = "Brittany";
Person.prototype.age = 23;
Person.prototype.job = "Web front-end engineer";
Person.prototype.getName = function() {
console.log(this.name);
};
var p1 = new Person();
var p2 = new Person();
console.log(p1.name); //Brittany
console.log(p2.name); //Brittany
console.log(p1.getName == p2.getName); //true
实例中创建的属性会覆盖原型中的同名属性,不能修改原型中的属性。
p1.name = "Susan";
console.log(p1.name); //Susan
hasOwnProperty()检测一个属性是否存在于实例中。
console.log(p2.hasOwnProperty("name")); //false
p2.name = "koko";
console.log(p2.hasOwnProperty("name")); //true
delete p2.name;
console.log(p2.hasOwnProperty("name")); //false
isPropertyOf()
console.log(Person.prototype.isPrototypeOf(p1)); //true
console.log(Person.prototype.isPrototypeOf(p2)); //true
getPropertyOf()
console.log(Object.getPrototypeOf(p1) == Person.prototype); //true
console.log(Object.getPrototypeOf(p1)); //Person
2)原型与in操作符
in单独使用时,通过对象访问到特定属性时返回true,无论该属性存在于实例中还是原型中。hasOwnProperty(),通过对象访问到特定属性时返回true,且该属性存在于实例中。
var p3 = new Person();
console.log("name" in p3); //true
console.log(p3.hasOwnProperty("name")); //false
p3.name = "insist";
console.log(p3.hasOwnProperty("name")); //true
确定属性到底是存在于对象中,还是存在于原型中。如下函数hasPrototypePropery()返回true表示该属性存在于原型中,而不是存在于实例中。
function hasPrototypeProperty(object, name) {
return !hasOwnProperty("name") && (name in object);
}
for..in循环,所有通过对象能够访问的,可枚举的(enumerated)属性,既包括存在于实例中的属性,也包括存在于原型中的属性。
for(var prop in p1) {
console.log(prop); //name age job sayName
}
Object.keys(),ECMAScript5的方法,取得对象上所有可枚举的属性,接收一个对象作为参数,返回值是一个包含所有可枚举属性的字符串数组。注意:Person.prototype也是对象。
var keys = Object.keys(Person.prototype);
console.log(keys); //["name age job sayName"]
var p1 = new Person();
console.log(Object.keys(p1)); //[]
p1.name = "get";
console.log(Object.keys(p1)); //["name"]
Object.getOwnPropertyNames(),得到所有实例属性,无论它是否可枚举。
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys); //["constructor", "name", "age", "job", "getName"]
var keys_p1 = Object.getOwnPropertyNames(p1);
console.log(keys_p1); //[]
3)更简洁的原型语法
function Person() {}
Person.prototype = {
name: "Brittany",
age: 23,
job: "Web front-end engineer",
getName: function() {
return this.name;
}
};
var friend = new Person();
console.log(friend instanceof Person); //true
console.log(friend instanceof Object); //true
console.log(friend.constructor == Person); //false
console.log(friend.constructor == Object); //false
在上面的代码中,将Person.prototype设置为等于一个对象字面量形式创建的新对象,最终结果相同。但有一个例外:constructor属性不再指向Person了。每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。而我们在这里使用的语法,本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。此时尽管instanceof操作符还能返回正确的结果,但通过constructor已经无法确定对象的类型了。
通过如下方式,将constructor手动设置为合适的值。
Person.prototype = {
constructor: Person,
name: "Brittany",
age: 23,
job: "Web front-end engineer",
getName: function() {
console.log(this.name);
}
};
4)原型的动态性
在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来——即使是先创建了实例后修改原型也照样如此。
var friend = new Person();
Person.prototype.sayHi = function() {
console.log("hi");
};
friend.sayHi();
尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,情况就不一样了。
function Person() {}
var friend = new Person();
Person.prototype = {
constructor: Person,
name: "Brittany",
age: 23,
job: "Web front-end engineer",
getName: function() {
console.log(this.name);
}
};
friend.getName(); //error
若是创建实例放在重写原型对象之后,则不会报错。
5)原生对象的原型
所有原生引用类型(Object、Array、String)都在其构造函数的原型上定义了方法,如:Array.prototype.sort()、String.prototype.subString(), 通过原生对象的原型可以取得所有默认方法的引用,并可以定义新方法。
console.log(typeof Array.prototype.sort); //function
console.log(typeof String.prototype.substring); //function String.prototype.startsWith = function(text) {
return this.indexOf(text) == 0;
};
var msg = "Hello World";
console.log(msg.startsWith("Hello")); //true
6)原型对象的问题
缺点一:省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下将取得相同的属性值。
缺点二:原型中所有属性被许多实例共享,这种共享对于函数非常适合。对于包含基本值属性倒也说得过去,因为通过在实例上添加一个同名属性,可以隐藏原型中对应的属性。但对于包含引用类型值得属性来说,问题比较突出。
function Person() { }
Person.prototype = {
constructor: Person,
name: "Brittany",
friends: ["pink", "judy", "sam"],
age: 23,
job: "Web front-end engineer",
getName: function() {
console.log(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("leo");
console.log(person1.friends); //["pink", "judy", "sam", "leo"]
console.log(person2.friends); //["pink", "judy", "sam", "leo"]
console.log(Person.prototype.friends); //["pink", "judy", "sam", "leo"]
person1.age = 35;
console.log(person1.age); //
console.log(person2.age); //
console.log(Person.prototype.age); //
person1的friends属性修改影响了person2的friends,但是person1的age属性修改并未影响person2的age属性。
原因在于:friends数组存在于Person.prototype中而非person1中,因此修改也会通过person2.friends(与person1.friends指向同一个数组)反映出来。而age属性在person1中也存在一份,修改的age属性只是修改person1中的,并不能修改Person.prototype中的age属性。
6. 组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。这样,每个实例都会有自己的一份实例属性的副本,但又同时共享着对方法的引用。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["aa", "bb", "cc"];
}
Person.prototype = {
constructor: Person,
sayName: function() {
console.log(this.name);
}
};
var person1 = new Person("Brittany", 23, "Web front-end Engineer");
person1.friends.push("dd"); //["aa", "bb", "cc", "dd"]
console.log(person1.friends);
var person2 = new Person("Sam", 26, "Web front-end Engineer");
console.log(person2.friends); //["aa", "bb", "cc"]
console.log(person1.friends == person2.friends); //false
console.log(person1.sayName == person2.sayName); //true
时间:2014-10-21
地点:合肥
引用:《JavaScript高级程序设计》
JavaScript 对象的创建的更多相关文章
- JavaScript对象的创建之使用json格式定义
json: javascript simple object notation. json就是js的对象,但是它省去了xml中的标签,而是通过{}来完成对象的说明. 定义对象 var person = ...
- JavaScript对象的创建
原文 简书原文:https://www.jianshu.com/p/6cb1e7b7e379 大纲 前言 1.简单方式创建对象的方法 2.工厂模式创建对象 3.构造函数模式创建对象 4.原型模式创建对 ...
- JavaScript 对象的创建和操作
<script> // 对象是属性的无序集合,每个属性都是一个名/值对. 属性名称是一个字符串. // 对象种类 // 内置对象(nativ ...
- JavaScript对象的创建之基于构造方法+原型方式
为了解决原型所带来的问题,此处需要通过组合构造方法和原型来实现对象的创建,将属性在构造方法中定义,将方法在原型中定义.这种有效集合了两者的优点,是目前最为常用的一种方式. function Perso ...
- javascript 对象的创建与继承模式
针对JS高级程序设计这本书,主要是理解概念,大部分要点源自书内.写这个主要是当个笔记加总结 存在的问题请大家多多指正! 6.1理解对象 创建对象的两个方法(暂时) //第一种,通过创建一个Object ...
- JavaScript对象的创建之基于原型方式
原型内存模型介绍 原型是javascript中非常特殊的一个对象,当一个函数创建之后,会随之就产生一个原型对象. 当通过这个函数的构造函数创建一个具体的对象之后,在这个具体的对象中就会有一个属性指向原 ...
- javascript 对象的创建,引用,释放,删除方法
1.用函数构造 A.声明时同时设置属性和方法 function func(){ this.name = "myname"; this.say = function(){aler ...
- javascript对象的创建--相对java 怎样去创建了"类"i以及实例化对象
由于javascript没有java那么多基本类型,同时也没有提供class这个东西,那么我们想实现javascript的对象创建应该怎么办呢,我简单地从w3c提供的课件中提取了一下几种方法: 一.工 ...
- JavaScript 对象的创建和对6种继承模式的理解和遐想
JS中总共有六种继承模式,包括原型链.借用构造函数.组合继承.原型式继承寄生式继承和寄生组合式继承.为了便于理解记忆,我遐想了一个过程,对6中模式进行了简单的阐述. 很长的一个故事,姑且起个名字叫 ...
随机推荐
- 关于引用JS和CSS刷新浏览器缓存问题
有时候我们会碰到上线的新版本都要刷新一次缓存的问题.那是因为改了JS的内容,但是JSP引用的地方后面的字符串未发生改变导致浏览器读取浏览器缓存而不会重新加载新的JS内容,以下提供两种解决方式: 1.每 ...
- 如何在IamgeButton上面添加文字
如何在IamgeButton上面添加文字? 首先要知道,IamgeButton是不可以直接添加文字的.所以我们需要间接制作一个Button按钮 我的代码将会展示另外一个例子,与本文中的代码相似. 本文 ...
- iphone中 input圆角bug
今天写了个简单的登录注册,在电脑手机(除了iphone)样式都没有问题,但在iphone中却出现了异常,提交的按钮变成圆角被背景渐变的效果,随后又测试两个iphone版都是一个样,断定应该是safar ...
- 【转】linux下安装ssh服务器端及ssh的安全配置
一.在服务器上安装ssh的服务器端. $ sudo apt-get install openssh-server 2. 启动ssh-server. $ /etc/init.d/sshrestart 3 ...
- zoj3806Incircle and Circumcircle
链接 自己的本本没有装画图软件,先借用两张图片..博客园不让贴源地址... 可以想到对于一个确定的外接圆的三角形来说内切圆最大的时候为等边三角形,如下图: 确定有合法的解之后,接下来就是去找这个解,解 ...
- springcloud(第三篇)springcloud eureka 服务注册与发现 *****
http://blog.csdn.net/liaokailin/article/details/51314001 ******************************************* ...
- git clone --early EOF
出现这个问题可能需要重新检查以下方面: 1. Android studio Git 的安装地址: ..../Git/cmd/git.exe 记得在环境变量 --Path 中进行配置: ,..../G ...
- maven引入本地jar
mvn install:install-file -Dfile=***.jar -DgroupId=**.***.** -DartifactId=* -Dversion=0.8.11 -Dpackag ...
- Ruby--学习记录(实时更新)
变量的命名方式决定了变量的种类: 局部变量 以英文字母或者_开头: 全局变量 以$开头: 实例变量 以@开头: 类变量 以@@开头:
- Tomcat APR & Linux Optimization
一.简介 APR(Apache portable Run-time libraries)模式:简单理解,就是从操作系统级别解决异步IO问题,大幅度的提高服务器的处理和响应性能, 也是Tomcat运行高 ...