什么是原型?

823 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

原型

引出原型

书接上文,我们讨论了需要使用原型的场景。
现在我们回顾一下:
我们创建了一个Star构造函数
在这个Star构造函数中,为每一个对象都添加sing方法
这个方法是我们在构造函数内部创建的,也就是说构造函数每执行一次,就会创建一个新的sing方法,所有的实例的sing方法都是唯一的

可以通过console.log(对象1.方法 == 对象2.方法) (方法不加括号) 来证明是否是同一个方法

这就导致了,如果我创建10000个对象,就会创建10000个新的方法,而10000个方法实现的功能是一样的
这是完全没有必要的,完全可以使所有对象共享同一个方法
(就比如一个宿舍10人,给每一个人配一个厕所是非常 ** 的事情,应该是10人共享一个厕所)
解决方法就是把这个 构造函数中这个方法在全局作用域中定义

    function fun(sang) {
                console.log(sang);
            }
            
         再把fun赋值给this.sing
         this.sing = fun;

这样即使构造函数执行了1000次,我的sing方法还是只有一个,就是这个fun
但是呢!这么写会有一个问题啊,就是把这个函数定义在全局作用域下,污染了全局作用域的命名空间
我这个函数叫fun,那别人就不能叫fun了,别人要是也叫fun,就把你这个函数覆盖掉了,就是说定义在全局作用域中是很不安全的。

概述

接下来就涉及到一个概念,叫原型(prototype)
我们所创建的每一个函数,解析器都会向函数对象中添加一个属性prototype
(不管你是构造函数还是普通函数)

👉首先要明白!每一个函数中的prototype都是唯一的,因为这个显式原型属性prototype指向的是空Object实例对象,每一个实例都具有唯一性👈

示例
function Person(){}; function MyClass(){};
console.log(MyClass.prototype == Person.prototype) 是false

这个prototype属性对应着一个对象,这个对象就是我们所谓的原型对象
1. 如果函数作为普通函数调用prototype没有任何作用
2. 当函数作为构造函数调用prototype时,它们所创建的对象都会有一个隐含的属性,指向该构造函数的原型对象,它就是__proto__ (所有对象都具有隐式原型属性__proto__)

        var mc = new MyClass();
        var mc2 = new MyClass();
        console.log(mc2.__proto__ == mc.__proto__ == MyClass.prototype); true

        原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象
        所以啊,为了解决刚刚的两个问题,我们可以将对象中共有的内容,统一设置到原型对象中

        //向MyClass的原型对象中追加属性  先通过MyClass.prototype获取原型对象,再追加(就是很普通的向对象中追加属性的方式)
    
        MyClass.prototype.a = 123;

        这时候你再console.log(mc.a) 发现会输出123,mc2同理

        得出: 当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,找到就直接用(沿着原型链)

        //向MyClass原型对象中添加一个方法
        MyClass.prototype.sayHello = function(){
            alert('hello');
        }

        mc.sayHello 输出hello ,mc对象中是没有这个方法的,然后就会去原型对象中找这个方法,找到了就用

原型尽头

向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";
var mc3 = new MyClass();
console.log(mc3.name); // "我是原型中的名字"

使用in检查对象中是否含有某个属性时,如果对象中没有,但是原型对象中有,也会返回true console.log("name" in mc); //true

那我们如果想看这个对象是不是自己有这个属性,而不是原型里面有没有,该怎么办?
这时不能用in,用每个对象自带的方法hasOwnProperty()来检查该对象自身中是否包含该属性 console.log(mc.hasOwnProperty("name")); 输出false

那么问题又来了,哈哈,我们mc这个对象中没有hasOwnProperty()方法呀,它哪来的,我们可以检测一下
console.log(mc.hasOwnProperty("hasOwnProperty")); 输出false
因为hasOwnProperty()是检测该对象自身是否包含某个属性的,hasOwnProperty这个方法mc对象中确实没有,但是在原型对象中有,所以使用这个方法时,在对象自身中没找到,就去原型对象中找了,而原型对象中有这个方法,就能用了0 0

欸嘿0 0问题又来了,我们也没给mc对象的原型对象添加hasOwnProperty方法呀,它哪来的,我们再检测一下原型对象自身是否包含这个方法
console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));输出false

那这个方法哪来的呢?我们可以很操蛋的知道,原型对象也他*是个对象,那这个原型对象里面也有那个隐含的属性__proto__(隐式原型属性),也就是说原型对象也有原型对象

还是那句话,当我们使用一个对象的属性或方法时,会先在对象自身中查找,有则用,没有就去原型对象中找,有则用,没有就再去原型的原型中查找
我们再检测一下,mc对象的原型对象的原型对象中是否包含hasOwnProperty方法 console.log(mc.__proto__.__proto__hasOwnProperty("hasOwnProperty"));输出true

牛*plus!!

这时候我们再看看,是否有原型的原型的原型 console.log(mc.__proto__.__proto__.__proto__) 找不到对象,输出null
所以在原型对象的原型对象就到头了(尽头在Object.prototype.__proto__ = null)

还是那句话,,,也就是说我们一直找,直到找到Object对象的原型,由于Object对象的原型对象没有原型了,如果依然没找到就返回undefined(找属性或方法找不到返回undefined)


以上是原型的一个概述,接下来我会详细去说明各个细节
传送入口