学习面向对象javascript(四)对象
?
?
?
?
之前,我们已经学习了javascript的基本数据类型,数组和函数。在下面的文章中,我们会了解到一些关于对象的知识。比如如何创建和使用对象,以及对象的构造函数等。
?
从数组到对象我们大家都知道,数组就是一序列元素的组合,每一个元素有他对应的数字索引,从0依次递增。
对象和数组很相似,不同的是对象的索引是由你自己来定义的。索引不局限于用数字来表示,可以用比较好记的关键字来表示。作为对象的属性名。比如:
var hero = { breed: 'Turtle', occupation: 'Ninja'};
?
var o = {prop: 1};var o = {"prop": 1};var o = {'prop': 1};
一般的,为了节约js文件的大小,通常把引号省略了。但是在有些情况下,又必须加:
如果属性的名字是javascript的保留字时,必须加引号;如果属性的名字中包含空格或者(数字、字母、下划线)以外的字符,必须加引号;如果属性名以数字开头时,也必须加引号。总之,如果你使用的属性名不符合javascript中变量名的定义的话,你都必须加引号。比如:
var o = { something: 1, 'yes or no': 'yes', '!@#$%^&*': true};
var dog = { name: 'Benji', talk: function(){ alert('Woof, woof!'); }};
var a = [];a[0] = function(what){ alert(what);};a[0]('Boo!');访问对象的属性
有两种方法可以访问一个对象的属性:
用方括号[],比如hero['occupation']用点号,比如hero.occupation用点号可以方便我们读写,但是如果属性名是不合法的时候,用点号就不行。
我们继续看上面的hero对象:
var hero = { breed: 'Turtle', occupation: 'Ninja'};?
// 用点号访问属性hero.breed; // "Turtle"// 用方括号访问属性hero['occupation']; // "Ninja"// 访问不存在的属性,将会返回undefined'Hair color is ' + hero.hair_color; // "Hair color is undefined"?
var book = { name: 'Catch-22', published: 1961, author: { firstname: 'Joseph', lastname: 'Heller' }};
book.author.firstname; // "Joseph"
或者用方括号
book['author']['lastname']; // "Heller"
也可以混合使用
book.author['lastname']; // "Heller"book['author'].lastname; // "Heller"
var key = 'firstname';book.author[key]; // "Joseph"调用对象的方法
可以说,方法只不过是特殊的属性,所以,你可以像访问属性一样访问方法。
var hero = { breed: 'Turtle', occupation: 'Ninja', say: function() { return 'I am ' + hero.occupation; }}hero.say(); // "I am Ninja"hero['say'](); // "I am Ninja"修改属性和方法
由于javascript是一种动态语言,它允许使用者在任何地方修改对象的属性和方法。包括添加,删除属性等。
我们可以先传进一个空的对象来试试:
var hero = {};
?
然后访问一个不存在的属性:
typeof hero.breed; // "undefined"
添加一些属性和方法在对象中:
hero.breed = 'turtle';hero.name = 'Leonardo';hero.sayName = function() { return hero.name;};
?
试一下调用对象的方法:
hero.sayName(); // "Leonardo"
删除属性:
delete hero.name; // true
然后再次调用刚才的方法:
hero.sayName(); // reference to undefined property hero.name
在前面的例子中,sayName方法使用的是hero.name来访问hero对象的name属性。其实可以使用this关键字来访问。
var hero = { name: 'Rafaelo', sayName: function() { return this.name; }}hero.sayName(); // "Rafaelo"
?所以this在这里表示当前对象。
构造函数创建对象的另一种方法是使用构造函数。
function Hero() { this.occupation = 'Ninja';}
为了创建对象,我们将会使用到new操作符;
var hero = new Hero();hero.occupation; // "Ninja"
使用构造函数的好处是,它可以接受参数。
function Hero(name) { this.name = name; this.occupation = 'Ninja'; this.whoAreYou = function() { return "I'm " + this.name + " and I'm a " + this.occupation; }}var h1 = new Hero('Michelangelo');var h2 = new Hero('Donatello');h1.whoAreYou(); // "I'm Michelangelo and I'm a Ninja"h2.whoAreYou(); // "I'm Donatello and I'm a Ninja"
习惯上,我们会把构造函数的第一个字面大写,以便和其他方法区别开。如果你在调用一个构造方法时,忘记写new操作符,这个不会报错,但是代码不会为你创建一个对象出来。
var h = Hero('Leonardo');typeof h; // "undefined"
这样,Hero只不过是像调用其他方法一样,方法中没有return。所以默认return了undefined并赋值给变量h。
全局对象实际上,环境(比如浏览器)会为我们提供一个全局的对象。我们在代码里声明的全局变量,其实都是这个全局对象的属性。对于浏览器而言,这个全局变量就是window。
你可以定义一个全局变量,然后用各种方式从window上访问试试。
var a = "aaaaa";window.a; // "aaaaa"window['a']; // "aaaaa"
就刚才的那个例子,Hero没有用new操作符。然而Hero方法里又使用了this这个关键字,this表示当前的对象,其实这个时候,当前对象就是这个全局对象。所以,此对象上会有一个name属性。
function Hero(name) { this.name = name;}var h = Hero('Leonardo');name; // "Leonardo"window.name; // "Leonardo"
可以发现,name这个属性加到了window上面了。
所以,只有在使用了new关键字时,才能返回这个new出来的对象给变量h,那么这个时候,this所指的对象就是这个新的对象了。
?
constructor属性其实在我们创建对象是,有一个特殊的属性会做一些幕后的工作。它就是constructor。它是构造方法的一个引用属性,我们也可以用它来创建对象。
function Hero(name) { this.name = name;}var h2 = new Hero('Leonardo');h2.constructor; // Hero(name)
可以看到,它其实就是构造方法。
由于constructor引用的是一个方法,说以我们可以往这个constructor里面传入参数来建立一个新对象。
var h3 = new h2.constructor('Rafaello');h3.name; // "Rafaello"
但是,值得注意的是,如果使用字面方式来定义一个对象的话,那么他的constructor属性将会指向他的内置对象Object的构造方法。
var o = {};o.constructor; // Object()typeof o.constructor; // "function"instanceof
使用instanceof关键字,可以用来检测对象是否是由某个特定的构造方法创建的。
function Hero(){}var h = new Hero();var o = {};h instanceof Hero; // trueh instanceof Object; // trueo instanceof Object; // true返回对象的函数
其实,我们还可以用另外的方式来创建对象,就是在函数中返回对象。
比如下面这个简单的factory方法
function factory(name) { return { name: name };}var o = factory('one');o.name; // "one"o.constructor; // Object()
实际上,我们还可以改变构造函数的默认行为。
通常的:
function C() {this.a = 1;}var c = new C();c.a; // 1
这样一个构造函数,方法最后会返回this这个对象。
function C2() {this.a = 1; return {b: 2};}var c2 = new C2();typeof c2.a; // "undefined"c2.b; // 2
但是现在在构造函数中返回的是另一个对象。注意这里只有当函数返回的是另一个对象是,这个返回this的默认行为才会被改变。
对象的传递当我们复制一个对象或者把对象传入函数的时候,其实他传递的是一个对象的引用。因此,对引用所做的任何改变,都会改变实际的对象。
var original = {howmany: 1};var copy = original;copy.howmany; // 1copy.howmany = 100;original.howmany; // 100
把对象传入函数也是一样的。
var original = {howmany: 100};var nullify = function(o) {o.howmany = 0;}nullify(original);original.howmany; // 0对象的比较
当比较两个对象是否相等时,只有这两个引用的对象是同一个的时候,才返回true。
var fido = {breed: 'dog'};var benji = {breed: 'dog'};benji === fido; // falsebenji == fido; // false
如果定义一个新的变量mydog,然后把其中一个对象benji赋值给他,那么benji就会和mydog指向同一个对象。
var mydog = benji;mydog === benji; // truemydog === fido; // false
?
在下一篇文章中,会对javascript的内置对象做一些说明。