原型链

338 阅读3分钟

函数的prototype属性。

1 每个函数都有prototype属性,它默认指向一个空的Object实例对象。


function Fn(){
    
}

console.log(Fn.prototype instanceof Object) //true
console.log(Object.prototype instanceof Object) //false
console.log(Function.prototype instanceof Object) // true

2 所有函数都是Function的实例(包括Function)

    console.log(Function.__proto__ = Function.prototype) // true

3 Object的原型对象是原型链的尽头

    console.log(Fn.prototype.__proto__ == Object.prototype) // true
    console.log(Object.prototype.__protpo__) // null

4 每个函数的原型对象默认都有一个constructor属性

每个函数的原型对象(Fn.prototype) 默认都有一个constructor属性 指向构造函数本身(Fn)

5 每个实例都有一个隐式原型(proto)指向构造函数的原型对象(Fn.prototype)

6 当实例调用一个属性或者方法的时候,先在本身查找,如果本身没有查找到,就顺着实例的隐式原型__proto__的指向一直往上查找。

7 每个函数的原型对象(Fn.prototype)上面也有隐式原型(proto)

    fn.__proto__ = Fn.prototype //true

8 原型链上也有隐式原型__proto__,这个隐式原型指向 Object.protype

Fn.prototype.prpto == Object.prototype

9 原型对象有一个属性construcoter,它指向函数对象。

(Date.prototype.constructor == Date //true

原型链继承 通过new Father()方式

让sun.prototype 成为Father的实例。

function Father() {
    this.name = 'father'
}
Father.prototype.fatherShow = function () {
    console.log('father show')
}
function Sun() {
    this.name = 'sun'
}
Sun.prototype.showSun = function () {
    console.log('sun show')
}

Sun.prototype = new Father();
//new Father之后Sun.prototype.consturctor指向Father,所以需要把
consturctor 在指到Sun上。
Sun.prototype.consturctor = Sun;

var sun = new Sun();

sun.fatherShow()

我们通过Sun.prototype = new Father(); 让Sun.prototype是 Father的实例。这样就能访问到 Father的原型对象。

首先我们sun.fatherShow()

1 先在sun实例对象上找 fatherShow方法

2 如果没有找到,通过sun.__proto__在sun的prototype原型链上找fatherShow方法

3 如果没有找到,因为sun.prototype上没有,因为Sun.prototype = new Father();在Father.prototype上找这个方法。

原型链继承 直接继承

Sun.prototype = Father.prototype;我们相当于跳过了Father这个构造函数,直接让Sun.prototype跟Father.protype建立联系。

缺点:

  • 但是这样sun上以前挂载showSun()就调用不到了。
  • 任何对sun.prototype的修改都会反映到father.prototype上 。 优点:
  • 不用和Father()建立联系,节省了内存。
function Father() {
    this.name = 'father'
}
Father.prototype.fatherShow = function () {
    console.log('father show')
}
function Sun() {
    this.name = 'sun'
}
Sun.prototype.showSun = function () {
    console.log('sun show')
}

Sun.prototype = Father.prototype;

Sun.prototype.play =function () {
    console.log('sun play')
}

var sun = new Sun();

sun.fatherShow()

console.log(Sun.prototype)
console.log(Father.prototype.constructor)


原型链继承 通过new Father()

Sun.prototype = new Father(); 让Sun的原型对象是 Father的实例对象,这样Sun.prototype就可以顺着__proto__找到Father.prototype.

    function Father() {
    this.name = 'father'
    }
    Father.prototype.fatherShow = function () {
        console.log('father show')
    }
    function Sun() {
        this.name = 'sun'
    }
    Sun.prototype.showSun = function () {
        console.log('sun show')
    }
    
    Sun.prototype = new Father();
    
    Sun.prototype.play =function () {
        console.log('sun play')
    }
    var sun = new Sun();
    
    sun.fatherShow()
    
    //console.log(Sun.prototype)
    console.log(Father.prototype)

通过call()或者apply() 方法实现继承。

这种通过call或者apply的继承,只能访问到Father上的属性,要是访问Father原型链上的方法是做不到的。

    function Father(name) {
    this.name = name
    }
    
    Father.prototype.fatherShow = function () {
        console.log('father show')
    }
    
    
    function Sun() {
        Father.call(this,'kira')
        Father.apply(this,['kira'])
    }
    var sun = new Sun();
    
    console.log(sun.name)

Class的本质

  • 1 class本质是function
  • 2 类的所有方法都定义在类的prototype属性上
  • 3 类创建的实例里也有__proto__ 指向类的prototype原型对象
  • 4 ES6的类其实就是语法糖。

使用 class 和 extends

class Father {
    constructor(name,age) {
        this.name = name
        this.age = age
    }

    info(){
        return `名字${this.name},年龄${this.age}`
    }
    say(){
        return 'fatherSay'
    }
}

class Sun extends  Father{
    constructor() { 
    // 通过super()关键字可以调用父类的constructor构造函数,给name 和 age赋值。
    
    super调用父类,可以调用父类的普通函数,也可以调用父类的构造函数。
    
        super('kira',25);
    }
    
     say() {
        return super.say();
    }
}

const sun =  new Sun();

console.log(sun.info())


static 静态方法和属性

定义在类上,而不是定义在类的原型上,类可以调用,类的实例不能调用的方法就叫做静态方法

    var Animal = function(){};
    //该name为静态属性,只能通过Animal来访问,不能通过实例访问。
    Animal.name = "dog";
    var a = new Animal();
    
    console.log(a.name) 
    // 打印 underfind
    console.log(Animal.name) 
    // 打印 dog 

ES2015规定使用static关键字即可定义一个静态方法

  • 使用实例对象调用方法,方法中的this指向此实例对象。
  • 如果使用类调用静态方法,那么静态方法中的this指向类本身。
class Antzone{
  static show(){
    console.log("蚂蚁部落");
  }
}
let ant=new Antzone();
Antzone.show(); // 打印  蚂蚁部落
ant.show();// typeError is not a function 

ES2015新增extends关键字,可以实现类之间的继承。

不但可以继承父类的实例属性与实例方法,静态属性与静态方法也会被继承。

class Father{
    static func(){
        console.log("蚂蚁部落");
    }
}
Father.address="青岛市南区";
class Sun extends Father{

}
Sun.func(); 
console.log(Sun.address);
//打印结果
蚂蚁部落
青岛市南区
var sun = new Sun();