prototype __proto__ constructor 的关系

proto_inner

构造函数

构造函数就是提供了一个生成对象的模板并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的属性和方法。总的来说,构造函数就是对象的模板(类),对象就是构造函数的实例。同一个构造函数的生成的对象实例之间无法直接共享属性或方法。

1
2
3
4
5
function Person(){
this.name = 'keith';
}
var boy = new Person();
console.log(boy.name); // 'keith'

构造函数的特点有:

a:构造函数的函数名首字母必须大写。
b:内部使用 this 对象,来指向将要生成的对象实例。
c:使用 new 操作符来调用构造函数,并返回对象实例。

prototype 属性

属性 prototype 是函数特有的属性,这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。通过 prototype 定义的属性及方法能被所有对象实例所共享,这就是 prototype 的意义。

对于构造函数来说, prototype 是作为构造函数的属性;对于对象实例来说, prototype 是对象实例的原型对象。

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, height) {
this.name = name;
this.height = height;
}
Person.prototype.hobby = function () { // 生成的对象实例会共享这样声明的构造函数方法
return 'watching movies';
}
var boy = new Person('keith', 180);
var girl = new Person('rascal', 153);
console.log(boy.name); //'keith'
console.log(girl.name); //'rascal'
console.log(boy.hobby === girl.hobby); //true 表明生成的对象实例共享着同一个方法

prototype 属性的特点有:

a:原型属性 prototype 的作用,就是定义所有对象实例所共享的属性和方法。
b:prototype 对于构造函数来说,它是一个属性;对于对象实例来说,它是一个原型对象。

constructor 属性

属性 constructor 返回对象实例的构造函数。

1
2
3
function Person(){};
var boy = new Person();
console.log(boy.constructor); // Person

constructor 属性实际上是原型对象的属性,这个属性包含了一个指针,指回原构造函数,它被所有实例对象继承。

1
2
3
4
function Person(){};
var boy = new Person();
console.log(Person.prototype.constructor === Person); // true 通过原型对象访问 constructor 属性返回的是原型对象所处的构造函数
console.log(boy.constructor === boy.prototype.constructor); // true

instanceof 运算符

instanceof 用于判断对象是否为某个构造函数的实例。

1
2
3
4
function Person() {};
var boy = new Person();
console.log(boy instanceof Person); //true
console.log(boy instanceof Object); //true 对整个原型链上的对象都有效

__proto__ 属性

属性 __proto__ 返回对象实例的原型对象,对象内部的 [[Prototype]],也即对象实例的构造函数的 prototype 属性值。使用 __proto__ 是有争议的,也不鼓励使用它,因为它从来没有被包括在 EcmaScript 语言规范中,但是现代浏览器都实现了它。
ES6 中可以使用 Object.getPrototypeOf() 获取。

1
2
3
4
5
function Person(name){
this.name = name;
}
var boy = new Person('jack');
console.log(boy.__proto__ === Person.prototype); //true 同样的, Person 也有原型对象,通过 Person 的 __proto__ 属性也可以访问到它的原型对象,以此类推,可以实现原型链的向上追溯。

相对于通过 __proto__ 属性继承其他对象的属性而言,Object.create() 方法是一个更加值得推荐的方法。该方法接收一个对象作为参数,返回一个以该对象为原型对象的新对象,即继承了作为参数的对象的属性及方法。

1
2
3
4
5
let person = {
age: 10
}
let boy = Object.create(person);
console.log(boy.age); //10

实现 子类 继承 父类

类继承的核心是让 子类 拥有 父类 的方法和属性

1
2
3
4
5
6
7
8
9
10
function Parent() {
this.parentAge = 50;
}
function Child() {}

Child.prototype = new Parent();
Child.prototype.constructor = Child; // 注意重置constructor

const obj = new Child();
console.log(obj.parentAge); // 50
1
2
3
4
5
6
7
8
9
10
function Parent() {
this.parentAge = 50;
}
function Child() {}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

const obj = new Child();
console.log(obj.parentAge); // 50

实现 new

new 其实就是生成了一个对象,初始化时执行类的构造函数,这个对象能够访问类的原型(属性和方法)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function myNew(func, ...args) {
const obj = {}; // 新建一个空对象
let res = func.call(obj, ...args); // 执行构造函数
obj.__proto__ = func.prototype; // 设置原型链

return res instanceof Object ? res : obj
}
function myNew1(func, ...args) {
let obj = Object.create(func.prototype); // 以构造器的 prototype 属性为原型,创建新对象;
let res = func.call(obj, ...args); // 将 this 和调用参数传给构造器执行

return typeof res === 'object' ? res : obj; // 如果构造器没有手动返回对象,则返回第一步的对象
}
function Puppy(age) {
this.puppyAge = age;
}

Puppy.prototype.say = function() {
console.log("汪汪汪");
}

const myPuppy3 = myNew(Puppy, 2);

console.log(myPuppy3.puppyAge); // 2
myPuppy3.say(); // 汪汪汪

实现 instanceof

instanceof 作用是检查一个对象是不是某个类的实例,换句话说就是检查一个对象的的原型链上有没有这个类的 prototype

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function myInstanceof(targetObj, targetClass) {
// 参数检查
if(!targetObj || !targetClass || !targetObj.__proto__ || !targetClass.prototype){
return false;
}

let current = targetObj;

while(current) { // 一直往原型链上面找
if(current.__proto__ === targetClass.prototype) {
return true; // 找到了返回true
}

current = current.__proto__;
}

return false; // 没找到返回false
}

// 用我们前面的继承实验下
function Parent() {}
function Child() {}

Child.prototype.__proto__ = Parent.prototype;

const obj = new Child();
console.log(myInstanceof(obj, Child)); // true
console.log(myInstanceof(obj, Parent)); // true
console.log(myInstanceof({}, Parent)); // false