Web前端之路让前端飞

JavaScript原型对象和原型链

2017-11-08  本文已影响77人  卓三阳

理解JS原型链是一件比较令人头疼的使。要彻底搞清楚原型链,我们先来搞清楚对象。
我们可以把对象分为普通对象和函数对象。
普通对象:有_ proto _属性(指向其原型链),没有prototype属性。
函数对象:通过 new Function() 创建的对象都是函数对象;拥有 _ proto _、prototype属性(指向原型对象);

对于新手,看完上面的介绍可能会有一点模糊,我们这里总结为一句话:_ proto _是每个对象都有的属性,prototype是函数对象才有的属性。关于_ proto _和prototype的区别我们下面将会介绍。

1._ proto _和prototype的区别

_ proto _(隐式原型)

JavaScript中任意对象都有一个内置属性[[prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过_ proto _来访问。 _ proto _指向创建这个对象的函数(constructor)的原型对象 。

prototype(显式原型)

每创建一个函数都会有一个prototype属性(自动),这个属性是一个指针,指向一个对象,这个对象就是原型对象。原型对象是包含特定类型的所有实例共享的属性和方法。

大家可能还是没有搞懂,没关系,我从网上找了一幅图,对于大家理解很有帮助。


原型链.jpg

这里我们看到原型对象都有一个constructor

在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。

当我们定义一个函数

function test(){

}

JavaScript 内部会执行如下几个动作
(1) 为该函数添加一个原型(即 prototype)属性
(2) 为 prototype 对象额外添加一个 constructor 属性,并且该属性保存指向函数test的一个引用

当我们把函数 test 作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(test)的 prototype 对象的一个属性_ proto _,所以我们在每一个对象实例中就可以访问构造函数的 prototype 所有拥有的全部属性和方法,就好像它们是实例自己的一样。当然该实例也有一个 constructor属性了(从 prototype 那里获得的),每一个对象实例都可以通过 constrcutor 对象访问它的构造函数,请看下面代码:

var t= new test();
alert(t.constructor === test); // true
alert(t.constructor === test.prototype.constructor);//true

constructor 的出现原本就是用来进行对象类型判断的,但是 constructor 属性易变,不可信赖。我们有一种更加安全可靠的判定方法:instanceof 操作符。

2.原型链

由于_ proto _ 是任何对象都有的属性,而js中万物皆对象,所以会形成一条 _ proto_连接起来的链条,递归访问到_ proto _最终值为null。当js引擎查找对象的属性时,先查找对象本身是否存在该属性,如果不存在,会在原型链上查找。

综合上面所述,我个人的理解如下:

//给数组原型增加一个打乱数组的函数
Array.prototype.shuffle=function(){
var value = this.valueOf(),len = this.length,temp,key;
while(len--){
//随机生成数组的下标
key = Math.floor(Math.random()*len);
//temp为中间交换变量
temp = value[key];
value[key] = value[len];
value[len] = temp;
}
return value;
}
var arr1 = [0,1,2,3,4,5,6,7,8,9];
var arr2 = ['a','b','c','d','e','f'];    
alert(JSON.stringify(arr1.shuffle()));
alert(JSON.stringify(arr2.shuffle()));

我们知道 JS 内置了一些方法供我们使用,比如:
对象可以用 constructor/toString()/valueOf() 等方法;
数组可以用 map()/filter()/reducer() 等方法;
数字可用用 parseInt()/parseFloat()等方法;
其原因就是这些对象继承了其构造函数的原型对象的结果。

上一篇下一篇

猜你喜欢

热点阅读