原型和继承

77 阅读6分钟

原型

概述

原型是一个公共的对象空间,每个对象都会存在对应的原型(私有的实例都能访问到这个原型的内容)

构造函数的缺陷

// 构造函数的缺陷
function Per(){
    this.name='tom'
    this.sayHello=()=>{
        console.log('你好')
        }
    }
let person = new Per()
let person2 = new Per()
console.log(person == person2)//false
console.log(person.name==person2.name)//true
console.log(person.sayHello == person2.sayHello)//false
  • 因为构造函数中的this 指向对应构造的实例对象,又因为两个实例对象不是一个对象,所以你给对应的实例对象添加的方法是加给不同的对象,又因为函数本身也是一个函数,那么加给对应的每一个实例对象的函数对象是不是一个
  • 综上所得,每一个实例对象再构造的时候指定的函数都是一个新的函数对象,那么也就是我构建了多少个实例对象就需要开辟多少个函数空间,而这些函数的代码又完全一致,这就造成了内存浪费。为了解决这个问题就产生了原型

prototype

概述

prototype是属于函数对象的一个属性,它本身就是一个对象,也就意味着里面可以存储对应的内容,构造函数也是一个函数,所以它也拥有prototype这个属性,这个属性被称为显示原型(函数的原型)。

function sayHello(){}
console.log(sayHello.prototype)//返回的是一个对象 {constructor:f} constructor(构造器)

从上可以看出对应的函数用于prototype这个属性,这个属性是一个对象空间,那么就意味着里面可以存储对应的属性和方法,里面有一个属性为constructor(构造器)

function person(){
    this.name='jack'
}
console.log(person.prototype)//{constructor: ƒ}  prototype是函数的一个对象空间
person.prototype.username='张三'
person.prototype.sayHello=()=>{
    console.log('hello')
}
let person1 = new person()
let person2 = new person()
console.log(person1)
// 通过实例对象可以访问到对应的prototype中的内容
console.log(person1.username)//直接访问构造函数prototype 中的属性
// person1.sayHello===> 访问person 构造内prototype的sayHello方法
// person2.sayHello===> 访问person 构造内prototype的sayHello方法
console.log(person1.sayHello==person2.sayHello)//true

通过上述可得 将对应的函数放在prototype中这个函数对象只会声明一次,那么对应的就解决了构造函数的缺陷

实例对象可以直接访问原型中的属性和方法
在设计对应的走早函数的时候需要注意的事项
  • 将函数放在原型(prototype)中
  • 将属性放在构造函数中
——proto—— pototype对应对象的原型
概述

——proto——是属于对象的一个公共对象空间,它是所有对象都具备的有个属性,他被称为隐式原型

function Person(){}
let person = new Person()
// 对象空间
console.log(person.*proto*)
//实例对象的\_\_proto\_\_指向对应的构造函数的prototype
console.log(person.*proto*==Person.prototype)//true

通过上面可得 实例对象的——proto——是指向对应的构造函数的prototype 那么对应的构造函数的prototype的__proto_又是什么?

console.log(person.*proto* == Person.prototype)//实例对象的\_proto\_指向对应的构造函数的prototype
// 基于继承关系
class Animal{
constructor(){
this.name='jack'
}
}
Animal.prototype.sayHello=()=>{
console.log('你好')
}
class Dog extends Animal{
constructor(){
super()
this.age=18
}
// class的构造函数外写对应的方法其实就是把他放在原型上
say(){
console.log('汪汪汪')
}
}
let dog = new Dog()
let dog1 = new Dog()
console.log(dog.say==dog1.say)//true
console.log(dog.*proto*)
console.log(dog.*proto*)//基于继承关系那么对应的Dog.prototype指向父类的构造函数
console.log(dog.*proto*.*proto*)//子实例对象的\_\_proto\_\_的\_\_proto\_\_指向父类构造函数的prototype的
console.log(dog.*proto*.*proto*.*proto*)//Object
console.log(dog.*proto*.*proto*.*proto*.*proto*)//null
//  构成了一个链条  这个链条就是原型链的基础

