天天看点

js创建对象| 25

JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。

当我们用​

​obj.xxx​

​访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到​

​Object.prototype​

​对象,最后,如果还没有找到,就只能返回​

​undefined​

​。

例如,创建一个​

​Array​

​对象:

var arr = [1, 2, 3];      

其原型链是:

arr ----> Array.prototype ----> Object.prototype ----> null      

​Array.prototype​

​定义了​

​indexOf()​

​、​

​shift()​

​等方法,因此你可以在所有的​

​Array​

​对象上直接调用这些方法。

当我们创建一个函数时:

function foo()
    return 0;
}      

函数也是一个对象,它的原型链是:

foo ----> Function.prototype ----> Object.prototype ----> null      

由于​

​Function.prototype​

​定义了​

​apply()​

​等方法,因此,所有函数都可以调用​

​apply()​

​方法。

很容易想到,如果原型链很长,那么访问一个对象的属性就会因为花更多的时间查找而变得更慢,因此要注意不要把原型链搞得太长。

构造函数

除了直接用​

​{ ... }​

​创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数:

function Student(name)
    this.name = name;
    this.hello = function ()
        alert('Hello, ' + this.name + '!');
    }
}      

你会问,咦,这不是一个普通函数吗?

这确实是一个普通函数,但是在JavaScript中,可以用关键字​

​new​

​来调用这个函数,并返回一个对象:

var xiaoming = new Student('小明');
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!      

注意,如果不写​

​new​

​,这就是一个普通函数,它返回​

​undefined​

​。但是,如果写了​

​new​

​,它就变成了一个构造函数,它绑定的​

​this​

​指向新创建的对象,并默认返回​

​this​

​,也就是说,不需要在最后写​

​return this;​

​。

新创建的​

​xiaoming​

​的原型链是:

xiaoming ----> Student.prototype ----> Object.prototype ----> null      

也就是说,​

​xiaoming​

​的原型指向函数​

​Student​

​的原型。如果你又创建了​

​xiaohong​

​、​

​xiaojun​

​,那么这些对象的原型与​

​xiaoming​

​是一样的:

xiaoming ↘
xiaohong -→ Student.prototype ----> Object.prototype ----> null      

用​

​new Student()​

​创建的对象还从原型上获得了一个​

​constructor​

​属性,它指向函数​

​Student​

​本身:

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

xiaoming instanceof Student; // true      

看晕了吧?用一张图来表示这些乱七八糟的关系就是:

js创建对象| 25

红色箭头是原型链。注意,​

​Student.prototype​

​指向的对象就是​

​xiaoming​

​、​

​xiaohong​

​的原型对象,这个原型对象自己还有个属性​

​constructor​

​,指向​

​Student​

​函数本身。

另外,函数​

​Student​

​恰好有个属性​

​prototype​

​指向​

​xiaoming​

​、​

​xiaohong​

​的原型对象,但是​

​xiaoming​

​、​

​xiaohong​

​这些对象可没有​

​prototype​

​这个属性,不过可以用​

​__proto__​

​这个非标准用法来查看。

现在我们就认为​

​xiaoming​

​、​

​xiaohong​

​这些对象“继承”自​

​Student​

​。

不过还有一个小问题,注意观察:

xiaoming.name; // '小明'
xiaohong.name; // '小红'
xiaoming.hello; // function: Student.hello()
xiaohong.hello; // function: Student.hello()
xiaoming.hello === xiaohong.hello; // false      

​xiaoming​

​和​

​xiaohong​

​各自的​

​name​

​不同,这是对的,否则我们无法区分谁是谁了。

​xiaoming​

​和​

​xiaohong​

​各自的​

​hello​

​是一个函数,但它们是两个不同的函数,虽然函数名称和代码都是相同的!

如果我们通过​

​new Student()​

​创建了很多对象,这些对象的​

​hello​

​函数实际上只需要共享同一个函数就可以了,这样可以节省很多内存。

function Student(name)
    this.name = name;
}

Student.prototype.hello = function ()
    alert('Hello, ' + this.name + '!');
};      

继续阅读