JS高级-构造函数与原型

8 篇文章 0 订阅

JS高级-构造函数与原型

1.实例、静态成员以及构造函数

1.1实例成员
  • 实例成员就是通过构造函数内部的this添加的成员
  • 实例成员只能通过实例化的对象来访问,不可以通过构造函数来访问实例成员
function Fun(name,age,sex){
    this.name=name;
    this.age=age;
    this.sex=sex;
    this.test=function(){
        console.log('实例成员')
    }
}
var fun=new Fun('小阿涵',22,'男');
fun.test();
console.log(fun.name);
console.log(fun.test);

在这里插入图片描述

1.2静态成员
  • 静态成员是指在构造函数本身上添加的成员
  • 静态成员只能通过构造函数来访问,不能通过对象来访问
function Fun(name,age,sex){
    this.name=name;
    this.age=age;
    this.sex=sex;
    this.test=function(){
        console.log('实例成员')
    }
}
var fun=new Fun('小阿涵',22,'男');
fun.test();
console.log(fun.name);
Fun.school='ZZGS';
console.log(Fun.school);

在这里插入图片描述

1.3构造函数的问题

构造函数虽然好用,但是存在内存浪费的问题

function Star(name,age){
    this.name=name;
    this.age=age;
    this.sing=function(){
        console.log('111')
    }
}
var ldh=new Star('GG',18)
var zxy=new Star('JJ',19)
  • 上述代码中,sing()方法是一个函数,函数属于复杂数据类型,在我们的内存中,无论是ldh对象中还是zxy对象,该方法都会单独开辟出一个新的空间来存放该函数,因此便产生了内存浪费的问题
  • 我们可以通过prototype属性来改善上述问题
    在这里插入图片描述

2.原型

什么是原型?

原型是一个对象,我们也称为prototype为原型对象

原型的作用是什么?

共享方法,节约内存资源

2.1构造函数原型 prototype
  • 构造函数可以通过原型分配的函数是所有对象所共享的
  • 我们所创建的每一个函数,解析器都会向函数中添加一个prototype
  • 这个属性对应着一个对象,这个对象就是所谓的原型对象
  • 该对象的所有属性和方法,都会被构造函数所拥有
  • 如果函数作为普通函数调用prototype没有任何作用
  • 我们可以把那些不变的方法,直接定义到prototype对象上,这样所有对象的实例就可以共享这些方法
2.2给原型对象添加属性(一般都是方法)

一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象上

function Star(name,age){
    this.name=name;
    this.age=age;
}
Star.prototype.sing=function(){
    console.log('原型方法')
}
var ldh=new Star('GG',18)
var zxy=new Star('JJ',19)
ldh.sing();

在这里插入图片描述

2.3对象原型__proto__

对象都会有一个__proto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在

function Star(name,age){
    this.name=name;
    this.age=age;
}
Star.prototype.sing=function(){
    console.log('原型方法')
}
var ldh=new Star('GG',18)
var zxy=new Star('JJ',19)
conslog.log(ldh);//对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象

在这里插入图片描述

  • __proto__和prototype两个属性是等价的
  • __proto__对象原型的意义就在于为对象的查找机制提供了一个方向,或者说一条路线,但是他是一个非标准属性
    在这里插入图片描述
2.4 construct 构造函数

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

function Star(name,age){
    this.name=name;
    this.age=age;
}
Star.prototype.sing=function(){
    console.log('原型方法')
}
var ldh=new Star('GG',18)
var zxy=new Star('JJ',19)
conslog.log(ldh);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SoKHQvYX-1652169794046)(img/11.png)]

2.5构造函数、实例、原型对象三者之间的关系

在这里插入图片描述

3.显示原型与隐式原型

(1)每个函数都有function都有一个prototype,即显示原型

function fun(){

}
console.log(fun.prototype);

在这里插入图片描述

(2)每个实例对象都有一个__proto__,可称为隐式原型

function Fun(){

}
console.log(Fun.prototype);
var fun=new Fun();
console.log(fun.__proto__)

在这里插入图片描述

(3)对象的隐式原型的值为其对应构造函数的显式原型的值

