继承的补充

	一个月前写的原型与原型链。那时还只是刚明白了__proto__与prototype之间的关系。
	因此那时对于继承的认知是不够的。所以对于继承来说,我还需要继续总结。(在过了一个月后。。。)

es6之前的继承

  • 首先是prototype 和 __proto__ . 我发现即使我记住了原型链图。 但是我发现当使用Object.getPrototypeOf()、Object.setPrototypeOf(). 这些方法时,我还是捋不清其中的联系。因此还是要具体了解这两个东西是干什么用的。

  • proto 。每个JS对象一定对应一个原型对象 (每个对象上都有一个__proto__属性)。 对象的__proto__的值就是它所对应的原型对象。 (称为该对象的原型)
    • 如下述。我们创建了一个obj对象,其__proto__的值是它对应的原型(Object.prototype) . 因为图中为true。
    • 引用:引用也就是而可以通过__proto__属性访问到对象的prototype
    • 其实ECMAScript 规范prototype应是个隐式引用(只能间接访问)
      • Object.getPrototypeOf(obj) 间接访问指定对象的 prototype 对象
      • Object.setPrototypeOf(obj, anotherObj) 间接设置指定对象的 prototype 对象
    • 但是部分浏览器。 却可以通过__proto__ 去直接操作prototype. 所以才有了__proto__这个概念 。(因此不推荐我们却使用它去进行操作。而是用于理解原型链的模型。)

		const obj = {
            name: 'fjh'
        }
        console.log(obj)
        console.log(obj.prototype) //undefined . 因为obj并不是个函数对象。所以其也没有能与上述职能的prototype对象的关联。
        console.log(obj.__proto__ === Object.prototype)

在这里插入图片描述

  • prototype
    • 定义为:给其他对象提供共享属性的对象。
    • 其本身不过是个对象。只是承担了提供属性功能的职责。它就成了该对象的prototype. 对象间形成了某种关联
    • 因此并不是每个对象都有prototype属性。只有函数对象才有该属性。且该对象内部有个constructor属性又指向了它的构造函数(对应的函数对象)。
		function F(name) {
            this.name = name
        }
        console.log(F.prototype.constructor === F)
        console.log(Object.prototype.constructor === Object)
        console.log(Number.prototype.constructor === Number)
		/*
			true
			true
			true
		*/
  • 原型链
    • 基于上述的总结。一个对象的prototype对象也应该有__proto__.其值也对应于一个prototype对象。因此有原型链的产生。
    • 因为JS中”万物都是对象“。所以该原型链的重点就是Object.prototype . 而 Object.prototype.proto 为 null
    • 因此当我们去访问某个属性时,它会按原型链去寻找
      • 自身的属性去找。
      • 它所对应的原型对象上找
      • 直到找到Object的原型对象。没有就为null。
		function F(name) {
            this.name = name
        }
        console.log(F.prototype)
        console.log(F.__proto__ === Function.prototype)
        console.log(Function.prototype.__proto__ === Object.prototype)
        console.log(Object.prototype.__proto__)

在这里插入图片描述

  • 原型继承:设置某个对象为另一个对象的原型。
    • 显示原型继承: 我们需要手动实现过程。
      1. Object.setPrototype( target , source ) 。将source作为target的原型对象
      2. Object.create( obj ) 。创建一个新的对象,并把它作为创建的新对象的原型
    • 通过obj.__proto__属性去访问 . 实际上是通过Object.defineProperty(): 定义一个__proto__属性(加了get/set方法) . 这样就避免了不规范的操作.
		let obj1 = {
            a: 1
        }
        let obj2 = {
            b: 2
        }
       	// 设置obj2的原型对象为obj1
        Object.setPrototypeOf(obj2, obj1)
        console.dir(obj2)
        console.log(obj1.prototype) //undefined
		
		//create函数
        Object.setPrototypeOf(obj2, obj1)
        let obj3 = Object.create(obj2)
        obj3.c = 3
        console.dir(obj3)
	

在这里插入图片描述
在这里插入图片描述

		let obj1 = {
            a: 1
        }
        let obj2 = {
            b: 2
        }
        Object.setPrototypeOf(obj2, obj1)
        Object.defineProperty(obj2, '__proto__', {
            get() {
                console.log('get方法')
                return Object.getPrototypeOf(this)
            },
            set(value) {
                Object.setPrototypeOf(this, value)
            }
        })
        console.log(obj2.__proto__)

在这里插入图片描述

  • 隐式原型继承 : ( constructor函数 )

    1. 创建空对象
    2. 设置空对象的原型为另一个对象 或者 null
    3. 为该对象 ,添加属性或方法.
    • 没有隐式原型继承 . 我们需要手动去是实现这三步.
    • 因此使用constructor函数 (构造函数) . 来做属性初始化.
      • constructor函数上 , 有prototype属性 .
      • 通过new 关键词 , 去创建新对象 .
		//F是构造函数. 默认做了F.prototype = Object.create(Object.prototype).
		
		function F(name, age) {
            this.name = name
            this.age = age
        }
        let f = new F('fjh', 20)
        console.log(f)
        console.log(f.__proto__ === F.prototype)
        console.log(F.prototype.__proto__ === Object.prototype)

