构造函数和原型

在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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容