JS核心知识点梳理原型继承下

42次阅读

共计 2570 个字符,预计需要花费 7 分钟才能阅读完成。

引言

正如上篇所提到的,有些人认为 JavaScript 并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象, 然后您可以简单地定义哪些类继承哪些类,JavaScript 使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承(通常被称为 原型式继承 —— prototypal inheritance)。

继承的方式

说到继承,首先得明白继承的是什么东西。为了方便理解,我个人把属性(包括方法)分为共有属性和私有属性。私有属性比如名字或者身份证之类的,是每个人独有的。共有属性是能共用的属性或者方法,比如爱好属性,吃饭方法。

私有属性的继承(构造函数 +call)

这个比较简单,私有属性用利用构造函数和 call 或者 apply

const Person = function (name) {this.name = name}
const Students = function (name) {Person.call(this,name)
    }
const xm = new Students('小明')
console.log(xm)  //Students {name: "小明"}

公有属性的继承

这里里面坑有点多,大家听我娓娓道来。
通过原型链实现公有属性继承肯定没错,但是我们设计的时候有个原则 子类需要有自己的原型,父类也必须要有自己的原型,子实例在自己的原型上找不到属性的时候才会到父原型上去找
子类.prototype = 父类.prototype 这样肯定不行,虽然能继承父类原型的方法,但是子类的原型和父类的原型是同一个,给子类原型添加方法的时候,相当于给父类的原型也添加了一个方法。so 我们应该有一个缓冲

子类实例 —-> 子类原型 ——-> 中间对象 ——-> 父类原型 // 沿着箭头能访问,表现上符合我们的设计原则

最常见的是使用父类的实例当这个中间对象。

Children.prototype = new Parent()    

但是了这么做有个不好的地方。会实例化一次父类。如果父类特别复杂,比如 axios,那么会带来很多额外的开销。

我们看一下中间对象有什么作用,实际上只起了一个隔离和原型重定向的作用。完全可以用一个空对象实现这个功能

// 实现缓冲
var fn = function() {}
fn.prototype = Parent.prototype
Children.prototype = new fn()

实际上,这个就是 Oject.create()的实现

//Oject.create()
Object.create = function(obj){var fn = funcion(){}
    fn.prototype = obj
    reurturn new fn()}

终极继承解决方案

现在既要继承私有属性,又要继承公有属性。

    const Person = function (name) {this.name = name}
    Person.prototype.eat = function () {console.log('i am hungry,i want to eat!')
    }
    const Student = function (name) {Person.call(this,name)
    }
    Student.prototype = Object.create(Person.prototype)   // 注意这里!原型重定向的后遗症
    const xm = new Student ('小明')
    xm.name //'小明' 
    xm.eat() //i am hungry,i want to eat!
    console.log(xm)
    //Student {name: "小明"}
    //  name: "小明"
    //  __proto__: Person
    //      __proto__: // 这一层是个空对象,只有一个__proto__属性指向 Person 的原型
    //          eat: ƒ ()
    //            constructor: ƒ (name)
    //            __proto__: Object

但是这里 xm.constructor.name // Person
这里因为原型重定向后没有重置 construtor,xm 本身没有 construtor 只能找我们创建的空对象,空对象没有 construtor 所以继续往上找,找到了 Person.prototype 上的 construtor 为 Person
所以解决思路很简单,在改写原型链的时候重置一下 constructor 就行了

...
var temObj =  Object.create(Person.prototype)
temObj.constructor = Student
Student.prototype =temObj
...
 xm.constructor.name // Student  ok 搞定  

new 干了啥

既然 new 在“类”的创建里面必须使用,那么我们就说一下 new 到底干了啥事情

1. 创建一个对象 o 继承构造函数
2. 让构造函数的 this 变为 o, 并执行构造函数,将返回值设置为 k
3. 如果 k

// 仿写 new
function new1(func) {var o = Object.create(func.prototype)
        var k = func.apply(o,arguments[1])
        return typeof k === 'object'? k: o
    }
const x = new1(Student,['张三'])
x.name //'张三'
x.eat //'i am hungry,i want to eat!'

我们回过头再分析一下构造函数模式继承

const Person = function (name) {this.name = name}
const Students = function (name) {Person.call(this,name) //this 是 student 实例
    }
const xm = new Students('小明')  // 分析这里干了什么
console.log(xm)  //Students {name: "小明"}

1. 让空对象 o 继承 Students(o 能访问 Students 的原型)
2.student 执行,执行 Person 的代码,this 是 o, 并且传入 name, o.name='小明' 返回的 k 是 undefined
3. 返回 o,也就是返回{name:'小明'}

今天先写到这,未完待续 ….

es6 继承

in

mvvm 中 definePrototype

对象的三种角色

静态属性、方法。私有属性、方法

综合训练

总结

正文完
 0