function Fun(){

}
console.log(Fun.prototype);
var fun=new Fun();
console.log(fun.__proto__)
console.log(Fun.prototype===fun.__proto__);//true

(4)总结:

  • 函数的prototype属性:在定义函数时自动添加的,默认值是一个Object空对象
  • 对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
  • 我们可以直接操作显式原型,无法直接操作隐式原型(ES6之前)
  • prototype属性 与 __proto__属性 两者都为引用类型

4.原型链

  • 只要是对象就有__proto__原型,指向我们的原型对象
function Star(name,age){
    this.name=name;
    this.age=age;
}
Star.prototype.sing=function(){
    console.log('我是Star原型对象的方法')
}
var ldh=new Star('ZZ',22);
console.log(Star.prototype);
console.log(Star.prototype.__proto===Object.prototype);
//Object.prototype原型对象里面的__proto__指向空
console.log(Object.prototype.__proto__);

在这里插入图片描述

因此我们的每一个对象实例都有一个__proto__原型,这个原型指向我们的原型对象prototype,这个原型对象里面也有一个__proto__,这个原型指向我们的Object的原型对象,Object原型对象里面的也有__proto__,但是这个__proto__指向空,这样的指向方向就构成了我们的原型链
在这里插入图片描述

5.JavaScript的成员查找机制(规则)

JavaScript的成员查找机制将遵循原型链的指向进行查找,一层一层逐步查找,同时遵循就近原则

  • (1)当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有这个属性
  • (2)如果没有就差找他的原型(也就是__proto__指向的prototype原型对象)
  • (3)如果还没有就查找原型对象的原型(Object的原型对象)
  • (4)依次类推一直找到Object为止(null)

6.原型对象的this指向问题

  • (1)在构造函数里,里面的this指向的是我们的对象实例
  • (2)原型对象函数里面的this指向的是 实例对象
function Star(name,age){
    this.name=name;
    this.age=age;
}
var that;
Star.prototype.sing=function(){
    console.log('我是Star原型对象的方法');
    that=this;
}
var ldh=new Star('ZZ',22);
ldh.sing();
console.loh(that===ldh);//true

因此不管是构造函数里面的this还是原型对象里面的this都指向我们的实例对象

7.扩展内置对象

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

Array.prototype.sum=function(){
    var num=0;
    for(var i=0;i<this.length;i++){
        num+=this[i];
    }
    return num;
}
var arr=[1,2,3];
console.log(arr.sum());
console.log(Array.prototype);

在这里插入图片描述

8.继承

ES6之前没有给我们提供extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承

8.1 call()方法

调用这个函数,并且修改函数运行时的this指向
语法:函数名.call(thisArg,arg1,arg2…)
thisArg:当前调用函数this的指向对象

function fun(){
    console.log('zz');
    console.log(this);
}
var a={
    name:'zh'
}
fun.acll();//this ===> window
fun.call(a);//this ===> a

8.2借用父构造函数继承属性

我们通常将共有的属性写到构造函数里面

//父构造函数
function Father(name,age){
    this.name=name;
    this.age=age;
}
//子构造函数
//通过call方法修改子构造函数的this指向
function Son(name,age){
    Father.call(this,name,age);
}
var son=new Son('zz',22);
console.log(son);

在这里插入图片描述

8.3借用父构造函数继承方法

我们通常将共有的方法写到原型对象上

//父构造函数
function Father(name,age){
    this.name=name;
    this.age=age;
}
Father.prototype.money=function(){
    console.log('父方法');    
}
//子构造函数
//通过call方法修改子构造函数的this指向
function Son(name,age){
    Father.call(this,name,age);
}
//让Son的原型等于Father的实例对象
Son.prototype=new Father();
//因为上述代码我们修改了原型对象,因此需要利用constructor来指向原来的原型对象
Son.prototype.constructor=Son;
//单独给子类添加一个方法
Son.prototype.exam=function(){
    console.log('考试')
}
var son=new Son('zz',22);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);

在这里插入图片描述

因此可以根据原型链的指向方式来更好理解上述代码

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值