构造函数和原型

在ES6之前,对象不是通过类创建的,而是用构造函数的特殊函数来定义。

创建对象可以通过以下三种方式:

  1. 对象字面量 var obj = {}
  2. new Object()
  3. 自定义构造函数 function Foo(){}

构造函数

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到一个函数里面。

构造函数与普通函数的区别

  1. 构造函数一般首字母大写
  2. 调用方式不一样,作用也不一样
  • 普通函数:直接调用 ();
  • 构造函数:需要使用new关键字 new Person();
  1. 构造函数内部使用this来构造属性和方法
function Person(name,job,age)
{
     this.name=name;
     this.job=job;
     this.age=age;
     //一般方法写在原型上,后面讲
     this.sayHi=function(){
         alert("Hi")
      }
 }

new 在执行时会做四件事情

  1. 在内存中创建一个新的对象。
  2. this 指向这个新对象。
  3. 给新对象添加属性和方法。
  4. 返回这个新对象(构造函数不需要return)

返回值不同

普通函数
如果没有设置return,调用时则返回undefined

        // 普通函数
        function person(){}
        var per = person();
        console.log(per); //undefined

构造函数
不需要设置return,实例时默认返回该对象

        // 构造函数
        function Person(){ }
        var per = new Person();
        console.log(per); //Person {}

静态成员与实例成员

静态成员在构造函数本身添加成员 Star.sex = ""
静态成员只能通过构造函数来访问。

实例成员就是构造函数内部通过this添加的成员
实例成员只能通过实例化对象来访问。

构造函数的问题

构造函数原型 prototype(显式原型)

构造函数通过原型定义的函数是所有实例对象共享的

JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象,这个对象的所有方法和属性,都会被构造函数所拥有。

我们可以把相同的方法,直接定义在prototype对象上,这样所有实例对象就可以共享这些方法。

        function Star(uname,age){
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function(){
            console.log("我会唱歌");
        }
        var ldh = new Star("ldh",18);
        var zxy = new Star("zxy",20);
        ldh.sing();
        zxy.sing()

一般情况下,公共属性定义到构造函数里面,而公共方法定义在原型对象上。

原型的主要作用是共享方法

对象原型 __proto __ (隐式原型)

创建对象时系统会自动添加一个proto属性指向我们构造函数的原型对象 prototype

  • 实例的__ proto__对象原型和构造函数的原型对象prototype是等价的。
  • __ proto__对象原型的意义就在于为对象的查找机制提供一个方向。

constructor 构造函数

对象原型(__proto __)和 构造函数原型对象(prototype)里面都有一个cunstructor属性,称为构造函数,因此它指向构造函数本身。

       function Star(uname,age){
            this.uname = uname;
            this.age = age;
        }
        Star.prototype = {
            //如果改变了原来的原型对象,给原型对象赋值的是一个对象,我们需要手动利用constructor
            // 指回原来的构造函数。
            constructor:Star,
            sing: function(){
                console.log("我会唱歌");
            },
            movie: function(){
                console.log("我会演电影");
            }
        }
        var ldh = new Star("ldh",18);
        var zxy = new Star("zxy",20);
        console.log(Star.prototype);
        console.log(ldh.__proto__);
        console.log(Star.prototype.constructor);
        console.log(ldh.__proto__.constructor);

当Star.prototype 以对象的形式添加方法时,会删除constructor属性,所以需要我们手动添加回来

注意:尽量不要给原型对象进行赋值操作,Array.prototype = {},尽量使用Array.prototype.xxx = function(){} 方式,如果不得已记得手动添加constructor属性。

构造函数、实例、原型对象三者之间关系

原型链

//我们Star原型对象里面的__proto__原型指向的是 Object.prototype
console.log(Star.prototype.__proto__ === Object.prototype); //true
console.log(Object.prototype.constructor); //Object 
console.log(Object.prototype.__proto__); //null
        function Star(uname,age){
            this.uname = uname;
            this.age = age;
        }
        Object.prototype.dacen= function(){
            console.log("我是object的方法");
        }
        var ldh = new Star("ldh",18);
        ldh.dacen();

我在Object原型对象中定义个dacen方法,ldh实例对象也能访问到。Object是原型链的尽头

查找机制:
ldh实例首先会在自身对象中查找,如果找不到该方法,则会在Stat原型对象中找,如果还找不到,就会通过__ proto__原型,到Object原型对象上找,直到为null。

原型链中Object与Function的关系

函数对象都是有Function函数生成的,而Function自身也是函数,则有Function.__ proto__ === Function.prototype // true (函数是自身的实例)

Object 也是构造函数,所以Object.__ proto__ === Function.prototype //对象是函数的实例

  1. 通过隐式原型链,f1.__ proto__ 到 Foo.prototype; 从 Foo.prototype.__ proto__ 到 Object.prototype,所以实例对象都可以访问到Object中的原型对象 ;

  2. 通过隐式原型链,f1.__ proto__ 到 Foo.prototype;但 Foo.prototype.__proto __ 不能到Function.prototype(只能是Foo.__proto __===Function.prototype),所以实例对象访问不到函数中的原型对象,只有构造函数才能访问到函数原型对象

  3. 函数和对象互为实例的说法是错误的,因为Object.__ proto__ === Function.prototype 但Function.__ proto__ != Object.prototype,因为Function.__ proto__ === Function.prototype,所以我认为只能说,对象是函数的实例,函数的原型对象是对象的实例!

案例

        function F(){}
        Object.prototype.a= function(){
            console.log("a()");
        }
        Function.prototype.b = function(){
            console.log("b()");
        }
        var f = new F(); //f是一个对象所以找不到Function的原型
        // 在原型链上查找
        console.log(f.__proto__.__proto__); //Object.prototype
        f.a();
        // f.b()  // undefined
        console.log(F.prototype.__proto__); //Object.prototype
        F.a() 
        console.log(F.__proto__ === Function.prototype); //true
        F.b();
        //我们看不了 Function.prototype 的属性

原型链查找机制

  1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
  2. 如果没有就会查找它的原型(也就是__proto __指向的prototype原型对象)。
  3. 如果还找不到就会查找原型对象(Object的原型对象)。
  4. 以此类推一直找到Object为止(null)。
  5. 如果都存在,则就近原则。

原型链继承

        // 父
        function Supper(){
            this.supper = "Supper prototype"
        }
        Supper.prototype.showSupper = function(){
            console.log(this.supper);
        }

        //子
        function Sub(){
            this.sub = "Sub prototype"
        }
        //子类型的原型对象为父类型的一个实例
        Sub.prototype = new Supper();
        //使constructor 指回Sub
        Sub.prototype.constructor = Sub
        
        Sub.prototype.showSub = function(){
            console.log(this.sub);
        }

        // 实例Sub对象
        var sub = new Sub();
        sub.showSub()
        sub.showSupper();

组合继承

  1. 利用原型链实现对父类型对象的方法继承
  2. 利用call借用父类型构造函数初始化相同属性
    <script>
        //父类型
        function Person(name,age){
            this.name = name;
            this.age = age;
        }
        Person.prototype.setName = function(name){
            this.name = name;
        }

        //子类型
        function Student(name,age,price){
            //利用call继承属性
            Person.call(this,name,age) //相当于 this.Person(name,age)
            this,price = price;
        }
        //利用原型链继承方法
        Student.prototype = new Person();
        Student.prototype.constructor = Student;
        Student.prototype.setPrice = function(price){
            this.price = price;
        }

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

推荐阅读更多精彩内容