原型(prototype)和原型链(__proto__)继承

在这里插入图片描述
js函数里面对象有三种状态:
var a; //私有的
this.a // 公有的
prototype.a //原型上的,通过__proto__访问

function fn(){
 var a = 'vara';
 this.a = 'thisa';
 fn.prototype.a = 'proa';
}
var f1 = new fn()
var f2 = new fn()
f2.__proto__ == f1.__proto__ // true
f1.__proto__ == fn.prototype //true
fn.prototype.constructor == f2.__proto__.constructor // true

1.每一个函数都有一个自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值
2.父函数.prototype.constructor = 父函数本身
3.派生函数(子函数).proto.constructor = 父函数.prototype

原型(prototype)和原型链(proto)
构造函数用prototype开辟共享空间
继承的对象,用__proto__访问。
Object.create()
Object.getPrototypeOf(obj)
Object.setPrototypeOf(obj, proto)
接收两个参数:第一个是现有对象,第二是原型对象。

prototype:此属性只有构造函数才有,它指向的是当前构造函数的原型对象。
proto:此属性是任何对象在创建时都会有的一个属性,它指向了产生当前对象的构造函数的原型对象,由于并非标准规定属性,不要随便去更改这个属性的值,以免破坏原型链,但是可以借助这个属性来学习,所谓的原型链就是由__proto__连接而成的链。
constructor:此属性只有原型对象才有,它默认指回prototype属性所在的构造函数。

构造函数的特点:
 a:构造函数的首字母必须大写,用来区分于普通函数
 b:内部使用的this对象,来指向即将要生成的实例对象
 c:使用New来生成实例对象

1:当方法定义在构造函数内部的this上的时候,要想调用该方法,需要使用实例化后的对象来调用,而不能直接使用构造函数来调用该方法;

         2:当方法定义在类上的时候,则该方法只能被构造函数来调用,而不能被构造函数实例化后的对象来调用;

        3:当方法定义在原型(prototype)上时,也是只能通过构造函数实例化后的对象来调用,如果使用构造方法来调用,则会提示XXXX   is   not   a    function;

        4:对于2和1以及3的区别,现在应该是清楚了,但是对于1和3的区别呢?1和3定义的方法,都是不能通过构造函数来调用,而是要实例化后的对象来调用,那么他们之间有什么不一样呢?通过1的方法定义的方法,在实例化多个对象的时候,同样的会把方法也会复制一遍(即每个实例化后的对象,都有一个方法),这样的话,当需要该构造函数实例化很多对象的时候,每一个实例化后的对象的方法都要占用一定的内存,这样就会导致内存开销太大了;而通过prototype来定义的方法,在实例化对象的时候,都只是在每个对象中复制了一个指向该方法的一个指针,所以实际上,占用的内存只有一个方法,所以对比两者的话,使用prototype来定义方法的话,可以节省很多的内存开销;那么是不是有了后者就不要前者了呢?并不是这样的,在构造函数里面,定义的变量有私有变量(即通过var 定义的变量)和公有变量(即通过this来定义的变量),因为私有变量不能被外界访问,所以我们需要有可以在外界访问私有变量的途径,而在构造函数里面通过this定义的方法可以有效的访问私有变量;

function Person(){
var name=‘varname’ // 私有属性,只能通过this.方法获取
this.name=‘this.name’ // 公有属性,可以被外部调用,优先级别比prototype.name高
Person.prototype.name=‘prototype.name’// 原型对象属性,实例化对象时,prototype里面的属性和方法可以减少内存,指针指向唯一prototype
this.getname = function(){
console.log(name,this.name,Person.prototype.name)
}
}

this和constructor的区别:

