这篇文章的的目的试图通过最简单的表述,让大家理解
prototype
和__proto__
先把最重要的几点列出来,大家可以带着这几个核心要点阅读下面的文章.
-
__proto__
是用来在原型链上查找你需要的方法的实际对象,所有的对象都有这个属性.这个属性被JavaScript
引擎用作继承使用. 根据ECMA的规范,这个属性应该是一个内在的属性,但是大多数的浏览器厂商都允许我们去访问和修改它. -
prototype
是函数:sunglasses:独有的属性😎.当我们使用关键词new
并且将函数作为构造函数来构造对象的时候, 它被用来构建对象的__proto__
属性. -
__proto__
属性和prototype
属性都是一个对象代码演示. -
(new A()).__proto__ === A.prototype
的结果为true
,(new A()).prototype === undefined
的结果也为true
,其中A
表示一个函数(也就是构造函数).
接下来我们来使用一些代码来解释上面所说的那些要点:
// 这是一个普通函数,我们把它用来当做构造函数,也当做一个[父类]
function Car(name) {
this.name = name;
}
Car.prototype.introduce = function() {
console.log('[From Car.prototype.introduce] ' + 'Hello, my name is: ' + this.name);
};
var car = new Car('porsche');
console.log(car.name); // porsche
car.introduce(); // [From Car.prototype.introduce] Hello, my name is: porsche
// 我们开始构建另外一个函数,我们把这个函数当做一个[子类],暂时这么说.
function MiniCar(name, color) {
this.name = name;
this.color = color;
this.getColor = function() {
console.log('My color is: ' + this.color);
}
}
MiniCar.prototype = new Car();
var miniCar = new MiniCar('benz', 'black');
console.log('\n');
console.log('name: ' + miniCar.name + ';color: ' + miniCar.color); // name: benz;color: black
miniCar.introduce(); // [From Car.prototype.introduce] Hello, my name is: benz
miniCar.getColor(); // My color is: black
// 如果使用A表示一个构造函数,那么 (new A()).__proto__ === A.prototype
console.log((new MiniCar()).__proto__ === MiniCar.prototype); // true
// 如果使用a表示A的一个示例的话,那么 a.__proto__ === A.prototype
console.log(miniCar.__proto__ === MiniCar.prototype); // true
// 一个对象是没有prototype属性的
console.log(miniCar.prototype === undefined); // true
如果你练习了上面的代码,对这两个属性的理解应该会有一定的帮助,也许你已经理解了;如果没有太懂的话,那也没关系; 我们下面来好好的说一说上面的代码(开始长篇大论了:joy:).
首先,在JavaScript
中是没有类
这个概念的,如果你学过Java
或者C++
的话,应该知道,要是想创建一个对象,必须先有一个类
;
但是JavaScript
中没有类
,那怎么办?模仿喽:see_no_evil:,所以JavaScript
创造了__proto__
这个属性用来连接子类
和父类
.
创造了prototype
属性去用来在构建子类
时候构建__proto__
这个属性.
这里先暂停上面的线程,我们来说说prototype
这个属性,这个属性是只属于Function
函数的,那么这个属性的作用是什么呢?
这个属性的作用是为了让使用Function
作为构造函数new
出来的对象实例都能够共享一些函数.
function Car(name) {
this.name = name;
}
Car.prototype.introduce = function() {
console.log('[From Car.prototype.introduce] ' + 'Hello, my name is: ' + this.name);
};
上面的代码中,只要是使用Car
这个构造函数new
出来的对象都具有方法introduce
.
继续上面的线程,我们按照代码的执行顺序来说明这件事情:
- 首先我们定义了两个函数
Car
和MiniCar
,如下图所示: - 然后我们给Car的原型上添加了一个方法
introduce
,如下图所示: - 接下来
var car = new Car('porsche')
这一行代码可不像它看起来那样,它内部的实现还是有许多值得玩味的; 首先,函数Car
创建了一个新的对象(a),这个对象有一个隐藏的属性__proto__
,这个属性和Car
的原型都指向同一个对象. 然后Car
函数内部的this
指向哪个新创建的对象(a).如下图所示: - 然后我们给这个对象添加了一个属性
name
,并且为其赋值.还要注意的一点是,我们这个Car
函数是有返回值的,虽然没有使用return
关键字 把这个值显式的返回,这个返回值是一个引用,然后变量car
就可以用来操作那个对象了(a). - 然后上面的语句运行完之后,场面上是下图这个样子:
- 接下来我们输出了这个对象的名字,然后调用了这个对象(的构造函数的原型上的)的
introduce
方法.输出的结果如下图: - 然后我们有定义了一个函数
MiniCar
,我们把它当做Car
(父类)的一个子类
; 我使用代码MiniCar.prototype = new Car()
来实现这个功能,这段代码更值得好好分析一下. 首先如上图所示,MiniCar
这个函数的prototype
是函数Car
使用new
关键字创建的一个对象(b), 所以MiniCar
的实例具有这个对象(b)能够使用的任何属性和方法. - 让我们更进一步吧,这一步我们开始运行
var miniCar = new MiniCar('benz', 'black')
这段代码, 首先我们先要运行函数MiniCar
函数,所以通过new
操作,我们新创建了一个对象(c),我们首先给这个对象 添加了了两个属性,分别是name
和color
,然后分别赋值benz
和black
,其实我们可以只添加一个属性,因为name
属性在Car
上是已经存在的. 我们还给它添加了一个getColor
方法,**它的__proto__
属性指向MiniCar.prototype
, 而MiniCar.prototype
是一个对象,这个对象也有一个__proto__
属性, 这个属性指向Car.prototype
,如此一来这个伪继承就实现了.**然后我们将这个对象(c)的索引赋值给miniCar
,所以通过miniCar
可以操作对象(c). - 然后接下来的一切应该都顺理成章了:joy:.
参考的文章或者问答:
- How does proto differ from constructor.prototype?
- proto VS. prototype in JavaScript
- Inheritance and the prototype chain
- JavaScript difference between proto and prototype
- Understanding "Prototypes" in JavaScript
- JavaScript Prototype in Plain Language
- Object.prototype.proto
- Function.prototype
- js中__proto__和prototype的区别和关系?
- prototype与__proto__的联系与区别
- 简单粗暴地理解js原型链--js面向对象编程