原型链
原型链: 通过对象__proto__属性指向函数的原型对象(函数.prototype)一层一层往上找,直到找到Object的原型对象(Object.prototype)为止,层层继承的链接结构叫做原型链(通过proto属性形成原型的链式结构,专业术语叫做原型链)
上述概念其实不够严谨,其实原型链的尽头是null,下面是我自己画的一个原型链
乍一看这也太复杂了吧,但是简单的描述原型链只需要说:
每一个构造函数构造出来的实例都会有一个_proto_属性指向其原型对象,然后原型对象实际上也是一个"对象",而对象是由Object所创建出来的实例,所以自然会指向Obect的Prototype(原型),然后Object的原型再往上指向null。这从下往上查找的“链”就是原型链。
下面有几个经典的题目,弄懂后保证你把原型链吃得透透的! 最后一题一定弄懂!!!
建议一次性把六个题目复制到一个js文件中,然后把答案删掉. (因为一次复制一个题目删一个做一个就会记得答案啦,我们要让自己忘记答案哈哈)
题目1:
function F() { this.a = 1; } var obj = new F(); console.log(obj.prototype);//打印undefined 对象只有 __proto__属性没有prototype属性,函数才有
看图; obj是F构造函数所构造的一个实例,而实例是没有prototype属性的,他是最底层的东西.
题目2:
Object.prototype.a = 1; var obj = { b: 2 }; for(var i in obj) { console.log(i); //能迭代出原型链里面的属性,所以会打印出 b和a }
拓展:
需要注意的是,虽然for…in循环可以迭代原型链上的属性,但它也会迭代对象的所有可枚举属性,包括从原型链继承的属性和对象自身的属性。如果你只想迭代对象自身的属性,可以使用Object.hasOwnProperty()方法来过滤掉原型链上的属性。例如:
for (var i in obj) { if (obj.hasOwnProperty(i)) { console.log(i); } }
这样就只会打印出obj对象自身的属性b,而不会打印出原型链上的属性a。
题目3:
Object.prototype.a = 1; var obj = { b: undefined }; console.log(obj.a);//1 console.log('b' in obj);//true //能找到处于原型链里面的b属性 console.log(obj.hasOwnProperty('a'));//false 不是自身的属性 console.log(obj.hasOwnProperty('b'));//true 是自身的属性
题目4:
function A(){ } function B(a){ this.a = a; } function C(a){ if(a){ this.a = a; } } A.prototype.a = 1; B.prototype.a = 1; C.prototype.a = 1; //实例对象都能访问构造函数的原型对象,可以明确的是以上3个都是能拿到a的 console.log(new A().a);//1 拿到的是原型上的a=1 console.log(new B().a);//undefined this.a = a;将new B()这个实例对象的a属性这是为undefined,所以拿到的是undefined console.log(new C(2).a);//2 将new C(2)这个实例对象的a属性这是为2,所以拿到的是2
题目5:
function A () { } A.prototype.n = 1; var b = new A();//b实例对象已经建立原型连接 //原型对象指向被改变,不会切断b实例对象的 A.prototype = { n: 2, m: 3 } var c = new A();//c实例对象将根据新的原型建立连接 console.log(b.n, b.m); //1 undefined 这里拿到是改变prototype之前的 console.log(c.n, c.m); //2 3 这里拿到是改变prototype之后的
题目6:
var F = function(){}; Object.prototype.a = function(){ console.log('a') } Function.prototype.b = function(){ console.log('b') } var f = new F(); F.a(); //打印a 对象都能访问Object.prototype中的属性和方法 F.b(); //打印b F是函数,原型链 F => F.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype f.a(); //打印a 对象都能访问Object.prototype中的属性和方法 f.b(); //报错f.b is not a function 因f是F实例对象,原型链 f=>f.__proto__=>F.prototype=>F.prototype.__proto__ => Object.prototype
是不是有人以为f.b()会打印 b ?
如果对最后一题很懵逼的同学,可以从上往下看上面那张图,哪一个箭头连接不懂就留言吧 !
欢迎指正!!