对象
在JavaScript中,万物皆对象Object。
Object是一个属性的集合,并且都拥有一个单独的原型对象
[prototype object]. 这个原型对象[prototype object]可以是一个object或者null值。一个Object的
prototype是一个内部的[[prototype]]属性的引用
proto 与 prototype
[[prototype]]即__proto__,对象__proto__属性的值就是它所对应的原型对象: 每个对象都有__proto__属性来标识自己所继承的原型,如
typeof function(){}.__proto__ === 'function' // true
typeof {}.__proto__ === 'object' // true
- 只有函数才有
prototype属性,当我们创建函数时,JS会自动创建该函数的prototype属性,它的值是一个有constructor属性的对象,指向该方法的原型对象。一旦你把这个函数当作构造函数调用(即通过new关键字调用),那么JS就会帮你创建该构造函数的实例,实例继承构造函数prototype的所有属性和方法(实例通过设置自己的__proto__指向承构造函数的prototype来实现这种继承)。
原型链
JS正是通过__proto__和prototype的合作实现了原型链,以及对象的继承。
官方注解:A prototype chain is a finite chain of objects which is used to implemented inheritance and shared properties.
构造函数,通过prototype来存储要共享的属性和方法,也可以设置prototype指向现存的对象来继承该对象。
对象的__proto__指向自己构造函数的prototype。obj.__proto__.__proto__...的原型链由此产生。
js的操作符instanceof正是通过探测obj.__proto__.__proto__... === Constructor.prototype来验证obj是否是Constructor的实例。
在支持Class类的语言中,我们可以通过Class A extends B {},实现A继承B。在Javascript语言,虽没有类的概念,但依然可以借由原型链实现继承,这种实现方式就是原型继承。
举例,a1对象通过设置自身__proto__,将自身的原型对象指向修改为a对象,实现了原型继承。
var a = {
x: 10,
calc: function(z) {
return this.x + this.y + z
}
}
var a1 = {
y: 10,
__proto__: a
}
console.log(`calc ${a1.calc(10)}`) // calc 30
在上述例子中,我们分析下,当我们调用a1.calc(10)时,先去找a1的calc方法,找不到继续在a1原型a上找到并调用该方法。在calc()方法内,有三个参数,分别是两个this上的属性x,y和一个传参z。因为是对象属性的调用方式,所以this指向的是a1,其中a1没有x属性,所以x属性用的是原型上的x的值,y属性用的是a1的属性,z用的是传进去的10;
两个注意点:
- js查找都会从自身开始寻找,找不到的时候,沿着原型链在a1的原型上寻找,找到的话,调用该方法,找不到则继续往上查找,直至原型是null时,返回calc() undefined。
- this的指向,在调用方法的时候决定,不会受到原型链查找的影响。
除了上述对象设置a1.__proto__ = a,或者函数设置function A(){}; A.prototype = AA这种方式,常见的创建对象方式是构造函数(Constructor);
构造函数
同样的上述例子,a1 原型继承 对象a,用构造函数的方式,改写如下:
function A(y) {
this.x = 10;
this.y = y;
this.calc = function(z) {
console.log(`x ${this.x}, y ${y}, z ${z}`);
return this.x + this.y + z
};
}
A.prototype.name = 'A的原型属性name';
var a2 = new A(20)
console.log(`name: ${a2.name}, 运算值: ${a2.calc(30)}`);
// x 10, y 20, z 30
// name: A的原型属性name, 运算值: 60
console.log(
a2.constructor === A, // true
a2.__proto__ === A.prototype, // true
a2.name === a2.__proto__.name, // true
a2.name === A.prototype.name, // true
A.__proto__ === A.prototype, // false
`A的原型: ${A.__proto__ === Function.prototype}`, // A的原型: true
`A的原型链: ${A.prototype.constructor === A}`, // A的原型链: true
);
当我们创建函数A时,JS会自动创建函数A的prototype属性,它的值是一个有constructor属性的对象。
通过关键词new,我们创建了对象a2,实例a2继承了构造函数prototype的所有属性和方法(a2的构造器是A,a2的原型是A的原型,a2继承了A的属性方法)。
回顾总结
function Foo(name) {
this.name = name;
}
var foo1 = new Foo('foo1');
var foo2 = new Foo('foo2');
var obj1 = new Object();
var obj2 = new Object();
console.log(foo1.__proto__ === Foo.prototype); // true
console.log(Foo.prototype.constructor === Foo); // true
console.log(obj1.__proto__ === Object.prototype); // true
console.log(Foo.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.constructor === Object); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.prototype.constructor === Function); // true
console.log(Function.__proto__ === Function.prototype); // true

- 对象有属性
__proto__,指向该对象的构造函数的原型对象。 - 方法除了有属性
__proto__,还有属性prototype,prototype指向该方法的原型对象。