构造函数原型和继承

一、构造函数原型

我们先学习两个概念

1.1、实例成员

是在构造函数内部通过this添加的成员,实例成员只能通过实例化的对象来访问。

// 实例成员
function Star(name, age) { 
	this.name = name; 
	this.age = age; 
	this.sing = function() { 
		console.log('唱歌'); 
	} 
}
// name,age,sing都是实例成员
var s1 = new Star('小明', 18); 
console.log(s1.name);//实例成员只能通过实例化的对象来访问

1.2、静态成员

在构造函数本身添加的成员,静态成员只能通过构造函数来访问。

// 静态成员
function Star(name, age) { 
	this.name = name; 
	this.age = age; 
	this.sing = function() { 
		console.log('唱歌'); 
	} 
}
Star.sex = '男'; //静态成员
console.log(Star.sex);//静态成员只能通过构造函数来访问

2、构造函数内存分配问题

先定义一个构造函数

function Fn(a,b){
      this.a = a;
      this.b = b;
      this.add = function(){
          console.log(a+b);           
      }
}

var f1 = new Fn(12,8);
var f2 = new Fn(14,8)

在这里插入图片描述
这样就造成了,每实例化一个对象都会开辟出一块空间存储构造函数的方法,造成内存浪费的问题。

3、构造函数原型prototype

  • 那怎样才能大大节省内存空间呢?

JavaScript 规定,每一个构造函数都有一个prototype 属性,指向另一个对象,即原型对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

//不使用prototype情况下
function Fn(a,b){
     this.a = a;
     this.b = b;
     this.add = function(){
         console.log(a+b);           
     }
}
var f1 = new Fn(12,8);
var f2 = new Fn(14,8)
//虽然调用的同一方法,但是存储地址不同,故不相等
console.log(f1.add==f2.add);// false

我们可以给函数的原型添加函数,这样的话,函数所有的实例化对象都可以使用这个函数,大大减少函数重复占用内存。

// 使用构造函数的原型对象
function Fn(a, b) {
        this.a = a;
        this.b = b;
    }

//给构造函数的原型对象添加方法
Fn.prototype.add = function (a, b) {
    console.log(a + b);
}
//给构造函数的原型对象添加属性
Fn.prototype.num = '求和';

var f1 = new Fn(12, 8);
var f2 = new Fn(14, 8)

// 构造函数通过原型对象添加的函数是所有实例对象所共享的,
// 所以即使不同实例对象调用也是相等的
console.log(f1.add == f2.add);// true
console.log(f2.num == f1.num);// true

4、对象原型

即任何对象都会有一个属性 __proto__指向构造函数的 prototype 原型对象;
实例对象的__proto __属性同样指向构造函数的原型对象。

用一张图来帮助理解三者的关系:
在这里插入图片描述
ps:图画的一般般,将就看看!

//__prito__属性
function Play(name){
        this.name = name;
    }

Play.prototype.hobby = function(){
    console.log(this.name+'喜欢游泳');      
}

Play.prototype.age = '19';
Play.prototype.sex = '女';

var p1 = new Play('小丽');
var p2 = new Play('小芳');

p1.hobby();//小丽喜欢游泳
p2.hobby();//小芳喜欢游泳
console.log(p1.hobby==p2.hobby);//true

//证明:实例对象的__proto __属性和构造函数
//的prototype属性指向相同,都为构造函数的原型对象
console.log(Play.prototype==p1.__proto__);//true

5、constructor

在对象的原型( proto)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指向构造函数本身。

注意:如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数。

function Play(name) {
   this.name = name;
}

Play.prototype.hobby = function () {
    console.log(this.name + '喜欢游泳');
}

var p1 = new Play('小丽');

// 以下都指向构造函数
console.log(Play.prototype.constructor);
console.log(p1.__proto__.constructor);

6、查找机制

● 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性;
● 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象);
● 如果还没有就查找原型对象的原型(Object的原型对象),
依此类推一直找到 Object 为止(null)。

