构造函数、实例、原型对象、继承

一、构造函数与原型对象之间的关系:

有一个Star构造函数,每一个构造函数里面都有一个原型对象,是通过构造函数的prototype指向这个原型对象的

同样在这个原型对象里面也有一个属性叫constructor,它又指回了构造函数

可以把构造函数看为是父亲,父亲通过prototype指向儿子原型对象,告诉别人看我有一个厉害的儿子叫原型对象,而原型对象里面又有一个属性constructor,又指回了构造函数说,看我有一个厉害的老爹,两人互相吹捧了一番

二、构造函数、实例对象与原型对象之间的关系:

我们可以通过构造函数创建一个实例对象,只要new了构造函数就可以产生一个实例对象,所以构造函数又可以指向这个对象实例,我们知道在这个实例对象中也有一个原型__proto__,这个__proto__指向的是Star原型对象,当然了我们对象实例里面也有一个constructor,它可以指回构造函数

在这里插入图片描述

三、原型链

只要是对象就有__proto__原型,指向原型对象

原型对象里面的__proto__原型指向的是Object.prototype

function Star(uname,age){
  this.uname = uname;
  this.age = age;
}
Star.prototype.sing = function(){
  console.log("我会唱歌");
}
var zxz = new Star('张信哲',18);
// 只要是对象就有__proto__原型,指向原型对象
console.log(Star.prototype);
console.log(Star.prototype.__proto__ === Object.prototype);// true
// 我们Star原型对象里面的__proto__原型指向的是Object.prototype
console.log(Object.prototype.__proto__);
// 我们的Object.prototype原型对象里面的__proto__原型 指向为null

在这里插入图片描述

四、JavaScript对象成员查找机制(规则)

  • 当访问一个对象的属性(包含方法)时,首先查找这个对象自身有没有该属性

  • 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)

  • 如果还没有就查找原型对象的原型(Object的原型对象)

  • 依次类推一直找到Object为止(null)

  • __proto__对象原型的意义就在于为对象成员查找机制提供一个方向或者说一条路线。

var that;
function Star(name,age){
// 在构造函数中,里面的this指向的是实例对象
this.name = name;
this.age = age;
}
Star.prototype.sing = function(){
// 原型对象函数中的this,指向的是实例对象,mxq对象
  that = this;
  console.log("我会唱歌");
}
// Star.prototype.sex = "女"
Object.prototype.sex = "女"
var mxq = new Star("萌小七",18)
// mxq.sex = "女"
console.log(mxq.sex);// 输出女
mxq.sing();
console.log(that === mxq);// true
原型对象this指向:
	在构造函数中,里面的this指向的是实例对象
	原型对象函数中的this,指向的是实例对象

五、利用原型对象扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。

注:数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式。

Array.prototype.sum = function(){
  var total = 0
  for(var i=0;i<this.length;i++){
    total += this[i]
  }
  return total;
}
var arr = [5,6,7];
arr.sum(); // 18

六、继承

ES6之前并没有给我们提供extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承,用原型链实现对原型属性和方法的继承,借用构造函数技术来实现实例属性的继承。

1、借用构造函数继承父类型属性

核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。

call() 作用:调用这个函数并且修改函数运行时this的指向

fun.call(thisArg,arg1,arg2,...)

thisArg:当前调用函数this的指向对象

arg1,arg2:传递的其他参数

function fn(x,y){
  console.log(this);
  console.log("我想出去玩");
  console.log(x+y)
}
var obj = {
  name:"萌小七"
}
fn();// this指向的是window,我想出去玩
// call()可以调用函数
fn.call();// this指向的是window,我想出去玩
// call() 可以改变这个函数的this指向,此时这个函数的this就指向了obj这个对象
fn.call(obj,1,2);// {name: "萌小七"} 我想出去玩 3

举例说明:借用父构造函数继承属性

function Father(name,age){
  // this指向父构造函数的对象实例
  this.name = name
  this.age = age
}
function Son(name,age,score){
  // this指向子构造函数的对象实例
  Father.call(this,name,age)
  this.score = score
}
var son = new Son("小丸子",18,98)
console.log(son); // 输出:Son {name: "小丸子", age: 18, score: 98}

此时子构造函数继承了父构造函数的属性

2、利用原型对象继承方法: Son.prototype = new Father();

function Father(name,age){
  // this指向父构造函数的对象实例
  this.name = name
  this.age = age
}
function Son(name,age,score){
  // this指向子构造函数的对象实例,构造函数来复制父类的属性给Son实例
  Father.call(this,name,age)
  this.score = score
}
Father.prototype.money = function(){
  console.log("父亲要挣钱")
}
// Son.prototype = Father.prototype
// 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起改变
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的原型对象
Son.prototype.constructor = Son
Son.prototype.school = function(){
  console.log("孩子要上学")
}
var son = new Son("小丸子",18,98)
console.log(son);
console.log(Father.prototype);

在这里插入图片描述
在这里插入图片描述

优点:

父类的方法可以被复用

父类的引用属性不会被共享

子类构建实例时可以向父类传递参数

缺点:

第一次调用Father():给Son.prototype写入两个属性name,age。

第二次调用Father():给instance1写入两个属性name,age。

实例对象son上的两个属性就屏蔽了其原型对象Son.prototype的两个同名属性。所以,组合模式的缺点就是在使用子类创建实例对象时,其原型中会存在两份相同的父类实例的属性/方法。这种被覆盖的情况造成了性能上的浪费。


寄生组合继承(最优方案)
组合继承会有两次调用父类的构造函数而造成浪费的缺点,寄生组合继承就可以解决这个问题。

核心在于inheritPrototype(son, father),让子类的prototype指向父类原型的拷贝,这样就不会调用父类的构造函数,进而引发内存的浪费问题。

function inheritPrototype(son, father) {
  // 修正子类原型对象指针,指向父类原型的一个副本 (用object()也可以) 
  son.prototype = Object.create(father.prototype)
  // 增强对象,弥补因重写原型而失去的默认的constructor属性
  son.prototype.constructor = son
}
function Father(name,age){
// this指向父构造函数的对象实例
this.name = name
this.age = age
}
function Son(name,age,score){
// this指向子构造函数的对象实例,构造函数来复制父类的属性给Son实例
  Father.call(this,name,age)
this.score = score
}
Father.prototype.money = function(){
  console.log("父亲要挣钱")
}
inheritPrototype(Son, Father)
Son.prototype.school = function(){
console.log("孩子要上学")
}
var son = new Son("小丸子",18,98)
console.log(son);
console.log(Father.prototype);

在这里插入图片描述
【转载】https://mp.weixin.qq.com/s/FZTgakDp-jQc1Ic4NT7WYg

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值