原型原型链

原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。

通过该构造函数产生的对象,可以继承该原型的属性和方法。

原型本质上是一个对象,这个对象是在构造函数身上的,为prototype,称为原型对象

原来在构造函数中直接赋给对象实例的值,可以直接赋给它们的原型。

//构造函数的特点:大驼峰。
//构造函数是为了生产对象的,使用new来生产对象
//构造函数通过不停的调用可以产生多个相似且独立的对象
//构造函数和原型的this都指向实例化对象

// Person.prototype   -->  原型
// Person.prototype = {}  是祖先
Person.prototype.LastName = 'Zhao'
Person.prototype.say = function(){
    console.log('hi');
}
function Person(){

}
let p1 = new Person()
let p2 = new Person()
console.log(p1.LastName);  //Zhao  会继承prototype上的属性

原型的应用

提取共有属性

利用原型的特点和概念,可以提取共有属性。

function Car(color,owner){
    this.owner = owner
    this.color = color
    this.height = 1400
    this.lang = 4900
    this.name = 'BMW'
}
let car = new Car('red','tom')
let car1 = new Car('green','tony')
console.log(car);  //Car {owner: 'tom', color: 'red', height: 1400, lang: 4900, name: 'BMW'}
console.log(car1); //Car {owner: 'tony', color: 'green', height: 1400, lang: 4900, name: 'BMW'}

但是每次实例化对象都要执行一次this.height,this.lang,this.name,就造成了代码的冗余。

所以可以通过继承的规则,将它们放入原型上,给原型赋值后,实例化对象就只执行一次代码。

Car.prototype.height = 1400
Car.prototype.lang = 4900
Car.prototype.name = 'BMW'

//也可以这样写
/*
Car.prototype = {
    height:1400,
    lang:4900,
    name:'BMW'
}
*/


function Car(color,owner){
    this.owner = owner
    this.color = color
}
let car = new Car('red','tom')
let car1 = new Car('green','tony')
console.log(car);  //Car {owner: 'tom', color: 'red'}
console.log(car1); //Car {owner: 'tony', color: 'green'}
console.log(car.height);  //1400
console.log(car1.name);  //'BMW'

把不变的方法,直接定义在prototype对象上,这样所有的对象的实例可以共享这些方法。

constructor属性

每个原型对象里面都有一个constructor属性,也叫构造器

该属性指向原型对象的构造函数

function Car(){

}
let car = new Car()
console.log(Car.prototype);
console.log(car.constructor);

并且可以手动修改constructor的值

原型对象__proto__

对象都有一个属性__proto__ 指向构造函数的prototype原型对象。

简单来说,对象原型指向原型对象

之所以对象能够使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。

__proto__不是js标准属性,所以浏览器用[[prototype]]表示

[[prototype]]和__proto__意义相同

对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数。

Person.prototype.name = 'abc'
function Person(){

}
let person = new Person()
console.log(person.__proto__);  //{name: 'abc'}

__proto__可以被修改

Person.prototype.name = 'abc'
function Person(){
    /**
     * 构造函数有一个定义this的过程   
     * let this = {
     *   __proto__ : Person.prototype
     *  }
    */
}
let person = new Person()
let obj = {
    name:'cba'
}
person.__proto__ = obj
// person继承的对象就变成了obj
console.log(person.name);  //'cba'

原型继承

继承是面向对象编程的另一个特征,通过继承进一步提升代码封装,JavaScript中大多是借助原型对象实现继承的特性。

let Person = {
    eyes:2,
    mouse:1
}
// 想要Woman和Man两个构造函数都继承Person对象的属性
// 则可以把Person对象放到原型上,也就是说的公共的部分放到原型上,称之为原型继承

function Woman(){

}
//Woman通过原型继承
Woman.prototype = Person
let lily = new Woman()
function Man(){

}
//Man通过原型继承
Man.prototype = Person
let tom = new Man()

按道理prototype里有一个constructor属性,可是这两个原型都没有这个属性

那就需要让它指回构造函数

//Woman通过原型继承
Woman.prototype = Person
//指回原来的构造函数
Woman.prototype.constructor = Woman

//Man通过原型继承
Man.prototype = Person
//指回原来的构造函数
Man.prototype.constructor = Person

现在,想给Woman添加一个方法,只想让Woman有这个方法,那就在Woman的原型上添加

//给女人添加一个生孩子的方法
Woman.prototype.haveBaby = function (){
    console.log('生孩子');
}

发现lily有这个方法,而tom也有这个方法,为什么呢?

Woman和Man继承的是同一个原型,它们的原型是一模一样的

Woman.prototype === Man.prototype 为true

根据引用类型的特点,他们指向同一个对象,修改其中一个另一个就会受到影响。他们的原型对象都指向Person,修改就会直接修改到Person

怎么解决对象一样,而结构不一样呢?

可以使用深拷贝,让它们两个的原型都等于JSON.parse(JSON.stringify(Person))

也可以使用构造函数,因为构造函数new出来的对象的特性就是结构一样,但是对象不一样。

//用构造函数的形式
function Person(){
    this.eyes = 2
    this.mouse = 1
}
function Woman(){

}
//Woman通过原型继承
Woman.prototype = new Person()
//指回原来的构造函数
Woman.prototype.constructor = Woman
//给女人添加一个生孩子的方法
Woman.prototype.haveBaby = function (){
    console.log('生孩子');
}
let lily = new Woman()
function Man(){

}
//Man通过原型继承
Man.prototype = new Person()
//指回原来的构造函数
Man.prototype.constructor = Person
let tom = new Man()

原型链

基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,将原型对象的链状结构关系称为原型链。

在 JavaScript 中,每个对象都有一个原型对象。这个原型对象又有自己的原型对象,它们形成了一个原型链。

Object.prototype是绝大多数对象的最终原型对象

function Grand(){

}
let grand = new Grand()

Father.prototype = grand
function Father(){
   
}
let father = new Father()

Son.prototype = father
function Son(){
    
}
let son = new Son()

/*
son.__proto__指向Son.prototype

Son.prototype指向father

father.__proto__指向Father.prototype

Father.prototype指向grand

grand.__proto__指向Grand.prototype

Grand.prototype__proto__指向Object.prototype

Object.prototype.__proto__指向为null

原型链和作用域链一样,是一层一层进行查找的
*/

查找规则:

  1. 当访问一个对象的属性或方法时,首先查找对象本身有没有该属性或方法

  2. 如果对象本身没有这个属性或方法,则去它的原型对象中查找,也就是__proto__指向的prototype

  3. 如果原型对象中也没有这个属性或方法,它会继续查找原型对象的原型对象

  4. 依次类推直到到达原型链的末尾(Object.prototype)

除了原型继承还有一些继承的方式,留到下一篇文章来讲。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值