对象和继承(Objects and inheritance)

2018-06-15 18:54 更新

和所有的值类型一样,对象有属性。事实上,你可以将对象当作一组属性的集合,每个属性都是一对(键和值)。键是字符串,值可以是任意JavaScript值。到目前为止,我们仅仅见过键是标识符的属性,因为点操作符处理的键必须为标识符。在这节,你讲见到另一种访问属性的方法,能将任意字符串作为键。

单个对象(Single objects)

在JavaScript中,你可以直接创建对象,通过对象字面量:

var jane = {
    name: 'Jane',


    describe: function () {
        'use strict';
        return 'Person named '+this.name;
    }
};

上面的对象有两个属性:namedescribe。你能读(“get”)和 写(“set”)属性:

> jane.name  // get
'Jane'
> jane.name = 'John';  // set
> jane.newProperty = 'abc';  // 自动创建

属性是函数如 describe 可以被当作方法调用。当调用他们时可以在它们内部通过this引用对象。

> jane.describe()  // 调用方法
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'

in 操作符用来检测一个属性是否存在:

> 'newProperty' in jane
true
> 'foo' in jane
false

若读取一个不存在的属性,将会得到undefined值。因此上面的两个检查也可以像下面这样:

> jane.newProperty !== undefined
true
> jane.foo !== undefined
false

delete操作符用来删除一个属性:

> delete jane.newProperty
true
> 'newProperty' in jane
false

任意键属性(Arbitrary property keys)

属性的键可以是任意字符串。到目前为止,我们看到的对象字面量中的和点操作符后的属性关键字。按这种方法你只能使用标识符。如果你想用其他任意字符串作为键名,你必须在对象字面量里加上引号,并使用方括号获取和设置属性。

> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;

方括号允许你动态计算属性关键字:

> var x = 'name';
> jane[x]
'Jane'
> jane['na'+'me']
'Jane'

引用方法(Extracting methods)

如果你引用一个方法,它将失去和对象的连接。就其本身而言,函数不是方法,其中的this值为undefined(严格模式下)。

> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined

解决办法是使用函数内置的bind()方法。它创建一个新函数,其this值固定为给定的值。

> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'

方法内部的函数(Functions inside a method)

每个函数都有一个特殊变量this。如果你在方法内部嵌入函数是很不方便的,因为你不能从函数中访问方法的this。下面是一个例子,我们调用forEach循环一个数组:

var jane = {
    name: 'Jane',
    friends: [ 'Tarzan', 'Cheeta' ],
    logHiToFriends: function () {
        'use strict';
        this.friends.forEach(function (friend) {
            // 这里的“this”是undefined
            console.log(this.name+' says hi to '+friend);
        });
    }
}

调用 logHiToFriends 会产生错误:

> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined

有两种方法修复这问题。

1:将this存储在不同的变量。

logHiToFriends: function () {
    'use strict';
    var that = this;
    this.friends.forEach(function (friend) {
        console.log(that.name+' says hi to '+friend);
    });
}

2:forEach的第二个参数允许提供this值。

logHiToFriends: function () {
    'use strict';
    this.friends.forEach(function (friend) {
        console.log(this.name+' says hi to '+friend);
    }, this);
}

在JavaScript中函数表达式经常被用作函数参数。时刻小心函数表达式中的this。

构造函数:对象工厂(Constructors: factories for objects)

目前为止,你可能认为JavaScript的对象仅是键值的映射,通过JavaScript对象字面量可以得出这个观点,看起来很像其他语言中的地图/字典(map/dictionary)。然而,JavaScript对象也支持真正意义上的面向对象特性:继承(inheritance)。本节不会完全讲解JavaScript中继承的工作原理,但会给你以此为开始的简单模式。如果你想得到更多知识,请查阅这篇文章“JavaScript inheritance by example”。 除了作为“真正”的函数和方法,函数还在JavaScript中扮演第三种角色:如果通过new操作符调用,他们会变为构造函数,对象的工厂。构造函数是对其他语言中的类的粗略模拟。约定俗成,构造函数的第一个字母大写。例如:

// 设置实例数据
function Point(x, y) {
    this.x = x;
    this.y = y;
}
// 方法
Point.prototype.dist = function () {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

我们看到构造函数分为两部分:首先,Point函数设置实例数据。其次,Point.prototype属性包含对象的方法。前者的数据是每个实例私有的,后面的数据是所有实例共享的。 我们通过new操作符调用Point:

> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301

p是Point的一个实例:

> p instanceof Point
true
> typeof p
'object'

深入阅读

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号