对比c++&&java等基于类的语言,JavaScript常常令人困惑,因为其是动态的,本身不提供一个class的实现,(ES6中的关键字class只属于一种语法糖,JavaScript仍然是基于原型的)。
看完文章,应该掌握:
- __proto__
- prototype
- __proto__和prototype的关系
- 原型链的结构
- 原型对象的意义
首先了解什么是原型?
假设有一个对象cat,其构造函数为Animal
- 所有的引用类型(除了null)都有一个属性__proto__,是一个对象,称为隐式原型。(cat.__proto__)
- 引用类型的构造函数都有一个属性prototype,是一个对象,成为显式原型(Animal.prototype)
- 所有的引用类型的__proto__都指向其自身的构造函数的prototype属性,也就是cat.__proto__ === Animal.prototype
- prototype,为原型对象,对象有一个属性:constructor,即Animal.prototype.constructor === Animal
- prototype既然也是对象,那么其自然也有其构造函数,因此也还会有自身的原型对象,即Animal.prototype
- 实例对象一旦创建,便会自动引用原型对象中的属性和方法
原型链是什么?
每个实例对象都有一个属性__proto__,指向其构造函数的原型对象(prototype),该原型对象也有其原型对象(__proto__),层层向上,直到一个对象的原型对象为null,null没有原型对象,作为原型链的最后一个环节。
当试图得到对象中的一个属性时,假如对象中并不存在该属性,那么便会去它的原型对象中寻找,假如找不到,便会继续向其原型对象的原型对象找,直到到了最上层的null为止。
我认为:原型对象可以看作是构造函数的补充,创建的实例对象会拥有构造函数内的属性和原型对象中属性的引用
原型对象的意义?
用于存放所有对象实例的共享资源,可以节省内存。
function Person(name,age){
this.name = name;
this.age = age;
this.eat = function(){
console.log(age + "岁的" + name + "在吃饭。");
}
}
var p1 = new Person('xiaoming',18);
var p2 = new Person('xiaoli',17);
console.log(p1.eat === p2.eat);//输出false
也就是说我们创建出来的每一个对象,里面都有独立的eat方法,这样将会占用很多的资源。
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.eat = function(){
console.log(age + "岁的" + name + "在吃饭。");
}
var p1 = new Person('xiaoming',18);
var p2 = new Person('xiaoli',17);
console.log(p1.eat === p2.eat);//输出true