Javascript对象浅谈——设计思想和如何创建
在javascript中,大多数事物都是对象,从核心的字符串和数组,到建立在Javascript之上的浏览器API。当然,我们也可以自己创建对象,将相关的函数和变量封装打包成便捷的数据容器。这种面向对象(object-oriented,OO)的特性是进一步学习Javascript语言知识必不可少的。下面,我们将详细看一下对象设计的思想和语法,再说明如何创建对象。
1.对象基础
对象是一个包含相关数据和方法的集合(通常由一些变量和函数组成,我们称之为对象里面的属性和方法)。
字面量方法创建一个对象:
var person = {
name : ['Bob', 'Smith'],
age : 32,
gender : 'male',
interests : ['music', 'skiing'],
bio : function() {
alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
},
greeting: function() {
alert('Hi! I\'m ' + this.name[0] + '.');
}
};
1>点表示法
我们可以使用点表示法来访问对象的属性和方法。
对象的名字表现为一个命名空间(namespace),它必须写在第一位——当你想访问对象内部的属性或方法时,然后是一个点(.),紧接着是你想要访问的项目,标识可以是简单属性的名字(name),或者是数组属性的一个子元素,又或者是对象的方法调用。如下所示:
person.age
person.interests[1]
person.bio()
> 子命名空间
可以用一个对象来做另一个对象成员的值。例如将name成员
name : ['Bob', 'Smith'],
改成
name : {
first : 'Bob',
last : 'Smith'
},
这样我们就创建了一个子命名空间,只需要链式的再使用一次点表示法:
person.name.first
person.name.last
2>括号表示法:
另一种访问方式是使用括号表示法:
person['age']
person['name']['first']
3>设置对象成员
通过声明需要设置的成员,可以修改或添加对象的属性值;
括号表示法一个有用的地方是它不仅可以动态的去设置对象成员的值,还可以动态的去设置成员的名字。
这是使用点表示法无法做到的,点表示法只能接受字面量的成员的名字,不接受变量作为名字。
4>"this"的含义
关键字"this"指向了当前代码运行时的对象。
接下来,我们将会了解面对对象编程(OOP)理论,和许多在JavaScript中使用的技巧。
2.面向对象(OOP)
简单来说,最基本的 OOP 思想就是我们想要在程序中使用对象来表示现实世界模型, 并提供一个简单的方式来访问它的功能,否则很难甚至不能实现。
对象可以包含相关的数据和代码,这些代表现实世界模型的一些信息或者功能,或者它特有的一些行为.
对象数据(也经常称为函数) 可以有结构的存储 (官方术语为 封装) 在对象包内 (也可以给一个特殊的名字来表示,有时候也叫做命名空间), 可以使它容易组织和访问; 对象也通常用于存储数据,这样就可以很容易的在网络上传输。
抽象-为了我们编程的目标而利用事物的一些重要特性去把复杂的事物简单化。
多态——这个高大上的词正是用来描述多个对象拥有实现共同方法的能力。
1>构建函数和对象实例
JavaScript 用一种称为构建函数的特殊函数来定义对象和它们的特征。从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的。
一个简单的例子:
首先定义一个构建函数
function createNewPerson(name) {
var obj = {};
obj.name = name;
obj.greeting = function () {
alert('Hi! I\'m ' + this.name + '.');
}
return obj;
}
通过函数来创建一个叫Jim的人:
var salva = createNewPerson('Jim');
salva.name;
salva.greeting();
上面的代码看着有点冗长,我们可以使用便捷的方法来代替:
function Person(name) {
this.name = name;
this.greeting = function() {
alert('Hi! I\'m ' + this.name + '.');
};
}
这个构建函数是Javscript版本的类。 您会发现,它只定义了对象的属性和方法,除了没有明确创建一个对象和返回任何值和之外,它有了您期待的函数所拥有的全部功能。这里使用了this
关键词,即无论是该对象的哪个实例被这个构建函数创建,它的 name
属性就是传递到构建函数形参name
的值,它的 greeting()
方法中也将使用相同的传递到构建函数形参name
的值。
注意:一个构建函数通常是大写字母开头,这样便于区分构建函数和普通函数。
那么如何调用构建函数来创建新的实例呢?
可以使用new关键字
var person1 = new Person('Bob');
var person2 = new Person('Sarah');
您现在看到页面上有两个对象,每一个保存在不同的命名空间里,当您访问它们的属性和方法时,您需要使用person1
或者person2
来调用它们。尽管它们有着相同的name
属性和 greeting()
方法,但是它们是各自独立的,所以相互的功能不会冲突。注意它们使用的是自己的 name 值,这也是使用 this 关键字的原因,它们使用的从实参传入形参的自己的值,而不是其它的什么值。
其他的创建对象的方式还有使用Object( )构造函数。
var person1 = new Object();
JavaScript有个内嵌的方法create()
, 它允许您基于现有对象创建新的对象实例。
var person2 = Object.create(person1);
以上我们讲述了Javascript与经典的OOP的关联和区别,如何使用构造函数实现 javascript 中的类, 以及生成对象实例的不同方法。下面,我们将探讨Javascript对象原型:
3.对象原型
通过原型这种机制,JavaScript 中的对象从其他对象继承功能特性;这种继承机制与经典的面向对象编程语言的继承机制不同。接下来,我们来看一下,原型链如何工作,并了解如何通过prototype属性向已有的构造器添加方法。
JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。
准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的prototype
属性上,而非对象实例本身。
在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的prototype
属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。
Object.getPrototypeOf(new Foobar())
和Foobar.prototype
指向着同一个对象。
1>使用Javascript中的原型
在javascript中,函数可以有属性。 每个函数都有一个特殊的属性叫作原型(prototype)。
2>prototype属性:继承成员被定义的地方
继承的属性和方法是定义在 prototype
属性之上的(你可以称之为子命名空间 (sub namespace) )——那些以 Object.prototype.
开头的属性,而非仅仅以 Object.
开头的属性。prototype
属性的值是一个对象,我们希望被原型链下游的对象继承的属性和方法,都被储存在其中。
于是 Object.prototype.watch()、
Object.prototype.valueOf()
等等成员,适用于任何继承自 Object()
的对象类型,包括使用构造器创建的新的对象实例。
Object.is()
、Object.keys()
,以及其他不在 prototype
对象内的成员,不会被“对象实例”或“继承自 Object()
的对象类型”所继承。这些方法/属性仅能被 Object()
构造器自身使用。
3>constructor属性
每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。
一个小技巧是,你可以在 constructor
属性的末尾添加一对圆括号(括号中包含所需的参数),从而用这个构造器创建另一个对象实例。毕竟构造器是一个函数,故可以通过圆括号调用;只需在前面添加 new
关键字,便能将此函数作为构造器使用。
var person3 = new person1.constructor('Karen', 'Stephenson');
此外,constructor
属性还有其他用途。比如,想要获得某个对象实例的构造器的名字,可以这么用:
instanceName.constructor.name
4>修改原型
objectname.prototype.fall = function(){...}
这种继承模型下,上游对象的方法不会复制到下游的对象实例中;下游对象本身虽然没有定义这些方法,但浏览器会通过上溯原型链、从上游对象中找到它们。这种继承模型提供了一个强大而可扩展的功能系统。
事实上,一种极其常见的对象定义模式是,在构造器(函数体)中定义属性、在 prototype
属性上定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,代码更具可读性。
// 构造器及其属性定义
function Test(a,b,c,d) {
// 属性定义
};
// 定义第一个方法
Test.prototype.x = function () { ... }
// 定义第二个方法
Test.prototype.y = function () { ... }
// 等等……
4.Javascript的继承
在了解了OOJS之后,我们将继续介绍如何创建"子"对象类别(构造器)并从“父”类别中继承功能。
1>原型式的继承
到目前为止我们已经了解了一些关于原型链的实现方式以及成员变量是如何通过它来实现继承,但是之前涉及到的大部分都是浏览器内置函数(比如 String
、Date
、Number
和 Array
),那么我们如何创建一个继承自另一对象的JavaScript对象呢?
在处理面向对象的问题上,JavaScript使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承(通常被称为 原型式继承 —— prototypal inheritance)。
下面通过具体的例子来解释上述概念:
开始,创建一个Person()构造器:
function Person(first, last, age, gender, interests) {
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interests = interests;
};
所有的方法都定义在构造器的原型上,比如:
Person.prototype.greeting = function() {
alert('Hi! I\'m ' + this.name.first + '.');
};
比如我们想要创建一个Teacher
类,就像我们前面在面向对象概念解释时用的那个一样。这个类会继承Person
的所有成员,同时也包括:
1.一个新的属性,subject
——这个属性包含了教师教授的学科。
2.一个被更新的greeting()
方法,这个方法打招呼听起来比一般的greeting()
方法更正式一点。
定义Teacher()构造器函数:
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
call()
函数允许我们调用一个在这个文件里别处定义的函数。第一个参数指明了在您运行这个函数时想对“this
”指定的值,也就是说,您可以重新指定您调用的函数里所有“this
”指向的对象。其他的变量指明了所有目标函数运行时接受的参数。
设置Teacher()的原型和构造器引用
Teacher.prototype = Object.create(Person.prototype);
在这里我们用create()函数来创建一个和Person.prototype
一样的新的原型属性值(这个属性指向一个包括属性和方法的对象),然后将其作为Teacher.prototype
的属性值。这意味着Teacher.prototype
现在会继承Person.prototype
的所有属性和方法。
但是此时的Teacher().prototype.constructor属性指向的是Person(),要解决这个问题,我们需要设置:
Teacher.prototype.constructor = Teacher;
每一个函数对象(Function
)都有一个prototype
属性,并且只有函数对象有prototype
属性,因为prototype
本身就是定义在Function
对象下的属性。当我们输入类似var person1=new Person(...)
来构造对象时,JavaScript实际上参考的是Person.prototype
指向的对象来生成person1
。另一方面,Person()
函数是Person.prototype
的构造函数,也就是说Person===Person.prototype.constructor。
在定义新的构造函数Teacher
时,我们通过function.call
来调用父类的构造函数,但是这样无法自动指定Teacher.prototype
的值,这样Teacher.prototype
就只能包含在构造函数里构造的属性,而没有方法。因此我们利用Object.create()
方法将Person.prototype
作为Teacher.prototype
的原型对象,并改变其构造器指向,使之与Teacher
关联。
任何您想要被继承的方法都应该定义在构造函数的prototype
对象里,并且永远使用父类的prototype
来创造子类的prototype
,这样才不会打乱类继承结构。
最后,向 Teacher() 添加一个新的greeting()函数:
Teacher.prototype.greeting = function() {
var prefix;
if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
prefix = 'Mr.';
} else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
prefix = 'Mrs.';
} else {
prefix = 'Mx.';
}
alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
};
考虑到JavaScript的工作方式,由于原型链等特性的存在,在不同对象之间功能的共享通常被叫做 委托 - 特殊的对象将功能委托给通用的对象类型完成。这也许比将其称之为继承更为贴切,因为“被继承”了的功能并没有被拷贝到正在“进行继承”的对象中,相反它仍存在于通用的对象中。
下面,我们将学习如何运用 JavaScript Object Notation (JSON), 一种使用 JavaScript 对象写的数据传输格式。
5.JSON
JavaScript对象表示法(JSON)是用于将结构化数据表示为JavaScript对象的标准格式,通常用于在网站上表示和传输数据(例如从服务器向客户端发送一些数据,因此可以将其显示在网页上)。您会经常遇到它,所以在本文中,我们向您提供使用JavaScript处理JSON的所有工作,包括访问JSON对象中的数据项并编写自己的JSON。
JSON 是一种按照JavaScript对象语法的数据格式,JSON可以作为一个对象或者字符串存在,前者用于解读 JSON 中的数据,后者用于通过网络传输 JSON 数据。
一个 JSON 对象可以被储存在它自己的文件中,这基本上就是一个文本文件,扩展名为 .json
, 还有 MIME type 用于 application/json。
-
JSON 是一种纯数据格式,它只包含属性,没有方法。
-
JSON 要求有两头的 { } 来使其合法。最安全的写法是有两边的括号,而不是一边。
-
甚至一个错位的逗号或分号就可以导致 JSON 文件出错。您应该小心的检查您想使用的数据(虽然计算机生成的 JSON 很少出错,只要生成程序正常工作)。您可以通过像 JSONLint 的应用程序来检验 JSON。
-
JSON 可以将任何标准合法的 JSON 数据格式化保存,不只是数组和对象。比如,一个单一的字符串或者数字可以是合法的 JSON 对象。虽然不是特别有用处……
-
不像 JavaScript 标识符可以用作属性,在 JSON 中,只有字符串才能用作属性。
json对象和文本之间的转换
-
parse()
: 以文本字符串形式接受JSON对象作为参数,并返回相应的对象。。 -
stringify()
: 接收一个对象作为参数,返回一个对应的JSON字符串。
以上就是Javascript关于面向对象的一些讲述,希望可以帮到大家。