在这里插入图片描述


  • 上述基本了解了__proto__ 和prototype 这两个属性. 因此接下来 就是具体的继承实现了.
  1. 原型链的继承
  • 问题:
    • 原型中包含引用值的时候, 其引用值会被所有的实例所共享 . 因此属性通常在构造函数上定义(实例属性)而不是在原型上定义(原型属性) .
    • 子类在实例化时 , 不能给父类型的构造函数传参.
		function F(name) {
            this.name = name
            this.arr = [1, 2, 3]
        }

        function S() {

        }
        S.prototype = new F('fjh')
        let s = new S()
        let s1 = new S()
        s.arr[1] = 'change'
        console.log(s1.arr) 
        // [1,' change ' , 3 ]  . 发现修改了s , s1 也跟者变了.
        // 并且初始化子类实例时 , 并没有参数. 参数是父类构造函数传入的.  
  1. 盗用构造函数 (经典继承)
  • 问题:
    • 解决了原型链继承无法向父类传参的问题 . 但无法继承父类原型上的方法 .
    • 且所有操作都必须通过子类 , 用call方法指定父类构造函数的this指向
		function F(name) {
            this.name = name
            this.arr = [1, 2, 3]
        }

        function S(name) {
            F.call(this, name)
        }
        let s = new S('fjh')
        let s1 = new S('fjh1')
        s.arr[1] = '改'
        console.log(s, s1)
        console.log(s.arr, s1.arr)
        
        //因为函数的上下文对象是不断建立的. 
        //所以每新建一个子类的实例 . 它们继承的引用类型都是独立的 ! 

在这里插入图片描述

  • 组合继承(融合了上述两种方法)
    • 问题:
      • 解决了属性的传递问题 . 以及方法的复用问题 .
      • 但是它会改变子类实例的原型对象的constructor的指向.
		function F(name) {
            this.name = name
            this.arr = [1, 2, 3]
        }
        F.prototype.doThing = () => {
            console.log('something')
        }

        function S(name) {
            F.call(this, name)
        }
        S.prototype = new F()
       	S.prototype.constructor === S 
       	
       	// Object.setPrototypeOf(S.prototype, F.prototype)
       	//这样写	.	可以达到上面两句的效果 . 
       	
        let s = new S('fjh')
        console.log(s)
        console.log(S.prototype.constructor === S)
		
		//根据上面所学, 函数对象的prototype都默认有个constructor属性 . 其指向它的构造函数 .
		//也就是与它有关联的那个函数对象. 但此时constructor的指向其实是父类函数对象.
		// 因此我们需要手动的去改变. 

在这里插入图片描述

  • 原型式继承
    • 问题 :
      • 适合不需要创建构造函数 . 但仍需要在对象间共享信息的场合 .
      • 属性中的引用值始终会在相关对象间共享.
		function create(obj) {
            function f() {}
            f.prototype = obj
            return new f()
        }
        let person = {
            name: 'fjh',
            arr: [1, 2, 3]
        }
        let p1 = create(person)
        p1.name = 'p1'
        p1.arr.push(4)
        let p2 = create(person)
        p2.name = 'p2'
        console.log(p1, p2)
		
		//这里可以不用新建create函数. 而是使用Object.create() . 也可以实现同样的效果.

在这里插入图片描述

  • 寄生式继承
    • 问题:
      • 给对象添加函数导致函数难以重用 . 与构造函数模式类似
      • 其背后的思路: 类似于寄生构造函数和工厂模式
		function create(o) {
            function F() {}
            F.prototype = o
            return new F()
        }

        function createAnother(original) {
            let clone = create(original)  //创建对象
            clone.doThing = function() {	//增强对象
                console.log('clone')	
            }
            return clone
        }
        let person = {
            name: "fjh",
            arr: [1, 2, 3]
        }
        let p1 = createAnother(person)
        console.log(p1)
        p1.doThing()

在这里插入图片描述

  • 寄生式组合继承
    • 可以算是引用类型继承的最佳模式
    • inheritPrototype这个函数是核心 .
		function create(o) {
            function F() {}
            F.prototype = o
            return new F()
        }

        function inheritPrototype(subType, superType) {
            let prototype = Object(superType.prototype) //  创建对象
            prototype.constructor = subType // 增强对象
            subType.prototype = prototype // 赋值对象
        }

        function SuperType(name) {
            this.name = name
            this.colors = ['red', 'blue', 'green']
        }
        SuperType.prototype.sayName = function() {
            console.log(this.name)
        }

        function SubType(name, age) {
            SuperType.call(this, name)
            this.age = age
        }
        inheritPrototype(SubType, SuperType)

        SubType.prototype.sayAge = function() {
            console.log(this.age)
        }

学习自深入理解JS原型
以及[ 红宝书 ]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值