js原型详解

js中的原型毫无疑问一个难点,学习如果不深入很容易就晕了!

任何一个js知识点,比如事件流,闭包,继承等,都有许许多多的说法,对于这些知识点我们都应该先熟练的使用,然后自己整理一套属于自己的理解说辞,才不会忘

原型(对象属性)

Javascript规定,每一个函数都有一个prototype对象属性,指向另一个对象(原型链上面的)。
prototype(对象属性)的所有属性和方法,都会被构造函数的实例继承。这意味着,我们可以把那些不变(公用)的属性和方法,直接定义在prototype对象属性上。

prototype就是调用构造函数所创建的那个实例对象的原型(proto)。

prototype可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

原型链 (JS原型与原型链继承)

实例对象与原型之间的连接,叫做原型链。proto( 隐式连接 )
JS在创建对象的时候,都有一个叫做proto的内置属性,用于指向创建它的函数对象的原型对象prototype。
内部原型(proto)和构造器的原型(prototype)
1、每个对象都有一个proto属性,原型链上的对象正是依靠这个属性连结在一起
2、作为一个对象,当你访问其中的一个属性或方法的时候,如果这个对象中没有这个 方法或属性,那么Javascript引擎将会访问这个对象的proto属性所指向上一个对 象,并在那个对象中查找指定的方法或属性,如果不能找到,那就会继续通过那个对象 的proto属性指向的对象进行向上查找,直到这个链表结束。

实际开发中,有这样一种写法,面向对象,这种写法就是通过构造函数以及原型来运用的(混合模式开发面向对象)

每一个函数都有一个原型属性prototype(对象属性),里面放置的是共有、公有的属性或者方法。(一般情况属性是私有的)。注意,只有函数才有prototyoe属性,

    function Person() {
       
    }
    var p = new Person()
    console.log(Person.prototype); // Object{} 
    console.log(p.prototype); //undefined

这个例子可以发现,函数是存在prototype属性的

任何对象都是默认存在构造器的,此时我们的Person()只是普通函数,它其实是js内置方法Function()构造出来的,而p此时是Person() new出来的,只有new 过了,才叫构造函数

浅谈constructor

在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。

上代码更容易理解

    function Person() {
       
    }
    var p = new Person()
    console.log(Person.prototype); // Object{} 
    console.log(p.prototype); // undifined
    console.log(p.constructor); //function Person(){}    
    此处的p是通过 Person函数构造出来的,所以p的constructor属性指向Person
    console.log(Person.constructor); //function Function(){}
    之前提过,每个函数其实是通过new Function()构造的
    console.log({}.constructor); // function Object(){}
    每个对象都是通过new Object()构造的
    console.log(Object.constructor); // function Function() {}
    Object也是一个函数,它是Function()构造的
    console.log([].constructor);  //function Array(){}

我想大家此时对于prototype属性有了一定的了解

console.log(Object.constructor); // function Function() {}

可能大家对于这个有些不理解,毕竟我们实际开发中哪见过这玩意

    console.log(Function instanceof Object); // true
    console.log(Object instanceof Function); // true

这样大家是不是就明白了呢 函数是对象构造的 对象也是函数构造的,俩者即是函数也是对象,所以为什么构造函数它是一个函数却返回一个对象,俩者是互相继承的关系

    var o1 = new f1();
    typeof o1 //"object"  

重点讲解一下原型prototype的用法,最主要的方法就是将属性暴露成公用的,上代码

    function Person(name,age){
        this.name = name;
        this.age = age;
        this.sayHello = function(){
            console.log(this.name + "say hello");
        }
    }
    var girl = new Person("bella",23);
    var boy = new Person("alex",23);
    console.log(girl.name);  //bella
    console.log(boy.name);   //alex
    console.log(girl.sayHello === boy.sayHello);  //false

再看下面的代码

    function Person(name,age){
        this.name = name;
        this.age = age;
        
    }
    Person.prototype.sayHello=function(){
        console.log(this.name + "say hello");
    }
    var girl = new Person("bella",23);
    var boy = new Person("alex",23);
    console.log(girl.name);  //bella
    console.log(boy.name);   //alex
    console.log(girl.sayHello === boy.sayHello);  //true 

我们给函数Person的原型中声明了sayHello方法,当我们的构造实例对象去访问的时候访问的方法是同一个,这就是prototype原型最大的作用,共享属性和方法

那么prototype与proto有什么关系,先看这么一串代码

    var obj = {}
     此处等价于 var obj = new Object()
    console.log(obj.__proto__ === Object.prototype)//true   

JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做 __proto__ 的内置属性,用于指向创建它的构造函数的原型对象。

根据前面的例子我们很清楚,obj是通过new Object 构造出来的一个对象,那我们Object的原型对象就是Object.prototype,在Object下面构造出来的所有对象,都有一个__proto__ 指向它的原型,我们称这个为原型链

    var obj = []
    console.log(obj.__proto__ === Array.prototype)//true

这个也是一样的道理

此处要理解,就是原型对象是谁构造的,是谁构造的我们看构造器

console.log(Array.prototype.constructor)//Array{}

原型对象肯定是它本身构造的,接着看之前的构造函数代码

    function Person(name,age){
    this.name = name;
    this.age = age;
    
    }
    Person.prototype.sayHello=function(){
        console.log(this.name + "say hello");
    }
    var girl = new Person("bella",23);
    var boy = new Person("alex",23);
    console.log(Person.prototype.constructor);  //Person
    console.log(girl.__proto__==Person.prototype);   //true
    console.log(girl.constructor==Person);  //true 

现在看是不是特别简单 注意这里是两个下划线__proto__ 叫 杠杠proto杠杠

接下来给一串代码

 function Person(){

 }
 var person1=new Person()

快速回答
1.person1.__proto__==

2.person1.constructor==

3.Person.__proto__==

4.Person.prototype.constructor==

5.person1.prototype.constructor==

6.Person.prototype==

大家可以测测输入的答案打印是不是为true 如果你很快打出,说明你理解的已经很透彻了

接下来我找几道题一起分析一下,深入了解

              function A(){
                }
                function B(a){
                  this.a = a;
                }
                function C(a){

                  if(a){
                    this.a = a;
                  }
                }
                A.prototype.a = 1;

                B.prototype.a = 1;

                C.prototype.a = 1;


                console.log(new A().a);  //1

                console.log(new B().a);//undefined

                console.log(new C(2).a);//2

我们先观察第一个
new A() 很明显它是A()构造的实例对象,在下面A函数prototype共享了一个属性a=1,那么实例对象也可以通过这个属性访问到他的值==1

第一个比较简单,我们看第二个
第二个其实是个坑,首先B()函数它声明的时候设置了一个参数,注意里面的方法this,此时this指向的是window,我们都知道,然而在构造函数之后this指向了new B(),然而此时B 没有传入参数,也就是说此时的参数a==undefined,
因此new B()下的属性a==this.a==undefined (不知道这么说能不能理解 ̄□ ̄||)

如果我们给B 传入一个数(可以是a,但a一定要先声明)

                console.log(new B(3).a); //3
                console.log(new B(5).a); //5

这样应该就好理解了,总不能我们自己设置的属性还覆盖不来爸爸给你的属性吧

第三个很好理解,从第二个的角度分析,如果传入参数,输出参数,如果没有 输出1

这道题没什么拐弯,比较简单,我们再看一题

    function Fun(){
        var getName = function(){
            console.log(1);
        }
        return this;
    }
    Fun.getName = function(){
        console.log(2);
    }
    Fun.prototype.getName = function(){
        console.log(3);
    }
    var getName = function(){
        console.log(4);
    }
    function getName(){
        console.log(5);
    }
            
    Fun().getName(); //4
    getName(); //4
    new Fun().getName();//3
    new new Fun().getName();//3

做题一定要一步步来,先看第一个 Fun().getName();
很明显此时是Fun()调用getName(),我们看看Fun(),注意,函数的返回值永远是return,此处return返回了this,在函数中这里的this指向window,也就是说第一步Fun( )返回的是window,转换一下就是window.getName(),
我们在什么找一下全局的getName,最终输出4

第二个其实很简单,我感觉就是来迷惑我们的,前面省略了window,很明显还是4

第三个也太简单了,只要你理解了我上面写的内容,一看就知道输出3

第四个,两个new? 不要慌 我们来分析一下

new后面跟的肯定是函数,用来构造实例对象,所以new Fun()这是不能拆的,
很明显此时new Fun()是一个构造函数,虽然称之为函数,但其实它是一个对象,因为函数是方法,而方法是不能调用属性的,但是对象可以,既然是对象,何来new 构造呢,所以此处分析可以得到new Fun ().getName才是一体连着的,
那很明显就是原型下的方法咯,所以输出3

随着框架应用的越来越广泛,很多用法我们可能并不是那么熟悉,还是希望大家秉着学习之心,多多复习

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容