创建对象
JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。
当我们用obj.xxx访问一个对象属性的时候,JavaScript引擎会现在当前对象上查找该属性,如果找不到,就在其原型对象上找,如果还是没有,就会上溯Object.prototype对象找,如果还没有的话就会返回 undefined。
- 创建一个
Array对象:
let arr = [1,2,3,4,5];
它的原型链是:
arr ----> Array.prototype ----> Object.prototype ----> null
Array.prototype定义了indexOf(),shift()等方法,所以可以在Array的对象上直接调用这些方法。 - 创建一个函数
function f1() {
return 0;
}
f1()的原型链:
f1 ---> Function.prototype ----> Object.prototype ----> null
由于Function.protype定义了call(),apply()等方法,因此所有函数都可以调用这些方法。如果原型链很长,访问一个对象的属性就会花更多的时间来查找而变得更慢。
构造函数
用构造函数的方法创建一个对象,首先定义一个构造函数:
function Person(name,age) {
this.name = name;
this.age = age;
this.sayName = function() {
console.log('Hi,'+this.name);
}
}
接下来用关键字 new来调用函数并返回一个对象。
let Nick = new Person('Nick',23);
Nick.sayName();//'Hi,Nick'
写了 new关键字,它变成一个构造函数,它绑定的 this指向新创建的对象,并默认返回 this,也就是说不需要在最后写return this;。
Nick的原型链如下:
Nick ---> Person.prototype ---> Object.prototype ---> null
也就是 Nick的原型指向 Person原型,如果再创建别的tom,jerry....这些对象的原型都是和 Nick一样的。
此外,通过new Person()创建的对象还从原型上获得了一个 constructor属性,它指向函数 Person本身。
Nick.construcotot === Person.prototype.constructor;
Person.prototype.construcor === Person;
Object.getPrototypeOf(Nick) === Person.prototype;
有一点需要注意,创建两个新的对象 Tom和 Jerry,但是他们俩的 sayName()函数虽然名称和代码相同,但实际上是两个不同的函数,如果通过 new Person()创建很多个对象,他们只需要共享一个sayName()函数就可以了,能节省很多内存。修改代码如下:
function Person(name,age) {
this.name = name;
this.age = age;
}}
Person.prototype.sayName = function() {
console.log('Hi,'+this.name);
}
特别注意
对象的.__proto__ === 其构造函数.prototypeObject.prototype是所有对象的直接或间接原型- 所有函数都是由
Function构造的,任意函数.__proto__ === Function.prototype
class继承
ES6引入了 class关键词, class的引入目的是让定义类更简单。
如果使用 class关键字来编写 Person:
class Person {
constructor(name) {
this.name = name;}
sayName() {
console.log('Hello,' + this.name);
}
}
通过比较可以发现使用 class包含了构造函数 constructor和定义在原型对象上的函数 sayName()(没使用 function关键字),这样就避免了 Person.prototype.sayName = fucntion(){...}这样的代码。
使用 class一个巨大的好处就是更方便继承。如果要从 Person派生一个 otherPerson,原型继承的中间对象、原型对象的构造函数都不需要考虑了,可以直接通过 extend来实现。
class otherPerson extends Person{
constructor(name,city) {
super(name); //使用supper调用父类的构造方法
this.city = city;
}
myCity() {
aler (' I am from ' + this.city);
}
}
otherPerson的定义也是 class关键字来实现的,而 extends表明了原型链对象来自 Person, otherPerson需要 name city两个参数,并且通过 super(name)来调用父类的构造函数。并且otherPerson已经自动获得了父类Person的sayName()方法。
class与原有的原型继承没有任何区别,只是简化了原型代码。