关于prototype、_proto_、constructor

首先,用一张图来总结这三者的关系:

about_proto.jpg

__proto__

js中每一个对象都含有一个内置属性_proto_

它的作用在于:当我们在访问一个对象obj中的某一属性时,如果在obj中找不到这一属性,就会沿着_proto_一直寻找,直到_proto_值为null。这样就构成了原型链。

对于_proto_的指向,我们可以根据对象定义方式的不同分为三种情况来讨论:

(1)使用字面量创建的对象

1
2
3
4
5
var a = new String('123');
a.__proto__ === String.prototype; // true
var obj = {};
obj.__proto__=== Object.prototype; // true

对于使用字面量来创建的对象,它的_proto_直接指向这一字面量的prototype

1
2
var a = new String('123');
String.prototype.__proto__ === Object.prototype;

补充说明String.prototype(即字面量原型)的__proto__的指向,因为String.prototype也是一个对象,所以它的__proto__指向的是Object.prototype

(2)使用new方式创建对象

第一种情况:

1
2
3
4
5
6
function Foo() {
console.log('hello');
}
var a = new Foo();
a.__proto__ === Foo.prototype; // true

第二种情况:

1
2
3
4
function Bar(){}
//这时我们想让Foo继承Bar
Foo.prototype = new Bar()
Foo.prototype.__proto__ === Bar.prototype //true

(3) 使用Object.create(...)方式创建对象

1
2
3
4
5
function Obj() {
console.log('hello');
}
var bar = Object.create(Obj);
bar.__proto__ === Obj; // true

对于上面的结果你可以这么理解,让我们看看一个Object.create(Obj)是怎么工作的:

1
2
3
4
5
Object.create = function(p) {
function f(){}
f.prototype = p;
return new f();
}

所以bar指向的是函数Obj本身。

对于__proto__这一方法在ES5中才被标准化为传统功能,为了更好的保证web浏览器的兼容性,建议只使用Object.getPrototypeOf()

prototype

除了Function.prototype.bind方法构造出来的函数,其他函数都有一个prototype属性。这个属性指向函数的原型对象。

1
2
3
4
5
6
function a() {
console.log('hello');
}
a.prototype; // constructor: f a()
// __proto__: Object
// ...

constructor

存在于原型对象上,此属性的值是对函数本身的引用,而不止是一个字符串。

1
2
3
4
5
6
7
8
function a() {
console.log('1');
}
a.prototype.constructor; // ƒ a() {
// console.log('1');
// }
a.prototype.constructor(); // 1

__proto__prototype的区别

1.对象有属性__proto__,指向该对象的构造函数的原型对象。
2.方法除了有属性__proto__,还有属性prototypeprototype指向该方法的原型对象。