JavaScript 构造函数相关

JavaScript中的对象概念和其他语言的对象概念有些不一样,所以学习起来稍微有点特殊。

  • 在JavaScript中,构造函数的使用一般如下:

    /*
      function MyConstructor(){
        // 函数实体写在这里
        // 根据需要在this上创建属性,然后赋值给它们,比如:
        this.a = 123;
        // 等等...
     
        // 如果函数具有返回对象的return语句,
        // 则该对象将是 new 表达式的结果。 
        // 否则,表达式的结果是当前绑定到 this 的对象。
        //(即通常看到的常见情况,其实就是在调用函数)。
      }
     */
    
    function C(){
      this.a = 37;
    }
    
    var o = new C();
    console.log(o.a); //会输出 37
    
    
    function C2(){
      this.a = 37;
      return {a:38};
    }
    
    o = new C2();
    console.log(o.a); //会输出 38
    
  • 所谓的“构造函数”,其实本质上还是一个普通的函数,但是由于在内部使用了this变量,如果对构造函数使用new运算符,就能够生成一个实例化的对象出来,并且在构造函数里面所使用到的this变量或方法,都会绑定到这个实例对象上面来。

    如果此时写一个人的构造函数,可以简单这么写:

    function Person(name, age, sex){
    	this.name = name;
        this.age = age;
        this.sex = sex;
    }
    

    实例化一个人的对象出来的话,可以这么来使用:

    var zhangsan = new Person("zhangsan", 18, "男");
    var lisi = new Person("lisi", 19, "男");
    
    console.log(typeof zhangsan); //object
    console.log(zhangsan); //Person { name: 'zhangsan', color: 18, sex: '男' }
    console.log(typeof lisi); //object
    console.log(lisi); //Person { name: 'lisi', color: 19, sex: '男' }
    

    经过上面的操作,就会构造两个Person的对象zhangsanlisi出来,这两个对象都自动会含有一个constructor属性,指向它们的构造函数;也可以通过instanceof运算符来验证实例对象和原型对象的关系:

    console.log(zhangsan.constructor === Person);//True
    console.log(lisi.constructor === Person);//True
    
    console.log(zhangsan instanceof Person);//True
    console.log(lisi instanceof Person);//True
    
  • 构造函数的prototype属性,每个构造函数都拥有一个protorype属性,能够指向另外一个对象,这个对象的所有属性和方法,都会被构造函数的实例所继承,这样就可以把对象的一些通用不变的属性和方法直接定义在prototype对象上:

    function Person(name, age, sex){
    	this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    Person.prototype.species = "人类";
    Person.prototype.eat = function () {
        console.log("吃东西");
    }
    
    var zhangsan = new Person("zhangsan", 18, "男");
    
    console.log(zhangsan.species); //人类
    zhangsan.eat(); //吃东西
    
    

    这个时候所有实例化对象的type属性和eat()方法,其实都是指向prototype对象。

  • 可以使用isPrototypeOf()方法来判断某个proptotype对象和某个实例之间的关系:

    console.log(Person.prototype.isPrototypeOf(zhangsan)); //True
    
  • 可以使用hasOwnProperty()方法来判断对象的某个属性是本身属性还是proptotype对象的属性:

    console.log(zhangsan.hasOwnProperty("sex")); //True
    console.log(zhangsan.hasOwnProperty("species")); //False
    
  • JavaScript中的in运算符可以用来判断实例化对象是否含有某个属性[无论是不是本身的属性],也可以用来遍历某个对象的所有属性。

构造函数的继承的五种方法

此时这里有两个构造函数,PersonDeveloper,如下:

function Person(){
  this.species = "人类";
}

function Developer(name, age, sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
}
  • 使用call()或者apply()的方法,将父对象的构造函数绑定在子对象上面,实现如下:

    function Developer(name, age, sex){
        
        //使用apply方法
    	Person.apply(this, arguments);
        //或者使用call方法
        //Person.call(this, name, age, sex);
        
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    var developer_1 = new Developer("zhangsan", 20, "男");
    console.log(developer_1);
    //Developer { species: '人类', name: 'zhangsan', age: 20, sex: '男' }
    
  • 使用prototype属性:

    function Person(){
        this.species = "人类";
    }
    
    function Developer(name, age, sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    
    Developer.prototype = new Person();
    Developer.prototype.constructor = Developer;
    
    var developer_1 = new Developer("zhangsan", 20, "男");
    
    console.log(developer_1);
    // Developer { name: 'zhangsan', age: 20, sex: '男' }
    console.log(developer_1.species); 
    // 人类
    
    
    

    Developer.prototype = new Person();就相当于给构造函数Developerprototype对象重新赋了一个新的值;又由于任何一个prototype对象都有一个constructor属性,指向它的构造函数的。而且每个实例的constructor属性,默认调用prototype对象的constructor属性。

    因此此时Developerprototype对象的constructor属性就是指向Person的,这个时候加上Developer.prototype.constructor = Developer;,会让Developerprototype对象的constructor属性就是指向Developer

  • 直接继承prototype

    这种方式是将不变的父类的属性直接写入到它的prototype上面,此时直接将父类的protptype属性赋值给子类的prototype,然后再更改子类的prototypeconstructor属性指向即可,这种方式的优点是效率较高[不用执行和建立Person的实例了],缺点是两个构造函数的prototype属性是指向同一个对象的,那么任何修改都会映射到两个上面:

    function Person() {
    
    }
    Person.prototype.species = "人类";
    
    function Developer(name, age, sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    
    Developer.prototype = Person.prototype;
    Developer.prototype.constructor = Developer;
    
    var developer_1 = new Developer("zhangsan", 20, "男");
    
    console.log(developer_1);
    // Developer { name: 'zhangsan', age: 20, sex: '男' }
    console.log(developer_1.species);
    // 人类
    
    
    // 缺点就是两个prototype会指向同一个
    console.log(Developer.prototype.constructor);
    // [Function: Developer]
    console.log(Person.prototype.constructor);
    // [Function: Developer]
    
    
  • 在直接继承prototype的基础上,可以再利用一个空对象做中介

    因为直接继承prototype这种方式仍有一些缺点,所以此时可以这么做:

    function Person() {
    
    }
    Person.prototype.species = "人类";
    
    function Developer(name, age, sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    var F = function(){};
    F.prototype = Person.prototype;
    Developer.prototype = new F();
    Developer.prototype.constructor = Developer;
    

    这样对Developerprototype对象的修改,就不会影响到Person,而是修改到了F上面,此时还可以继续改进,将上面的代码封装到一个函数里面,更加方便使用:

    function packaging(Child, Parent) {
        var F = function () {
        };
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
        //uber属性:指向父类原型。
        Child.uber = Parent.prototype;
    }
    
    packaging(Developer, Person)
    
    var developer_1 = new Developer("zhangsan", 20, "男")
    console.log(developer_1) // Developer { name: 'zhangsan', age: 20, sex: '男' }
    
  • 拷贝继承

    如果纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,也能够实现继承,具体如下:

    function Person() {
    
    }
    Person.prototype.species = "人类";
    
    function Developer(name, age, sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    function copy_inherit(Child, Parent) {
        var parent = Parent.prototype;
        var child = Child.prototype;
        for (var attribute in parent){
            child[attribute] = parent[attribute];
        }
        child.uber = parent;
    }
    
    copy_inherit(Developer, Person)
    
    var developer_1 = new Developer("zhangsan", 20, "男")
    console.log(developer_1) //Developer { name: 'zhangsan', age: 20, sex: '男' }
    console.log(developer_1.species)// 人类
    

参考 Javascript面向对象编程(二):构造函数的继承

posted @ 2020-06-11 16:28  依吁的回忆  阅读(100)  评论(0编辑  收藏  举报