logo头像

实现技术的弯道超车

JavaScript原型

JavaScript原型

构造函数 就是与 ‘new’ 运算符一起使用用来创建对象并初始化对象的‘函数’。
实例化对象 就是构造函数创建对象由抽象到具体的过程,这个过程就叫实例化
1
2
3
4
5
6
7
function Person(){} // Person构造函数
var person1 = new Person(); // Person构造函数创建对象,也可叫做实例化
var person2 = new Person();
// 原型属性
Person.prototype.name = 'zhang';
// 实例属性
person1.name = 'z';
原型 prototype和proto

构造函数在创建的过程中,系统自动创建出来与构造函数相关联的一个空的对象,可以由 构造函数.prototype来访问到。
每个函数都会有一个prototype属性,这个属性是一个指针,它默认指向一个Object空对象(称为原型对象),记住只有函数才有,并且通过bind()绑定的也没有。

这个对象Person.prototype的所有属性和方法,都会被构造函数的实例继承。
构造函数的prototype属性(Person.prototype)就是原型对象,也就是实例person1和person2的原型

主流浏览器上在每个对象上(null除外)都支持一个属性,那就是proto,这个属性会指向该对象的原型(自身构造函数的prototype即Person.prototype)
所以 person1.proto === Person.prototype true

constructor属性

constructor属性是原型对象Person.prototype的属性,指向这个原型对象所对应的构造函数。
Person.prototype.constructor === Person true

person1没有.constructor属性,它是通过原型链在原型Person.prtototype上面找到的
Person1.constructor === Person true

构造函数、原型对象、实例化对象三者的关系:

img

#####

原型链 就是依托proto和prototype连接起来的

每一个对象都有自己的原型对象,原型对象本身也是对象,原型对象也有自己的原型对象,这样就形成了一个链式结构,叫做原型链
person1对象—–>Person.prototype——->Object.prototype———>null

当我们读取一个属性的时候,如果在实例属性上找到了,就读取它,不会管原型属性上是否还有相同的属性,这其实就是属性屏蔽。即当实例属性和原型属性拥有相同名字的时候,实例属性会屏蔽原型属性,记住只是屏蔽,不会修改,原型属性那个值还在。但是如果在实例属性(person1.name)上没有找到的话,就会在实例的原型属性Person.prototype上去找(因为Person.prototype的所有属性和方法,都会被构造函数的实例继承。),如果原型上还没有,就继续到原型的原型上去找。

假如在原型对象Person.prototype还是没有找到这个属性,还会继续找,原型对象也是对象所以它也有proto属性,连接它的原型,原型对象Person.prototype的原型就是Object.prototype,所有原型对象都是Object构造函数生成的。
正是因为所有的原型最终都会指向Object.prototype,所以对象的很多方法其实都是继承于此,比如toString()、valueOf(),前面用到的hasOwnProperty,甚至是.constructor、proto。

Object.prototype没有原型,为null,所以它就是前面所提到的尽头。

实例 和 原型对象 存在一个连接。不过,要明确的真正重要的一点就是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。

  1. 检测一个属性存在于实例中,还是原型中,使用方法hasOwnProperty,属性只有存在于实例中才会返回true。
    person1.hasOwnProperty(‘name’) 实例中有name属性返回 true
  2. in操作符则会遍历所有属性,不管是实例上的,还是原型上的。in操作符有两种使用方式,单独使用和在for-in循环中使用
    for in 循环遍历 和 ‘name’ in person1 ture
  3. Object.keys() 此方法可以获取对象的所有可枚举的属性的名字
继承 js继承的几种方法(参考链接第二章更详细):

(1). for-in拷贝继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(){              //父类  
this.name="水煮鱼";
this.age=18;
}

function Son(){ //子类
}
var p=new Person();
var s=new Son();
for(var k in p){
s[k]=p[k];
}
console.log(s.name); //水煮鱼
console.log(s.age); //18

(2). 原型继承:

1
2
3
4
5
6
7
8
9
10
function Human(){  
this.name="香辣虾";
this.age=21;
}
function Man(){
}
Man.prototype=new Human();
var m=new Man();
console.log(m.name); //香辣虾
console.log(m.age); //21

(3). 经典继承:

1
2
3
4
5
6
7
var animal={  
name:"阿咪",
type:"猫科"
};
var a=Object.create(animal) //ES5属性
console.log(a.name); //阿咪
console.log(a.type); //猫科

Object.create()是让一个对象的原型继承另外一个对象;所以虽然a.name和a.age是可以访问成功的,但实际上a本身并没有这些属性,而是a的原型上有这些属性。

完整原型链:

img

难点:
1.Function构造函数可以用Function.proto来访问Function.prototype. 这是因为Function构造函数的构造函数是他本身,作为实例化对象的角色来访问,可行。
2.任何函数都是函数,他都继承Function的所有属性和方法,而Function是内置的构造函数,也是对象,都是继承Object的所有属性和方法。

推荐学习笔记:

初学必看:Javascript 面向对象编程 http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html

深入理解javascript原型和闭包(完结) https://www.cnblogs.com/wangfupeng1988/p/3977924.html

部分资料来自网络,如有侵权,联系删除。转载请注明出处!

支付宝打赏 微信打赏

赞赏是不耍流氓的鼓励