Hello 小伙伴们,如果觉得本文还不错,记得给个 star , 你们的 star 是我学习的动力!
一、牛刀小试
题目一:
var A = function () {};
A.prototype.n = 1;
var b = new A();
A.prototype = { n: 2, m: 3 };
var c = new A();
console.log(b.n);
console.log(b.m);
console.log(c.n);
console.log(c.m);
试着写出上面编程的输出结果吧!
题目二:
var F = function () {};
var foo = new F();
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';
console.log(foo.a);
console.log(foo.b);
console.log(F.a);
console.log(F.b);
试着写出上面编程的输出结果吧!
二、答案
题目一答案:
console.log(b.n); // 1
console.log(b.m); // undefined
console.log(c.n); // 2
console.log(c.m); // 3
题目二答案:
console.log(foo.a); // value a
console.log(foo.b); // undefined
console.log(F.a); // value a
console.log(F.b); // value b
- 如果小伙伴们查看答案后仍不明白,那就接着往下看,愉快的扩展我们的知识吧!!!
三、要想理解原型和原型链必读的三句话:
- 每一个构造函数都有一个默认的prototype属性,指向自己的原型对象
- 每一个实例(对象)都有一个__proto__的属性,指向所属类或构造函数的原型对象
- 每一个默认的原型对象上都有一个constructor属性,指向对应构造函数本身
不理解没关系,那就让风暴来的更猛烈些吧!
四、请看下图:
- 看完这张图的第一反应多数小伙伴肯定是一脸蒙蔽(我自己也是>_<),不过没关系,接下来就让我们一起来攀登这座高山吧。 命运对勇士低语:“你无法抵御风暴!”,勇士低声回应:“我就是风暴!”
五、原理及示例解释:
1、原型链的作用:
- 在利用构造函数创建实例的时候,会给每个实例都添加自身所独有的属性和方法,可行,但是最后会导致大量的重复代码的编写以及内存的占用;所以就有了原型和原型链,把实例所共享的属性和方法都放在原型上,让实例在需要的时候自己沿原型链去寻找并使用。
- 属性和方法的查找规则是,先在自身寻找,找到输出;未找到则沿着原型链向上寻找,层层向上,直到Object的原型(Object.prototype)还未找到就输出报错信息,因为Object.prototype的原型为null。
2、首先让我们来揭开原型的真面目吧!
let F = function () {};
let f = new F();
/*
上述例子中:
构造函数: F;
构造函数 F的实例:f;
*/
// 1.每一个构造函数都有一个默认的prototype属性,指向自己的原型对象,
// 2.每一个实例(对象)都有一个__proto__的属性,指向所属类或构造函数的原型对象
// 实例f是构造函数F 的实例,所以验证一下上面的两句话吧
console.log(F.prototype); // {} 从结果来看,构造函数 F 上有一个属性prototype,且该属性指向一个对象
console.log(f.__proto__); // {} 从结果来看,实例 f 上有一个属性__proto__,且该属性指向一个对象
console.log(f.__proto__ === F.prototype); // true 这两个对象为同一个对象
// 我们就把这个对象叫做构造函数的原型对象,也就是我们常说的原型
// 3.每一个默认的原型对象上都有一个constructor属性,指向对应构造函数本身
// 那我们再来验证一下第三句话,已知F为构造函数,且F.prototype属性指向构造函数的原型
console.log(F.prototype.constructor === F); // true
console.log(f.__proto__.constructor === F); // true
3、知道了什么是原型,那就轮到原型链了
- 原型链和函数的作用域很相似,原型链查找和变量的作用域查找也很相似,好了废话不多说,直接上例子。 例子还是原先的那个,只是更深入的剖析 ^_^
let F = function () {};
let f = new F();
/*
上述例子中:(记住这些,后面会用到)
所有的构造函数或类有:
1、Object;
2、Function;
3、F
所有的实例:
!!!F既是Function的实例,也是f的构造函数
!!!所有的原型对象又都是Object的实例(没有被修改)
1、Function.prototype 和 F.__proto__指向的是同一个实例对象(Object的实例)
2、F(Function的实例)
3、F.prototype 和 f.__proto__ 指向的是同一个实例对象(Object的实例)
4、f(F的实例)
*/
// 首先我们得知道Object是所有(除Object的原型外)原型对象的构造函数(未做出修改),
// 那么肯定就会有小伙伴问啦,构造函数Object的原型(Object.prototype)又是谁的实例?
console.log(Object.prototype.__proto__, '1'); // null "1"
// 结果很显然,Object的原型不是任何构造函数的实例,这也是为什么原型链的查找只到Object的原型就结束了
// 原型链是什么? 原型链相当于一条线索,两端连接着实例和原型,而原型又连接着原型的原型,最终到Object的原型
// !!! 要时刻记住,实例上的 __proto__ 属性 ; 构造函数和类上的prototype 属性 !!!
// 原型链的起点为null
// 然后是构造函数Object的原型
console.log(null === Object.prototype.__proto__, '2'); // true "2"
// 再接着是构造函数Object的实例,也就是其他的原型(本例子中是构造函数Function和构造函数F的实例)
console.log(Object.prototype === Function.prototype.__proto__, '3'); // true "3"
console.log(Object.prototype === F.prototype.__proto__, '4'); // true "4"
// 紧接着是构造函数Function的实例 构造函数F(F既是构造函数,也是实例); 和构造函数F的实例f
console.log(Function.prototype === F.__proto__, '5'); // true "5"
console.log(f.__proto__ === F.prototype, '6'); // true "6"
- 上述关系如下图:
最后,再去看之前的那张图,是否有种我就是风暴的感觉!