function Play(name) {
    this.hobby = name;
}

Play.prototype.hobby = '游泳';

var p1 = new Play('跑步');

// 先查找自身有没有
console.log(p1);
console.log(p1.hobby);

7、原型链

每一个实例对象都有一个__proto__属性,指向构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。

示意图:
在这里插入图片描述
简单来说,层层传递的赶脚,从实例对象往上传递,直到最顶层(Object)。

8、原型对象中this指向

function Star(uname, age) { 
	this.uname = uname; 
	this.age = age; 
}
var that; 
Star.prototype.sing = function() { 
	console.log('我会唱歌'); 
	that = this; 
}

var ldh = new Star('刘德华', 18); 

一句话总结:
在构造函数中,this指向的是实例对象
原型对象中,this指向的还是实例对象

二、继承

2.1、理解继承

什么是继承?

字面意思来看就是继承,就是你理解的那个继承。
同时继承也是面向对象中三大要素之一,即(封装,多态,继承)。

继承能干什么?

可以实现对象和方法的复用,从而提高代码的利用率,也是代码优化的一种方案。

2.2、call方法实现继承

注意,我现在所说的继承都是ES6之前的,构造函数的继承

  • call()

可以修改this的指向,同时调用函数,弊端是无法继承方法

  • 语法结构
	构造函数.call(this, 参数1,参数2)
// 构造函数的继承,es6之前
function Fn1(name,age){
    this.name = name;
    this.age = age;
}

Fn1.prototype.drink = function(){
        console.log('太渴了');       
}

function Fn2(name,age,sex){
    //通过call()方法改变父方法this指向为子方法
    //但此方法只能继承属性
    Fn1.call(this,name,age);//此时Fn1的name,age属性已经继承到Fn2了,括号里的this是Fn2中的this
    this.sex = sex;
}

var f = new Fn2('zz',19,'女');
console.log(f.name);
console.log(f.age);
// f.drink()//报错  无法继承方法

2.3、借用原型对象继承方法

//实现构造函数继承 父类的属性
function Fath(age,sex){
    this.age = age;
    this.sex = sex;
}
Fath.prototype.hobby = function(){
    console.log('是个'+this.sex+'的,'+'喜欢喝茶');        
}

function Son(age,sex,name){
    // Fath.call(this,age,sex)//不推荐,只能继承属性
    this.age = age;
    this.sex = sex;
    this.name = name;
}
// 父构造方法的实例指向子构造方法的原型对象
Son.prototype = new Fath(21,'女')

Son.prototype.sport = function(){
    console.log(this.name+'喜欢打球');    
}

var f1 = new Fath(19,'女');
var s1 = new Son(18,'女','小梅')
console.log(s1.age);
s1.hobby();//是个女的,喜欢喝茶
s1.sport()

f1.hobby()//是个女的,喜欢喝茶
// f1.sport()//喜欢打球 报错 父函数无法访问子函数中的方法

2.4、class继承

我们现在说说es6之后类的继承

//ES6中 class 的继承
class F1{
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    eat(){
        console.log('吃鸡腿');      
    }
}

// F2继承F1
class F2 extends F1{
    constructor(name,age,sex){
        //子类中用super去调用父类的constructor
        super(name,age);
        this.sex = sex;
    }
}

var f2 = new F2('小红',19,'女');
console.log(f2.age);
f2.eat()

经典案例 为数组对象增加内置方法

// 给Array本地对象增加一个原型方法,它用于删除数组条目中重复的条目
Array.prototype.quchong = function(arr){
    var new_arr = [];
    for(var i=0;i<arr.length;i++){
        if(new_arr.indexOf(arr[i])==-1){
            new_arr.push(arr[i])
        }
    }
    return new_arr;
}

var resu = Array.prototype.quchong([23,23,45,45,67,23,67,89,89,23]);
console.log(resu);

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

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值