function Person(){
    var name='varname'
    this.name='this.name'
    Person.prototype.name='prototype.name'
    this.getname = function(){
        console.log(1,this)
        console.log(2,Person.prototype.constructor)
        console.log(3,this==Person.prototype)
    }
}
var foo = new Person()
foo.getname()
// 下面是打印出来的
1 Person {name: "this.name", getname: ƒ}
2 ƒ Person(){
    var name='varname'
    this.name='this.name'
    Person.prototype.name='prototype.name'
    this.getname = function(){
        console.log(1,this)
        console.log(2,Person.prototype…
3 false

原型的继承的方式
function Person() {}
function Man() {}
man = new Man();

1.Man.prototype.proto = Person.prototype
man instanceof Man
true
man instanceof Person
true

2.原型链继承
注意:
1.默认的原型
所有的引用类型都默认继承Object,这个继承也是通过原型链实现的

2.原型链的问题
a.对象实例共享所有继承的属性和方法
b.创建子类型的实例的时候,不能传递参数

a.prototype = new b()
a.prototype.constructor = a // 上一步constructor指向了b,所以修改回来
Man.prototype = new Person();
man instanceof Man
true
man instanceof Person
true

3.(完美继承, 推荐)
function Person() {}
function Man() {}

function Person(a) {
if (a !== false) {return;}
}
function Man() {}
Man.prototype = new Person() // 此处 实例化直接 return 不损耗效率
man = new Man();
man instanceof Man
true
man instanceof Person
true

3.bind(this,1,2),apply(this,[1,2]),call(this,1,2)
//这种方式只能继承实例上的属性,原型上的不能继承

function Person() {
    this.name ='dhk'
    this.fun = function(){
        console.log(this.name)
    }
    Person.prototype.fun2=function(){
        console.log('pro')
    }
}
function Man() {
    Person.call(this)
}
man = new Man();

或者

function F(name,age){
  this.name = name 
  this.age = age
}
function G(name,age,a) {
  F.call(this,...arguments)
  this.a = a
}
var g = new G('a',12,1) // G {name: "a", age: 12, a: 1}

4.组合继承(伪经典继承 ,推荐)

function SuperType (name) {
  this.name = name
  this.colors = ["red", "blue", "green"]
}
SuperType.prototype.sayName = function () {
  alert(this.name)
}
function SubType (name, age) {
  // 继承属性
  SuperType.call(this, name) // 第二次调用SuperType()
  this.age = age
}
//继承方法
SubType.prototype = new SuperType() // 第一次调用 SuperType()
Subtype.prototype.sayAge = function () {
  alert(this.age)
}
实现思路:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承
注意:
组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,成为js中最常用的继承方式。

参考资料:
https://www.cnblogs.com/8609lql/archive/2019/03/21/10572518.html

console.log("——————————————————————第一种继承:原型链继承——————————————————————")
//父类型
function Person(name){
   this.name=name
   this.action=["吃","喝","拉","撒"]
}

Person.prototype.name="婴儿"

Person.prototype.move=function(){
	console.log(this.name+"在走路")
}

//对象原型(__proto__)和构造函数原型对象(prototype)里面都有一个属性constructor,constructor我们称为构造函数,因为它指向的是构造函数本身
console.log(new Person().constructor===Person)//true
console.log(new Person().constructor===Person.prototype.constructor)//true

//子类型
function Worker(name){
  this.money=1000
}

//原型链继承
Worker.prototype=new Person()
console.log(Worker.prototype.constructor)//这里会指向Person构造函数,所以我们得修正一下
Worker.prototype.constructor=Worker


//实例化
var person=new Person("爸爸")
var w1=new Worker("儿子") 


person.move()//爸爸在走路
w1.move()//undefined在走路,,如果添加this.name=name那有啥继承意义

//实例既是父类的实例,也是子类的实例
console.log(w1 instanceof Person)//true
console.log(w1 instanceof Worker)//true

//【缺点】******:
//①可以方便继承父类型的原型中的方法,但是单纯属性的继承无意义,我想要在这个方法的基础上传参那怎么办?
//②给子类原型添加或者重写方法的代码一定要放在替换原型的语句Worker.prototype=new Person()之后。
//③通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样做就会重写原型链
//④难以实现多继承,会共享属性比如:
w2=new Worker()
console.log(w1.action)//w1只会人类都懂的吃喝拉撒行为

//w2很特别,学到了新技能————飞
w2.action.push("飞")
console.log(w2.action)//["吃", "喝", "拉", "撒", "飞"]
console.log(w1.action)//["吃", "喝", "拉", "撒", "飞"]

//等等为什么w1也会飞了?!
//
//——————————————————————第二种继承:借用构造函数——————————————————————
console.log("——————————————————————第二种继承:借用构造函数——————————————————————")
//使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

function Actor(name){
   Person.call(this,name)
   this.money=10000
}

let a1=new Actor("成龙")
console.log(a1.name)//成龙     //传参成功




   

//【缺点】******:
//①实例并不是父类的实例,只是子类的实例
console.log(a1 instanceof Person)//false  
console.log(a1 instanceof Actor)//true

//②无法使用原型protoyype定义的属性,只能使用构造函数内定义的
//a1.move()        //Error 

//③无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
//引用

console.log("——————————————————————第三种继承:组合继承——————————————————————")
//通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Student(name,score){
  Person.call(this,name)//第一步
  this.score=score
}

//第二步
Student.prototype=new Person()
Student.prototype.constructor=Student

Student.prototype.showScore=function(){console.log(this.name+"考试得分:"+this.score)}

var s1=new Student("罗杰",100)
s1.move()//罗杰在走路
s1.showScore()//罗杰考试得分:100

var s2=new Student("杰罗",99)
s2.action.push("打游戏")

console.log(s2.action)//["吃", "喝", "拉", "撒", "打游戏"]
console.log(s1.action)//["吃", "喝", "拉", "撒"]


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端段

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值