学习前端第三十五天(原型继承,F.prototype,原生的原型)

发布于:2024-05-17 ⋅ 阅读:(71) ⋅ 点赞:(0)

一、原型继承

1、[ [ Prototype ] ],对象属性

 所有对象都有一个[ [ Prototype ] ]

当从object中读取一个缺失的属性时,JavaScript 会自动从原型中获取该属性,“原型继承”。

其中之一设置原型的方法,使用特殊的名字 __proto__

        const one = {
            name: "jack",
            sayHi() {
                console.log("Hi")
            }
        }
        const two = {
            // name: 'xc'
        };

        two.__proto__ = one;  // 原型继承,two继承one的属性

        console.log(two.name);  // one的name
        two.sayHi();

 one中的属性和方法在two中可读取和使用

2、原型链

        const one = {
            name: "jack",
            sayHi() {
                console.log("Hi")
            }
        }
        const two = {
            // name: 'xc'
            __proto__: one, // 原型继承,two继承one的属性
        };

        // 原型链,自下往上寻找内容
        // 原型继承不能形成闭环;__proto__:只能继承对象和null
        const three = {
            __proto__: two,
        }
        const four = {
            __proto__: three,
        }

        console.log(four.name); // jack
        four.sayHi(); // Hi

four中没有,会再从three中招,然后是two,one,找到为止

但是:1.继承不能产生闭环;2.继承的只能是对象和null

3、写入不使用原型 ,修改属性不会对原型产生任何影响

原型仅用于读取属性。对于写入/删除操作可以直接在对象上进行。

        const one = {
            name: "jack",
        };
        const two = {
            __proto__: one,
        };
        const three = {
            __proto__: two,
        };

        three.name = "xxx";
        // one中的“name”没受到影响
        console.log(three);  // {name: 'xxx'}
        console.log(one);  // {name: 'jack', sayHi: ƒ}

访问器(get / set)是例外 ,设置与访问器有关的属性时,会调用原型中的访问器

        const one = {
            firstName: "xu",
            lastName: "chuang",
            get fullName() {
                return `${this.firstName} ${this.lastName}`;
            },
            set fullName(value) {
                [this.firstName, this.lastName] = value.split(" ");
            },
        };
        const two = {
            firstName: "zhang",
            lastName: "li",
            __proto__: one,
        };

        console.log(two.fullName); // zhang li
        // 第一次查找:在two中未找到fullName,在one中找到fullName方法;
        // 第二次查找:在two中找到firstName和lastName,执行该方法
        two.fullName = "xu li"; // 原型one中的set被调用了
        console.log(two);  // {firstName: 'xu', lastName: 'li'}

4、原型继承时this的值 

无论在哪里找到方法:在一个对象还是在原型中。在一个方法调用中,this 始终是点符号 . 前面的对象。

        const obj1 = {
            name: "xc",
            say() {
                console.log(this.name)
            }
        }
        const obj2 = {
            name: "xl",
            __proto__: obj1,
        }
        const obj3 = {
            name: "xyp",
            __proto__: obj2,
        }
        // 无论在哪里找到方法:在一个对象还是在原型中。在一个方法调用中,this 始终是点符号 . 前面的对象。
        obj1.say(); // xc
        obj2.say(); // xl
        obj3.say(); // xyp

5、for...in 循环会迭代继承的属性

想排除继承的属性:

obj.hasOwnProperty(key):如果 obj 具有自己的(非继承的)名为 key 的属性,则返回 true

        const obj1 = {
            name: 'xc',
            age: 20,
            gender: "male"
        };
        const obj2 = {
            name: "xl",
            __proto__: obj1,
        };
        for (let i in obj2) {
            if (obj2.hasOwnProperty(i)) console.log(obj2[i]);
            // 没有判断:xl 20 male ; 有if判断:xl
        }

二、F.prototype

1、构造函数创造对象时设置原型

new F() 构造函数创建一个新对象。如果 F.prototype 是一个对象,那么 new 操作符会使用它为新对象设置 [[Prototype]]

F.prototype 指的是 F 的一个名为 "prototype" 的常规属性。这听起来与“原型”这个术语很类似,但这里实际上指的是具有该名字的常规属性。可以任意设置F的“prototype”。

        // 构造函数(实例)对象指向构造函数的原型
        function Fn() {
            // this={}
            // ! this.__proto__=Person.prototype
            // return this
        };
        const fn = new Fn()
        console.dir(Fn);
        console.log(fn);

        console.dir(Fn.prototype == fn.__proto__)

2、默认的F.prototype

每个函数都有 "prototype" 属性。默认的 "prototype" 是一个只有属性 constructor 的对象,属性 constructor 指向函数自身。constructor 属性可以通过 [[Prototype]] 给所有 构造函数对象 使用。

        function Fn() {
            // this={}
            // ! this.__proto__=Person.prototype
            // return this
        };
        const fn = new Fn()
        console.dir(Fn);
        console.log(fn);

        console.log(Fn === Fn.prototype.constructor); // true
        console.log(Fn === fn.constructor); // true
        console.log(Fn.prototype.constructor === fn.constructor); // true

三、原生的原型

1、Object.prototype

 所有对象的原型链的终点:Object;

Object的原型是null。

        // 原生的原型
        /* function Object() {
             this = {}
             this.__proto__ = Object.prototype  
             return this
         } */
        //  const obj1= new Object()

        const obj1 = {};
        console.log(obj1.__proto__ === Object.prototype);
        console.log(Object.prototype.__proto__ ); // null 原生

2、其他内建原型

其他内建对象,像 ArrayDateFunction 及其他,在prototype中都自带了各种方法。

当我们创建一个数组 [1, 2, 3],在内部会默认使用 new Array() 构造器。 Array.prototype 变成了这个数组的 prototype,并为这个数组提供数组的操作方法。

 3、更改原生原型(不推荐使用)

        // 原生原型的方法所有都能用

        Array.prototype.say = function () {

            console.log("hello");

        }

        const arr = [];

        arr.say();

四、原型方法,没有__proto__的对象

1、原型方法

Object.create(proto,{??}) 创造一个对象,以proto为原型,以及可选的属性描述来创建一个空对象;

Object.getPrototypeof(obj)返回对象obj的[ [ Prototype ] ];

Object.setPrototypeof(obj,proto)设置对象obj的原型为proto;

Object.create 方法更强大,因为它有一个可选的第二参数:属性描述器。

我们可以在此处为新对象提供额外的属性,就像这样:

        const Person = { name: "jack" };
        const obj = Object.create(Person, {
            age: {
                value: 20
            },
            gender: {
                value: "boy"
            }
        });
        console.log(obj);

实现对对象Person的完全复制: 

      let clone = Object.create(
            Object.getPrototypeOf(Person),
            Object.getOwnPropertyDescriptors(Person),
        )

2、没有__proto__的对象

        // 极简对象“Very plain” object,原型为null

        const obj = { a: 1, b: 2 }

        Object.setPrototypeOf(obj, null);

        console.log(obj); // {a: 1, b: 2} , 没有原型