JS-Prototype-chain

关于JS原型链的理解

原型:每一个对象(除null外)都有另一个对象与之相关联,这个另一个对象便称之为“原型”(prototype),每个对象都是从原型继承属性。

原型链:一系列链接的原型对象的链称为“原型链”(prototype chain)。对象通过原型链的方式实现了相关联(即我们常称作原型链的方式实现了继承)

对象实例化方式

原始模式

1
2
3
4
5
6
var Car = {
color: 'red', //车的颜色
wheel: 4, //车轮数量
}

alert(Car.color);//red

生成两个实例对象

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,然后对这个构造函数实例化了两个对象cat1cat2。( CreateCar称为cat1cat2的原型对象(简称原型), cat1cat2称为CreateCar的实例对象)实例可以访问原型对象上定义的属性和方法

*new 函数构造内部变化:

自动生成一个对象
this指向这个对象
函数自动返回这个对象*

构造函数注意事项

此时CreateCar称之为构造函数,也可以称之类,构造函数就是类

CreateCar 构造函数中this指向CreateCar实例对象即 new CreateCar( )出来的对象cat1cat2

必须带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

CreateCarcat1为例,其属性结构关系如下图所示

JS-Prototype-chain

原型链

什么是原型链

简单来说就是函数原型通过继承关系联系在一起,形成的链式结构就被叫做原型链。简单来说就是在实现原型继承的时候,会将下级函数的原型和上级函数的实例联系起来,那么多个构造函数相互继承,将彼此的原型联系起来就形成了原型链。

实际上所有函数都有自己的原型对象;因为函数在广义上可以认为是对象

对象能作为其他对象的原型对象,也能作为原型对象的实例化对象,由此形成了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原型链

× 请我吃糖~
打赏二维码