javascript之原型

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
var fn = function() {}
console.log(typeof fn); // function
console.log(fn instanceof Object); // true
// 函数确实是对象

function Person(name) {
this.name = name;
}
var person = new Person('dottie');
console.log(person) // Person { name: 'dottie' }

// 故,对象都是通过函数来创建的。
// 但是, 又有问题了。 下面创建的方式我们没用函数
var person2 = {
name: 'dottie'
};
console.log(person2); // { name: 'dottie' }

// 其实。这种方式是这样创建对象的。
var person3 = new Object();
person3.name = 'dottie'
console.log(person3); // { name: 'dottie' }
console.log(Object.prototype.toString.call(person2)); // [object Object]

// 总之,对象都是通过函数来创建的。

// 上面的function Person() {}
// 这里的Person就是构造函数。
console.log(person.constructor === Person); // true

// 那么person的constructor属性是哪儿来的呢?
// 这里就扯出了 prototype 原型的话题了。

// 每一个对象都有一个 __proto__ 属性 指向该对象函数的prototype
console.log(person.__proto__ === Person.prototype); // true

// 而prototype上面就是挂载了许多东西:
// 1. 自身方法 如 Object.prototype.toString方法
// 2. constructor 方法
// 3. __proto__ 因为万事万物皆为对象,这里的对象函数Person(){} 其实他也是一个对象,是Function的实例对象,
console.log(Person.__proto__ === Function.prototype) // true

// 并且 Person.prototype 其实也是一个对象,他的 __proto__是指向Object.prototype
console.log(Person.prototype.__proto__ === Object.prototype) // true
// 所以,当我们调用person.toString()方法时,我们Person类并没有定义toString方法。但是却可以直接使用toString方法。
// 是因为person对象中有一条原型链。当在Person类中没有我们需要的方法时,就会顺着原型链向上找,直到直到Object中prototype的方法。

// 而又有人会好奇,既然万事万物都是对象,那么Object.prototype的__proto__指向哪儿呢。答案是null。 否则就会造成一个死循环。
// Object.__proto__为Function.prototype
console.log(Object.__proto__ === Function.prototype); // true


// 所以对象的原型链如下;
// 调用person.toString方法的流程:
// 1. 先在Person中toString方法,
// 2. 没找到。去 person.__proto__中也就是 Person.prototype中去找toString方法,
// 3. 没找到. 继续去 Person.prototype.__proto__中也就是Object.prototype中去找,
// 4. 最终在 Object.prototype中找到了toString方法了。

// ------------------------------
// 还要有人会好奇。当我们对函数调用call或者apply方法时。那么为什么函数可以调用call和apply方法?
// var aa = function() {alert(this)}
// aa.call(); 就是将当前context的this。传递给aa函数。
// 那么call方法时哪儿来的呢?肯定不是Object.prototype的。
// 其实call和apply方法时Function.prototype的方法。
// 验证
var aaa = function() {}
console.log(aaa.apply === Object.prototype.apply) // false
console.log(aaa.apply === Function.prototype.apply) // true
var names = Object.getOwnPropertyNames(Function) // [ 'length', 'name', 'prototype' ]
var allNames = Object.getOwnPropertyNames(Function.prototype)
console.log(allNames); // 遍历 [ 'length','name','arguments','caller','constructor','apply','bind','call','toString' ]
console.log(Function.name === Function.prototype.name) // false

console.log(new aaa().constructor === aaa.prototype.constructor) // true
console.log(aaa.constructor === aaa.prototype.constructor) // false
console.log(aaa.constructor === Function.prototype.constructor) // true
console.log(aaa.constructor) // [Function: Function]
console.log(aaa.prototype.constructor) // [Function: aaa]

console.log(person.toString()) // [object Object]
// 我们修改Object中toString方法。适用于我们person类输出。
// Object.prototype.toString = function() {
// return this.name;
// };
// console.log(person.toString()) // dottie

// 或者我们自己定义toString方法
Person.prototype.toString = function() {
return this.name;
};
console.log(person.toString()) // dottie