class的构造函数外的方法其实就是把它放在原型上 继承会将当前的构造函数的原型的_proto_指向其父类的原型

简单实现new关键词
// 实现new方法
function myNew(constructFn,...args){
    // 如果没有原型不能被new
    if(typeof constructFn !='function' || !constructFn.prototype){
    throw new Error('is not a contstructor')
    }
    // new的操作
    // 自动构建对象
    let newObj={}
    // 将当前构造完的对象原型指向对应的构造函数的原型
    newObj.*proto*=constructFn.prototype
    // 自动执行方法
    constructFn.apply(newObj,args)//apply只能传入一个,且是数组
    // 自动返回对象
    return newObj
}
console.log(myNew(person,'jack',18))
原型链

原型链就是在对应的_proto_上找属性的过程,所有的链条

Object.prototype.password='123456'
class Animal{
    constructor(){
        this.name='hello'
    }
}
Animal.username='张三'
Animal.phoneNumber='123456789'
Animal.prototype.username='李四'
new Animal().like='吃饭'
class Person extends Animal{
    constructor(){
        super()
        this.age=18
    }
    age=20
}
Person.prototype.sex='男'
let person = new Person()
person.email='<123@234.com>'
console.log(person.age)//18
console.log(person.sex)//男
console.log(person.username)//李四
console.log(person.phoneNumber)//undefined
console.log(person.like)//undefined
console.log(person.name)//hello
console.log(person.password)//123456
person.a=100
console.log(person.a)//100  赋值不是原型链
原型链的依据
  • 原型上的内容的访问(实例对象,属性名访问)
  • 所有对象都具备_proto_(自己的构造函数 ==> 自己的构造函数的原型 ==> 父亲构造函数的原型 ==> Object的原型 ==> null)
原型链查找顺序
  • 找自身的构造函数
  • 自身构造函数的原型
  • 父类构造函数的原型
  • Object构造函数的原型
  • null
原型链的查找
Object.prototype.password='123456'
class Animal{
constructor(){
this.name='hello'
}
}
Animal.username='张三'
Animal.phoneNumber='123456789'
Animal.prototype.username='李四'
new Animal().like='吃饭'
class Person extends Animal{
constructor(){
super()
this.age=18
}
age=20
}
Person.prototype.sex='男'
let person = new Person()
person.email='<123@234.com>'
console.log(person.age)//18
console.log(person.sex)//男
console.log(person.username)//李四
console.log(person.phoneNumber)//undefined
console.log(person.like)//undefined
console.log(person.name)//hello
console.log(person.password)//123456
person.a=100
console.log(person.a)//100  赋值不是原型链

~{HC}A0G{W2UX4SKN5E0A}M.png 对象属性赋值是不属于原型链的

  • 如果存在这个属性就修改
  • 如果不存在这个属性就添加
高阶函数原型重构
.forEach原型重构
// 方法存在于对应的原型上 实例对象调用的方法都是原型方法
Array.prototype.myForEach=function(fn){
    // 判断是否是一个函数
    if(typeof fn!='function'){
    throw new Error('参数类型错我')
}
// this指向当前的实例对象 指向需要遍历的数组
for(var i=0;i\<this.length;i++){
// 忽略empty
    if(i in this){
    // 调用方法
    fn.call(this,this\[i],i,this)
}
}
}

let arr0=\[1,3,,6,8]
let value0=arr0.myForEach((v,i,arr)=>{
console.log(v,i,arr)
})
console.log(value0)

let vau0=arr0.forEach((v,i,arr)=>{
console.log(v,i,arr)
})
console.log(vau0)
map原型重构
// 方法存在于对应的原型上 实例对象调用的方法都是原型方法
Array.prototype.myMap = function(fn){
// 判断是否是一个函数
    if(typeof fn!='function'){
    throw new Error('参数类型错误')
}
// 准备一个返回的数组
let result=\[]
    for(var i=0;i\<this.length;i++){
    // 忽略空
    if(i in this){
    // 调用方法
    result [i]=fn.call(this,this\[i],i,this)
}
}
return result
}
// 测试
let arr1=\[1,4,,7,5]
let value1=arr1.myMap((v,i,arr)=>{
console.log(v,i,arr)
return v+i
})
console.log(value1)

