
关于JS原型链的理解
原型:每一个对象(除null外)都有另一个对象与之相关联,这个另一个对象便称之为“原型”(prototype),每个对象都是从原型继承属性。
原型链:一系列链接的原型对象的链称为“原型链”(prototype chain)。对象通过原型链的方式实现了相关联(即我们常称作原型链的方式实现了继承)
对象实例化方式
原始模式
1 2 3 4 5 6
| var Car = { color: 'red', wheel: 4, }
alert(Car.color);
|
生成两个实例对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| var Car = { color: 'red', wheel: 4, } var Car2 = { color: 'blue', wheel: 4, } ```js 这样的写法有两个缺点,一是如果多生成几个(100个!)实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。
#### 原始模式的改进
我们可以写一个函数,解决代码重复的问题。 ```js function createCar(color,wheel) { return { color:color, wheel:wheel } }
var cat1 = createCar("红色","4"); var cat2 = createCar("蓝色","4");
alert(cat1.color);
|
工厂模式
1 2 3 4 5 6 7 8 9 10 11
| function createCar(color,wheel){ //createCar工厂 var obj = new Object; //或obj = {} 原材料阶段 obj.color = color; //加工 obj.wheel = wheel; //加工 return obj; //输出产品 } //实例化 var cat1 = createCar("红色","4"); var cat2 = createCar("蓝色","4");
alert(cat1.color);//红色
|
构造函数模式
为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。
所谓”构造函数”,其实就是一个普通函数,但是内部使用了this
变量。对构造函数使用new
运算符,就能生成实例,并且this
变量会绑定在实例对象上。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function CreateCar(color,wheel){//构造函数首字母大写 //不需要自己创建对象了 this.color = color;//添加属性,this指向构造函数的实例对象(cat1、cat2) this.wheel = wheel;//添加属性
//不需要自己return了
}
//实例化 var cat1 = new CreateCar("红色","4"); var cat2 = new CreateCar("蓝色","4"); alert(cat1.color); //红色
|
首先定义一个构造函数CreateCar
,然后对这个构造函数实例化了两个对象cat1
和cat2
。( CreateCar
称为cat1
和cat2
的原型对象(简称原型), cat1
,cat2
称为CreateCar
的实例对象)实例可以访问原型对象上定义的属性和方法
*new
函数构造内部变化:
自动生成一个对象
this
指向这个对象
函数自动返回这个对象*
构造函数注意事项
此时CreateCar
称之为构造函数,也可以称之类,构造函数就是类
CreateCar 构造函数中this
指向CreateCar
实例对象即 new CreateCar( )
出来的对象cat1
和cat2
必须带new
构造函数首字母大写,这是规范,官方都遵循这一个规范,如Number()
Array()
理解 constructor,protototype,proto 属性
每个构造函数都有一个prototype
属性,这个属性是一个指针,指向一个prototype对象
,而这个prototype对象
中保存着构造函数的原型属性
和一个constructor
属性,该constructor
属性,这个属性也是一个指针,指向了构造函数CreateCar
本身.
实例是通过构造函数创建的。无prototype属性,但实例一创造出来就具有constructor
属性(指向构造函数)和proto
属性(指向该实例的原型对象)
cat1和cat2会自动含有一个constructor属性,指向它们的构造函数,即CreateCar
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function CreateCar(color,wheel){ //构造函数首字母大写 //不需要自己创建对象了
this.color = color; //添加属性,this指向构造函数的实例对象(cat1、cat2) this.wheel = wheel; //添加属性
//不需要自己return了 }
//实例化 var cat1 = new CreateCar("红色","4"); var cat2 = new CreateCar("蓝色","4"); alert(cat1.color); //红色
alert(cat1.constructor == CreateCar); //true alert(cat2.constructor == CreateCar); //true
|
以CreateCar
和cat1
为例,其属性结构关系如下图所示

原型链
什么是原型链
简单来说就是函数原型通过继承关系联系在一起,形成的链式结构就被叫做原型链。简单来说就是在实现原型继承的时候,会将下级函数的原型和上级函数的实例联系起来,那么多个构造函数相互继承,将彼此的原型联系起来就形成了原型链。
实际上所有函数都有自己的原型对象;因为函数在广义上可以认为是对象
对象能作为其他对象的原型对象,也能作为原型对象的实例化对象,由此形成了prototype chain原型链
所有的对象的原型对象如果一层层往上“回溯”,最后可以回溯到Object.prototype
;而Object.prototype
的原型对象是null
,null
没有自己的原型对象!!!
在面向对象原型链中常见的函数
1.hasOwnProperty()
判断一个属性是否是自有属性,还是继承原型属性。必须用字符串指定该属性。如果是的话返回true,否则的返回false
1 2 3 4 5 6 7
| 1. function fn(index){ 2. this.index = index; 3. } 4. fn.prototype.name = '二狗'; 5. var obj = new fn(88); 6. console.log( obj.hasOwnProperty('index') );//true 7. console.log( obj.hasOwnProperty('name') );//false
|
2.isPrototypeOf()
判断该对象的原型是否为xxxxx。 返回true 或 false
Obj.constructor.prototype.isPrototypeOf(Obj) //true
原型链中对象属性的查找规则
读取对象的某个属性时,js引擎会先在对象本身属性上寻找,如果找不到,那么去其原型对象的prototype
上找(不会查找其原型对象的内部属性!!),如果没有的话就会沿着原型链向上查找,一层一层往上”回溯”,直至null.找到的话就返回属性值,如果都没有的话就会返回undefined。所以如果找寻某个不存在的属性,会将完整的原型链遍历一变,对性能影响较大。
举个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| //原型链中属性的查找 function Person(name,color){ this.name=name; this.color=color; } function Teacher(name,age){ Teacher.color = "红色"; this.name=name; this.age=age; } //实现继承 Teacher.prototype=new Person(); Teacher.prototype.book="书本"; var t1=new Teacher("aa",32);
//查找属性 //1.当Teacher中含有name属性,不管有没有赋值都表明在Teacher中含有name属性 console.log(t1.name); //aa
//2.当查找Teacher中没有的属性的时候,但是Teacher的原型中有该属性 console.log(t1.book); //书本
//3.当查找Teacher原型和本身都没有属性的时候,但是上级函数中有该属性的时候 console.log(t1.color); //undefined
|
以上的三种情况就是一个很明显的利用原型链查找属性的例子,可以写个demo简单实验一下,只要测试过之后肯定能够很清晰的理解啦。
原型继承
利用call()
及for in
继承,如
1 2 3 4 5 6 7 8 9 10
| 1.function inherit(){ 2. 3. fn.call(obj3,'旺财'); 4. for(key in fn.prototype){ 5. if(!obj3[key]){ 6. obj3[key] = fn.prototype[key]; 7. } 8. 9. } 10. }
|
1 2 3 4 5 6 7 8 9 10 11
| 1.function inherit(constructor,obj,ownAttr){ 2. constructor.call(obj3,ownAttr); 3. for(key in constructor.prototype){ 4. if(!obj[key]){ 5. obj[key] =constructor.prototype[key]; 6. } 7. 8. } 9. 10. 11.}
|
构造函数实例方式继承
1.利用obj.constructor.prototype 继承对象 自身属性及 继承属性
1 2 3 4 5 6 7 8 9 10 11 12
| 1. function fn(name){ 2. this.name = name; 3. } 4. fn.prototype.index = '88'; 5. function fn2(age){ 6. this.age = age; 7. } 8. fn2.prototype = new fn('旺财'); 9. var obj = new fn2(66); 10. console.log(obj.name);//旺财 11. console.log(obj.index);//88 12. console.log(obj.age);//88
|
2.利用 prototype 继承对象 自身属性及 继承属性
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1. function fn(name){ 2. this.name = name; 3. } 4. fn.prototype.index = '88'; 5. 5. function fn2(age){ 6. this.age = age; 7. } 8. 8. var obj = new fn('二狗'); 9. fn.prototype = Object.create(fn.prototype); 10. console.log(obj.name);//二狗 11. console.log(obj.index);//88
|
在上面例子中 可以发现,给对象的constructor.prototype添加方法属性 对象就会继承
obj.constructor.prototype 与 对象obj.proto 的关系 ,带__为非标准属性
1 2 3 4 5
| 1.function fn(name){ 2. this.name = name; 3. } 4.var obj = new fn('二狗'); 5. console.log( obj.__proto__ === fn.prototype ) //true,
|
3.一个对象继承其他对象
对象继承对象
1 2 3 4
| 1.var obj = {name : 'hello'} 2.var obj2 = {age:30} 3.obj2.__proto__ = obj; 4.console.log( obj2.name);//hello
|
继承构造函数实例对象
1 2 3 4 5 6 7 8 9
| 1.function fn(name){ 2. this.name = name; 3. } 4. var obj = new fn('hello'); 5. var obj2 = { 6. age:30, 7. __proto__:obj 8. } 9. console.log(obj2.name);//hello;
|
进阶理解
MDN原型链