js 原型与原型链的理解

163 阅读3分钟

关于js的原型其实要从继承说起

什么是继承?

继承主要是为了代码复用。

比如我定义了两个对象(PersonStudent),Person里有两个属性namegetName,Student也需要这同样的属性,但又不想重复的写这部分代码,这样就有一个需求,Student需要从Person里继承这两个属性。

var Person = {
	name:'person',
    getName:function(){
        return this.name;
    }
}
var Student = {}

那么问题就来了,怎么继承?

在js中实现继承最主要是通过原型链(当然也有其他方法)来实现继承。

那么现在再说说原型

什么是原型

js中的每个对象(null与Object.prototype除外)都和另一个对象关联,这个对象就是原型(prototype),js中的每个对象(null与Object.prototype除外)都从原型中继承属性。【摘自js权威指南】

上面的是比较官方的说法,说通俗点,就是js中每个对象都有一个属性【prototype】指向一个对象,js中每个对象可以使用【prototype】指向的对象中的方法,【prototype】被称为对象的原型。

获得对象的原型可通过Object.getPrototypeOf()方法获得。

var obj = {};
Object.getPrototypeOf(obj) //返回obj的原型

在大多数浏览器中实现了通过__proto__属性来查看原型(ie不支持),并可通过这个属性进行设置。

var obj = {};
obj.__proto__  //返回obj的原型

__proto__是浏览器对对象原型的对外暴露,用以直接查询/设置对象的原型,但IE和Opera还不支持

通过__proto__修改原型非常影响性能,推荐使用 Object.create()来实现原型继承

Object.prototype不继承任何属性

原型属性是不可配置的,不可枚举的,不可写的

原型指向那个对象?

这个用几个示例可以说明

var obj1 = {}
obj1.__proto__  === Object.prototype; //返回true

var arr = []
arr.__proto__  === Array.prototype; //返回true

var obj2 = new Object();
obj2.__proto__  === Object.prototype; //返回true
var obj3 = new String('string');
obj3.__proto__  === String.prototype; //返回true

//ES5的方法
var obj4 = Object.create(obj);
//Object.create() 创建一个新对象,其中第一个参数是这个对象的原型,第二个参数对对象的属性进行进一步描述。
obj4.__proto__  === obj; //返回true
原型链

除了Object.prototype,其他原型都是普通对象,普通对象都有原型,最终原型指向Object.prototype,这样的关联关系为原型链。【摘自js权威指南】

例如:

var obj = new Object();
obj.__proto__ === Object.prototype //返回true
//obj的原型直接指向Object.prototype

var obj2 = new String("test"); 
obj2.__proto__ === String.prototype //返回true
String.prototype.__proto__ === Object.prototype //返回true
//obj2的原型指向String.prototype 然后String.prototype的原型指向Object.prototype

实例为了方便用了__proto__,虽然大部分浏览器对__proto__都支持,但不推荐使用这个属性,因为还是存在有浏览器不支持的情况。

例如上述例子可写为

var obj = new Object();
Object.getPrototypeOf(obj)===Object.prototype //返回true

通过原型继承的属性一般是可枚举的(可以被for/in遍历到),但是某些特殊属性是不可枚举的(例如toString)

用for..in遍历对象的时候,__proto__属性也会被遍历,但是function的prototype属性不会被遍历。

function的prototype

每个function对象都有prototype属性,通过new操作符可实现继承

//新建一个构造函数
function Animail(name){
   //初始化代码
   this.name = name
}
//设置prototype的属性
Animail.prototype.getName = function(){
    return this.name;
}
//通过new实例化一个对象
var cat = new Animail("TOM") //新建一个名字叫TOM的猫
//调用继承的属性方法
cat.getName()//返回'TOM'

其中new操作符相当于进行了以下操作

var cat= {};

obj.__proto__ = ClassA.prototype;

ClassA.call(obj);