在Javascript中没有类的概念,也就没有父类和实例的区分,在Javascript中实现继承主要是基于constructor,也就是构造函数,至于Javascript的继承的设计思想,阮一峰老师的一片文章说的很清楚,从Javascript的诞生讲了Javascript的设计思想————Javascript继承机制的设计思想
constructor
constructor,也就是构造函数,本质上就是一个函数,其内部使用this变量。对构造函数使用 new 运算符就能得到实例,并且this指向会绑定到实例对象上。
function Person(name) {
this.name = name;
}
const p = new Person('小明')
p // {name: '小明'}
例子中,p是通过new Person
得到的实例,所以,p
的构造函数就是Person
(p.constructor === Person
)。
所以,一个对象的constructor
属性,指向的就是使其诞生的那个函数,在java等语言中,也就是父类,父类中是有一个构造函数的——constructor
,而Javascript
中没有类的概念,就直接使用constructor
来得到实例对象,仅此而已。
用new
关键字实例化得到的对象,其constructor
就指向new
关键字之后的那个函数;那如果是使用Object.create
方法生成的实例呢?此时的对象的构造函数又是谁呢?
const obj = {
name: 'xiaohong'
}
const a = Object.create(obj)
console.log(obj.constructor) // ƒ Object() { [native code] }
a.constructor === Object //true
从上面的例子可以看出,使用 Object.create 方法得到的实例对象,其 constructor 指向的是 Object
constructor的缺点
我们把对象的属性写进构造函数,需要的时候就通过 new 运算符得到实例,很好用,但是存在一个问题,就是内存浪费很严重。
在所有实例中都有的属性和方法,如果写在构造函数中,那么,每生成一个实例,就会创建一遍这些属性和方法,但是,我们的属性和方法可能是所有实例都一样的
function Person(name){
this.name = name;
this.age = 12;
this.eat = function (){
console.log(1)
}
}
此例子中的 age 属性和 eat 方法每个实例都会有,但是他们是不会发生变化的,所以就存在了内存浪费,那可不可以将这些属性和方法放在一个地方,然后所有的实例公用呢?当然可以,那就是 prototype。
prototype
前面已经讲了将所有属性与方法都放在 constructor 中缺点,那如何解决这个缺点呢?就是Prototype 模式
在 javaScript 中,每一个构造函数都有一个 prototype属性,这个属性指向另一个对象。这个对象的所有属性和方法都会被构造函数的实例继承。
所以我们可以把那些不变的属性和方法放在 prototype 上
function Person(name) {
this.name = name;
}
Person.prototype.age = 12;
Person.prototype.eat = function() {console.log(1)};
const person1 = new Person('小明');
const person2 = new Person('小王');
console.log(person1.age); // 12
console.log(person2.age); // 12
// eat方法也是一样的。。。
那如何判定一个对象是否是存在另一个对象的原型链上呢?
- isPrototypeOf 这个方法用来判断一个对象是否在另一个对象的原型链上。
使用方式:
Person.prototype.isPrototypeOf(person1) // true
- instanceof instanceof 同 isPrototypeOf作用类似,也是用来判断一个对象是否在另一个对象的原型链上的,其区别在于 instanceof 对 person1 的原型链是针对Person.prototype 去检查的,而不是 Person 本身。
使用方式:
person1 instanceof Person // true。
- hasOwnProperty 每个实例对象都有一个 hasOwnProperty 方法,用来判断属性是本地属性,还是原型属性。
使用方法:
person1.hasOwnProperty('name') // false 表示是本地属性
- in in 方法用来判断指定的属性是否在指定的对象或其原型链中
使用方法:
'name' in person1 // true
__proto__属性
前面已经讲过每个构造函数都有一个 prototype 属性,而其实例对象都能访问到 prototype 中的属性跟方法,那么,我们如何直接通过实例对象取到构造函数的 prototype对象呢?那就是通过 __proto__属性
每个对象都有一个__proto__属性,其指向的就是构造函数(父类)的prototype(原型对象)
function Person(name) {
this.name = name;
}
Person.prototype.age = 12;
Person.prototype.eat = function() {console.log(1)};
const person1 = new Person('小明');
const person2 = new Person('小王');
person1.__proto__ === Person.prototype // true
person1.__proto__ === Person.prototype // true
Person.__proto__ === Function.prototype // true
Person.constructor === Function // true
从例子中可以看出,实例对象的__proto__属性指向的就是其构造函数的 prototype,而构造函数的__proto__指向的是 Function.prototype,这样所有对象的prototype通过__proto__链接起来形成的一条链,就是原型链了。
总结
- constructor 就是构造函数,与普通函数的区别就是,构造函数首字母一般大写
- 每个构造函数都有一个 prototype 对象,称为原型对象,原型对象上的属性与方法在所有实例中共享
- 每个对象都有一个__proto__属性,其指向的就是实例化改对象的构造函数的prototype对象
至此,关于js中constructor、prototype、__proto__三者之间的关系就已经讲完了。