JS--原型链

310 阅读3分钟

先来一个小测试

环境:对象 person1 有一个__proto__属性,创建它的构造函数是 Person,构造函数的原型对象是 Person.prototype

问题:

  1. person1.__proto__是什么?
  2. person.__proto__是什么?
  3. Person.prototype.__proto__是什么?
  4. Object.__proto__是什么?
  5. object.prototype.__proto__ 是什么?

答案:
第一题:
因为 person1.__proto__==person1 的构造函数Person .prototype

因为 person1的构造函数 ===Person

所以 person1.__proto__ === Person.prototype

第二题:
因为person.__proto__===Person的构造函数 .prototype 
因为Person的构造函数===Function
所以person.__proto__===Function.prototype

第三题:
Person.prototype 是一个普通对象,我们无需关注它有哪些属性,只要记住它是一个普通对象。
因为一个普通对象的构造函数 === Object
所以Person.prototype.__proto__===Object.prototype

第四题,参照第二题,因为 Person 和 Object 一样都是构造函数

第五题:
Object.prototype对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。Object.prototype.
Object.prototype.__proto__=== null

先看下图


分析一下:

  1. 一个函数在生成的时候,javascript都会默认生成一个prototype属性,通过prototype生成一个空对象,也就是原型对象。
  2. 原型对象通过constructor构造器,来指向它被声明的那个函数。
  3. 构造函数通过new 和实例产生关联, 实例又通过__ptoto__指向构造函数的原型。


可以看出原型链的以为就是:原型链通过prototype和__proto__来完成原型的查找。从一个实例对象往上找构造这个实例的相关的原型对象,然后相关联的原型对象再往上找创造它的原型对象,一直到object.prototype原型对象终止。

通过原型链,可以进行数据共享,也就是找到原型对象,原型对象的方法被不同的实例所共有。



查找一个实例中的一个方法或者属性,如果这个这个实例没有找到,就会通过__proto__往上一级找原型对象,如果还找不到就会再往上找,直到object.proototype位置,如果还找不到,就会返回这个属性或者方法没有找到或者没有定义。如果在中间任何一个原型对象找到了,那么返回这个属性和方法,就不会再往上寻找。

强调一下只有函数才会有有prototype,对象是没有prototype的,只有实例对象有 __proto__

解释一下为什么函数也会有__proto__


从图中可以看到,函数name()也是函数Function的原型对象,所以函数也即对象。

万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object 、Function 是 JS 自带的函数对象。

原型链污染

看下面例子

//a.js
(function()
{
  var secret = ["aaa","bbb"];
  secret.forEach();
})();

//b.html
<body>
    <script>
	Array.prototype.forEach = function() {
	    var result = 'result: ';
	    for(var i=0,length = this.length; i<length;i++){
	        result += this[i];
	        result += ''
	    }
	    document.write(result);
	}
	</script>
	<script src="./a.js"></script>
</body>

运行结果:


在a.js中声明了一个数组 secret ,然后该数组调用了属于 Array.prototype的forEach方法,如下


但是,在调用js文件之前,js代码中将 Array. prototype.forEach 方法进行了重写,而prototype链为 secret -> Array.prototype ->object.prototype,secret中无 foreach 方法,所以就会向上检索,就找到了 Array.prototype 而forEach方法已经被重写过了,所以会执行输出。

这就是原型链污染。很明显,原型链污染就是:在我们想要利用的代码之前的赋值语句如果可控的话,我们进行 __proto__ 赋值,之后就可以利用代码了。