let vau1=arr1.map((v,i,arr)=>{
console.log(v,i,arr)
return v+i
})
console.log(vau1)
some原型重构
Array.prototype.mySome=function(fn){
    if(typeof fn!='function'){
    throw new Error('参数类型错误')
}
    for(var i=0;i\<this.length;i++){
    if(i in this){
        if(fn.call(this,this\[i],i,this)) return true
}

    }
    return false

}
// 测试
let arr2=\[5,3,7,4,,7,1]
let value2=arr2.mySome((v,i,arr)=>{
console.log(v,i,arr)
return v>3
})
console.log(value2)

let vau2=arr2.some((v,i,arr)=>{
console.log(v,i,arr)
return v>3
})
console.log(vau2)
every原型重构
Array.prototype.myEvery=function(fn){
    if(typeof fn!='function'){
    throw new Error('参数类型错误')
}
// 遍历数组
for(var i=0;i\<this.length;i++){
    if(i in this){
        if(!fn.call(this,this\[i],i,this)) return false
}
}
return true
}
// 测试
let arr3=\[4,6,8,2,,1,4]
let value3=arr3.myEvery((v,i,arr)=>{
console.log(v,i,arr)
return v>3
})
console.log(value3)

let vau3=arr3.every((v,i,arr)=>{
console.log(v,i,arr)
return v>3
})
console.log(vau3)
reduce原型重构
Array.prototype.myReduce=function(fn,initValue){
    if(typeof fn != 'function'){
    throw new Error('参数错误')
}
// 设置初始下标为0
let initindex=0
// 判断是否有初始值
    if(arguments.length<2){
    // 获取所有的值
    let arr =Object.values(this)
    // 如果里面没有一个值  那么直接报错
    if(arr.length==0){
    throw new Error('reduce Empty Array is not initValue')
}
// 指定初始值
initValue=arr\[0]
// 指定初始下标
initindex= this.indexOf(arr\[0])+1
}
// 遍历  从初始值开始
for(;initindex\<this.length;initindex++){
// 忽略empty值
if(initindex in this){
// 调用函数  传入参数
initValue = fn.call(this,initValue,this\[initindex],initindex,this)
}
}
return initValue
}
// 测试
let arr4=\[9,4,,6,7,8,1]
let value4=arr4.myReduce((prev,v,i,arr)=>{
console.log(prev,v,i,arr)
return prev+v
})
console.log(value4)

let vau4=arr4.reduce((prev,v,i,arr)=>{
console.log(prev,v,i,arr)
return prev+v
})
console.log(vau4)
.filter原型重构
Array.prototype.myFilter=function(fn){
    if(typeof fn != "function"){
    throw new TypeError("参数类型错误")
}
// 准备返回的数组
var result =\[]
// 循环遍历
    for(var i =0;i\<this.length;i++){
        if(i in this){
            fn(this\[i],i,this)?result.push(this\[i]):""
}
}
return result
}
let arr5=[4,,5,8,7,9]
let value5=arr5.myFilter((v,i,arr)=>{
console.log(v,i,arr)
return v%2
})
console.log(value5)

let vau5=arr5.filter((v,i,arr)=>{
console.log(v,i,arr)
return v%2
})
console.log(vau5)
简单instanceof重构
继承
class Animal{
constructor(){
}
}
class Person extends Animal{
constructor(){
super()
}
}

let person = new Person()
// 对象  instanceof  构造函数
console.log(person instanceof Person)//true
console.log(person instanceof Object)//true
// 原理是判断当前的原理链上是否具备对应的构造函数
function myInstanceof(target,constructor){
while(target.*porto*){
target=target.*porto*
// 如果我的原型上存在这个构造  返回true
if(target.constructor == constructor){
return true
}
}
return false
}

// 递归写法
function myInstanceof(target,constructor){
  if(target,_porto_){
    target=target._porto_
    if(target.constructor==constructor){
      return true
    }
  }
  return false
}