原型和原型链,以及圣杯模式

原型和原型链

  • 所有对象都是通过new 函数创建
  • 所有的函数也是对象
    • 函数中可以有属性
  • 所有对象都是引用类型

原型 prototype

所有函数都有一个属性:prototype,称之为函数原型

默认情况下,prototype是一个普通的Object对象

默认情况下,prototype中有一个属性,constructor,它也是一个对 象,它指向构造函数本身。

隐式原型 proto

所有的对象都有一个属性:__proto__,称之为隐式原型

默认情况下,隐式原型指向创建该对象的函数的原型。

当访问一个对象的成员时:

  1. 看该对象自身是否拥有该成员,如果有直接使用
  2. 在原型链中依次查找是否拥有该成员,如果有直接使用

猴子补丁:在函数原型中加入成员,以增强起对象的功能,猴子补丁会导致原型污染,使用需谨慎。

原型链

特殊点:

  1. Function的__proto__指向自身的prototype
  2. Object的prototype的__proto__指向null

链条全貌

在这里插入图片描述

原型链的应用

基础方法

W3C不推荐直接使用系统成员__proto__

Object.getPrototypeOf(对象)

获取对象的隐式原型

Object.prototype.isPrototypeOf(对象)

判断当前对象(this)是否在指定对象的原型链上

对象 instanceof 函数

判断函数的原型是否在对象的原型链上

Object.create(对象)

创建一个新对象,其隐式原型指向指定的对象

Object.prototype.hasOwnProperty(属性名)
Object.hasOwnProperty(属性名)

判断一个对象自身是否拥有某个属性

应用

类数组转换为真数组

Array.prototype.slice.call(类数组);

实现继承

默认情况下,所有构造函数的父类都是Object

圣杯模式

定义俩个俩个函数VIPUser,User。要使VIPUser能使用User的所有属性,实现如下图

在这里插入图片描述

function User(firstName, lastName, age) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.age = age;
            this.fullName = this.firstName + "" + this.lastName;
        }
        User.prototype.sayHello = function () {
            console.log(`大家好,我叫${this.fullName},今年${this.age}岁`);
        }

function VIPUser(firstName, lastName, age, money) {
            User.call(this, firstName, lastName, age)
            this.money = money;
        }
        // 改变继承关系函数调用一开始就要调用,

        inherit3(VIPUser, User)

        VIPUser.prototype.upgrade = function () {
            console.log(`使用了${100}元`)
        }
var vUser = new VIPUser("我", "爱", 23, 100)

第一种

function inherit1(son, father) {
         son.prototype = father.prototype;
         //直接这样改会污染别人(father)的原型,所以不可取。这样并不是我们想看见的
        }

第二种

function inherit2(son, father) {
            var newObj = Object.create(father.prototype)
            // 新创建一个对象,newObj的隐式原型__proto__指向father.prototype
            son.prototype = newObj;
            // 然后再把son的原型prototype指向newObj,这样就粗略的完成了继承
        };

第二种细节完善版:圣杯模式标准写法

细节一:由于newObj是新创建出来的普通对象,所以他原型.prototype是没有constructor的所以我们需要手动给其添加一个,让他指向本身。
细节二,为了方便,我们一般会在son的原型son.prototype里面添加一个uber 属性,(本来是super(父亲的意思)但是由于super是保留字,所以我们用uber代替) 用来指向farther的原型father.prototype。

 function inherit3(son, father) {
            var newObj = Object.create(father.prototype)
            // 新创建一个对象,newObj的隐式原型__proto__指向father.prototype
            son.prototype = newObj;
            // 然后再把son的原型prototype指向newObj,这样就粗略的完成了继承

            // 细节一,添加constructor
            son.prototype.constructor = VIPUser;
            // 细节二,
            son.uber = father.prototype;
        }

圣杯模式扩展

但是在es5之后,我们已经可以通过Object.getPrototypeOf(son.prototype)方法 获取到他的隐式原型,也就是father.prototype。所以现在看来这行代码有点多余 所以我把uber指向User本身。这样操作起来就会很方便。
记得和同事沟通好

 function inherit4(son, father) {
            var newObj = Object.create(father.prototype)
            // 新创建一个对象,newObj的隐式原型__proto__指向father.prototype
            son.prototype = newObj;
            // 然后再把son的原型prototype指向newObj,这样就粗略的完成了继承

            // 细节一,添加constructor
            son.prototype.constructor = VIPUser;
            // 细节二,
            // son.uber = father.prototype;
            // 细节二进阶版
            son.uber = father;
        }

        function VIPUser(firstName, lastName, age, money) {
            // User.call(this, firstName, lastName, age)
            // 进阶之后我们就可以不用call了,
            // 直接用下面这种
            this.uber(firstName, lastName, age)
            this.money = money;
        }

第四种,以前的写法。没有Object.create()之前

function inherit5(son, father) {
            var Temp = function () {}
            Temp.prototype = father.prototype;
            son.prototype = new Temp();
            son.prototype.constructor = son;
            son.prototype.uber = father;
        };

但是这样写有一个多余的东西,就是var Temp = function () {}。每次调用都会创建一个新变量
后面雅虎公司流传除了一种写法,采用闭包的方式,使var Temp = function () {};永远只运行一次。

var inherit6 = (function () {
            var Temp = function () {};
            return function (son, father) {
                Temp.prototype = father.prototype;
                son.prototype = new Temp();
                son.prototype.constructor = son;
                son.prototype.uber = father;
            }
        }());
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值