Js高级程序设计第三版学习(六章)

                              Js高级程序设计第三版学习(六章)

 

第六章 面向对象程序设计

   对象 : 无序属性合集 其属性可以包含基本值 对象或函数

  1.理解对象:

  • 属性类型 : 描述属性的各种特征,这些特征为了实现js引擎用的,js不能直接访问他们, 用[[  ]] 表示,es中有两种属性:数据属性和访问器属性

1.数据属性 : 有四个特征 [[Configurable]],[[Enumerable]],[[Writable]],[[value]], 修改单个属性使用Object.defineProperty() 参数: 属性所在对象,属性名字和描述符对象(只能是4个特征一个或多个)

      var obj = {
        name: 'lmx',
        age: 12,
        description: 'ojbk'
      };

      // configurable 默认true 改为false 不能通过delete 删除属性 也不能改为访问器属性
      // configurable一旦改为false [[configurable]]与[[enumerable]]就不可以更改 否则会报错
      Object.defineProperty(obj, 'name', {
        configurable: false
      });
      delete obj.name; //无效
      console.log(obj.name); //还会打印lmx
      /* study.html:43 Uncaught TypeError: Cannot redefine property: name
         at Function.defineProperty ()
         at study.html:43 */
      Object.defineProperty(obj, 'name', {
        // enumerable: false, //会报错
        // configurable: true //会报错
        writable:false //不会报错
      });
      
      //writable 默认true 改为false 无法直接改变属性的值,但是可以通过改变[[value]]来改变
      Object.defineProperty(obj, 'age', {
        writable: false
      });
      obj.age = 18;
      console.log(obj.age); //12
      Object.defineProperty(obj, 'age', {
        value: 22
      });
      console.log(obj.age); //22

      //enumerable 默认为true 改为false 不能被for in 枚举
      Object.defineProperty(obj, 'description', {
        enumerable: false
      });
      for (const key in obj) {
        console.log(key); // name age 没有description
      }

      //value 存放属性的值 默认为undefined 更改value可以改变属性的值
      Object.defineProperty(obj, 'description', {
        value: 'ppap'
      });
      console.log(obj.description); // ppap

2.访问器属性 : 访问器属性不能直接定义,必须使用Object.defineProperty() 定义,有四个特征 [[Configurable]],[[Enumerable]],[[Set]],[[Get]]

      var obj = {
        name: 'lmx',
        age: 12,
        description: 'ojbk',
        _ppap: 'lala'
      };
      console.log(obj.name);
      //访问器属性 不可以直接定义 但是这里我让其直接与name属性同名 并没有问题
      // name属性就变成了访问器属性
      Object.defineProperty(obj, 'name', {
        get: function() {
          return this.description;
        },
        set: function(newValue) {
          this.description = newValue;
        }
      });
      obj.name = '你好明天';
      console.log(obj.name + '-------------' + obj.description); //你好明天-------------你好明天

      //访问器属性 包含一对函数 get() set() 并不是必须的, 再读取的时候调用get() 在写入的时候调用set() 默认都是undefined
      //如果缺少 get(), 变成只写, 缺少set(), 变成只读
      Object.defineProperty(obj, 'ppap', {
        get() {
          return this._ppap;
        },
        set(newValue) {
          this._ppap = newValue;
        }
      });
      obj.ppap = '风往哪吹';
      console.log(obj.ppap + '------------' + obj._ppap); //风往哪吹------------风往哪吹

 

  • 定义多个属性 : js对象中一个对象有多个属性,如果要一起修改时,可以使用Object.defineProperties(),参数: 参数1--目标对象, 参数2 -- 对象-要修改或添加的属性
          var obj = {
            name: 'lmx',
            age: 12,
            description: 'ojbk',
            _ppap: 'lala'
          };
          
          //规定多个属性 规则与Object.defineProperty() 基本相同
          Object.defineProperties(obj, {
            name: {
              configurable: false
            },
            age: {
              writable: false,
              value: 100
            },
            ppap: {
              get() {
                this._ppap;
              }
            }
          });

     

  •  读取属性特性:  可以使用Object.getOwnPropertyDescriptor() 来获取一个对象某个属性的操作符, 参数: 参数1-- 读取属性的对象, 参数2 -- 读取的属性 . 返回: 对象 包含属性的描述符
          var obj = {
            name: 'lmx',
            age: 12,
            description: 'ojbk',
            _ppap: 'lala'
          };
    
          //规定多个属性 规则与Object.defineProperty() 基本相同
          Object.defineProperties(obj, {
            name: {
              configurable: false
            },
            age: {
              writable: false,
              value: 100
            },
            ppap: {
              get() {
                this._ppap;
              }
            }
          });
    
         //获取访问器属性 描述符
         var ppapDescripter =  Object.getOwnPropertyDescriptor(obj, 'ppap');
         console.log(ppapDescripter);
    
         //获取数据属性 描述符
         var nameDescripter =  Object.getOwnPropertyDescriptor(obj, 'name');
         console.log(nameDescripter);

    Js高级程序设计第三版学习(六章)_第1张图片

  • 补充 : 数据属性与访问器属性, 在调用defineProperty()时,不指定所有操作符,返回操作符是不同的 如下
          var obj = {
            name: 'lmx',
            age: 12,
            description: 'ojbk',
            _ppap: 'lala'
          };
          //数据属性
          Object.defineProperty(obj, 'name', {});
          var descriptor = Object.getOwnPropertyDescriptor(obj, 'name');
          console.log('数据属性--------------');
          console.log(descriptor);
    
          //访问属性
          Object.defineProperty(obj, 'ppap', {
            get() {
              return this._ppap;
            },
            set(newValue) {
              this._ppap = newValue;
            }
          });
          var descriptor = Object.getOwnPropertyDescriptor(obj, 'ppap');
          console.log('\n访问属性');
          console.log(descriptor);

  2.创建对象:  

  • 工厂模式 :  用函数封装以特定接口创建对象, 虽然工厂模式解决了创建多个相似对象代码的冗余,但是并未解决对象的类型问题.(总是object类型)
          function SuperClass(name, age) {
            var obj = {
              name: name,
              age: age
            };
            return obj;
          }
    
          var xiaoMing = SuperClass('xiaoming', 18);
          var xiaoHong = SuperClass('xiaohong', 18);
          //都包含相同属性名
          console.log(xiaoMing); //{name: "xiaoming", age: 18}
          console.log(xiaoHong); //{name: "xiaohong", age: 18}

     

  •  

    构造函数模式 : 创建自定义的构造函数,从而定义自定义对象的属性和方法, 构造函数: 方法名首字母大写, 不会手动创建对象,没有返回值,将属性方法赋值给this,

1.new :  通过构造函数创建对象,需要使用new关键字, 执行new时会有4个步骤,下面是模拟代码

      function SuperClass(name, age) {
        //创建一个新对象
        var obj = {};
        //执行构造函数 实质执行SuperClass 模式改用SubClass
        var result = SubClass.apply(obj, arguments);
        //将obj的proto属性指向构造函数的prototype
        //对象的属性查找实质就是通过_proto_一层一层往上查找_proto_,由自己的_proto_直到null为止
        obj._proto_ = SuperClass.prototype;
        //判断result的类型 如果是引用类型就赋值给obj引用否则返回obj
        if (result instanceof Object) {
          obj = result;
        }
        return obj;
      }

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

 2.构造函数的问题: 创建实例时每个方法都需要重新创建一遍,当然也可使用全局函数,但是对于我们的需求而言不合理

      function SubClass(name, age) {
        this.name = name;
        this.age = age;
        this.sayHello = function(){
          console.log(name);
        }
      }
      person1 = new SubClass('lmx');
      person2 = new SubClass('xml');
      console.log(person1.sayHello == person2.sayHello);//false 也就是说 实例方法引用的并不是相同指针

      //当然 我们可以使用指针的形式, 但是这并不单单属于实例的方法,而是全局方法,全部都可以调用这是不合理的
      function Person(name){
        this.name = name;
        this.sayHi = sayHi;
      }
      function sayHi(){
        console.log(this.name);
      }
      var person3 = new Person('lmx');
      var person4 = new Person('xml');
      console.log(person3.sayHi == person4.sayHi);//true
      sayHi();
  • 原型模式: 每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象的作用就是让所有实例共享它所包含的属性和方法
      function SubClass() {};
      SubClass.prototype.name = 'lmx';
      SubClass.prototype.sayHello = function(){
        console.log(this.name);
      }
      person1 = new SubClass();
      person2 = new SubClass();
      person1.sayHello();//lmx
      person2.sayHello();//lmx
      console.log(person1.sayHello == person2.sayHello);//true

1.理解原型对象:

当我们创建一个函数,就会为函数创建一个prototype属性,这个属性指向原型对象,每一个原型对象会默认获取一个constructor属性,这个属性指向prototype所在的函数, Person.prototype.curstructor = Person, 而每一个实例都有一个 指针叫[[prototype]](脚本无法访问) ,但是某些浏览器(chrome,ff)支持访问实现了一个_proto_属性, 这个属性指向的是构造函数的prototype, 所以实例与构造函数之间的联系与构造函数无关,只与构造函数的prototype 有关.

isPrototypeOf() 用来检测对象间是否存在依存关系,存在true 否则false

  function SubClass() {};
      SubClass.prototype.name = 'lmx';
      SubClass.prototype.sayHello = function(){
        console.log(this.name);
      }
      person1 = new SubClass();
      person2 = new SubClass();
      console.log(SubClass.prototype.isPrototypeOf(person1));//true

Object.getPrototypeOf() 会取得 这个对象得原型

      function SubClass() {}
      SubClass.prototype.name = 'lmx';
      SubClass.prototype.sayHello = function() {
        console.log(this.name);
      };
      person1 = new SubClass();
      console.log(Object.getPrototypeOf(person1)); 

代码搜索实例得属性时,会先在实例本身查找,然后才会到原型中查找,这意味着我们只能通过对象实例获取原型得属性,却不能修改原型得属性,因而不必担心实例属性与原型属性重名得问题,js只会先读取实例得属性.

function SubClass() {}
      SubClass.prototype.name = 'lmx';
      SubClass.prototype.sayHello = function() {
        console.log(this.name);
      };
      person1 = new SubClass();
      person2 = new SubClass();
      person1.name = 'lalalalla';
      console.log(person1.name); // lalalalla
      console.log(person2.name); //lmx

hasOwnProperty() : 检测属性是存在于实例(true) 还是 原型(false), 返回true或者false

      function SubClass() {}
      SubClass.prototype.name = 'lmx';
      SubClass.prototype.sayHello = function() {
        console.log(this.name);
      };
      person1 = new SubClass();
      person2 = new SubClass();
      person1.name = 'lalalalla';
      console.log(person1.hasOwnProperty('name')); //true
      console.log(person2.hasOwnProperty('name')); //false

2.原型与in操作符:  in 判断对象是否有该属性(包含原型), for in (循环遍历,包括原型)可枚举得属性, Object.keys() 枚举实例对象可枚举的属性,Object.getOwnPropertyNames() 枚举实例对象所有属性

      function SubClass() {}
      SubClass.prototype.name = 'lmx';
      SubClass.prototype.age = 18;
      SubClass.prototype.sayHello = function() {
        console.log(this.name);
      };
      person1 = new SubClass();
      person1.name = 'lalalalla';
      Object.defineProperty(person1,'name',{
        enumerable:false
      })
      for (const key in person1) {
        console.log(key); // age sayHello
      }
      console.log('name' in person1);//true

      //Object.keys() 只得到可枚举得实例对象属性(不枚举原型)
      console.log(Object.keys(person1));//[]

      //Object.getOwnPropertyNames 得到实例对象本身所有的属性 无论是否可以枚举
      console.log(Object.getOwnPropertyNames(person1));//["name"]

3.更简单的原型语法:  我们可以使用字面量写法重写函数的原型,这样可以减少不必要的输入,但是这样等于给函数的原型以字面量的形式创建了一个新对象,这也导致了Person.prototype.constructor 指向了Object 而不是Person了,当然我们也可重新给原型的constructor赋值Person,但是这样会导致constructor变得可枚举, 可能还需要Object.defineProperty()来重新修正

      function Person() {}
      Person.prototype = {
        name: 'lmx',
        age: 18
      };
      var xiaoHong = new Person();
      for (const key in xiaoHong) {
        console.log(key);//name age
      }
      console.log(xiaoHong.constructor);//ƒ Object() { [native code] }
 function Person() {}
      Person.prototype = {
        constructor: Person,
        name: 'lmx',
        age: 18
      };
      var xiaoHong = new Person();
      for (const key in xiaoHong) {
        console.log(key); // constructor name age
      }
      console.log(xiaoHong.constructor); //ƒ Person() {}
 function Person() {}
      Person.prototype = {
        constructor: Person,
        name: 'lmx',
        age: 18
      };
      //更改constructor 枚举修饰符
      Object.defineProperty(Person.prototype,'constructor',{
        enumerable:false
      })
      var xiaoHong = new Person();
      for (const key in xiaoHong) {
        console.log(key); //  name age
      }
      console.log(xiaoHong.constructor); //ƒ Person() {}

4.原型的动态性: js中实例与原型之间是松耦合性,只有在原型中查找值得时候才算一次搜索,因此那怕我们在实例创建后再在原型中添加属性或者方法,实例对象也可以拿到, 但是 用字面量方式重写原型不可以, 由于先创建实例,然后再重写原型, 实例对象的[[prototype]]指向原型是Person.prototype 而用字面量方式 Person.prototype的指针变了,所以实例对象就无法拿到新的指针了

 //直接再原型上添加属性
      function Person() {}
      var person1 = new Person();
      Person.prototype.name = 'lmx';
      console.log(person1.name);//lmx 

      //字面量形式更改 原型
      function Student(){}
      var xiaoHong = new Student();
      Student.prototype = {
        name:'xiaoHong'
      }
      console.log(xiaoHong.name);//undefined

5.原生对象的原型: 所有原生的引用类型,都是用原生模式创建的,我们可以给原生对象的原型添加方法(不推荐,可能会跟原生方法冲突)

6.原型对象的问题: 原型共享方法非常合适,但如果是引用类型,那么他的实例也会共享,实例一般要有属于自己的属性,使用共享的方法,所以原型很少被单独使用

 //person1 与 person2 共享一个数组
      function Person() {}
      var person1 = new Person();
      Person.prototype.sons = ['lmx', 'nihao', 'mingtian'];

      var person1 = new Person();
      var person2 = new Person();
      person1.sons.push('haha');
      console.log(person1.sons);//(4) ["lmx", "nihao", "mingtian", "haha"]
      person2.sons.push('home');
      console.log(person2.sons);//(5) ["lmx", "nihao", "mingtian", "haha", "home"]
  • 组合使用构造函数模式和原型模式: 这种混合模式基本就是 js自定义引用类型的默认模式, 构造函数用来定义实例属性,原型模式用来共享方法,属性.
     //组合使用构造函数模式和原型模式
          function Person(name,age) {
            this.name = name;
            this.age = age;
          }
          Person.prototype.sayHi = function(){
            console.log(this.age);
          }
          var person1 = new Person('lmx',18);

     

  •  动态原型模式:前文中,原型与构造函数是分开的,其实可以把原型模式 写在构造函数里面,然后自己的判断是否使用原型(不能使用字面量方式更改prototype)

      //动态原型模式
          function Person(name, age) {
            this.name = name;
            this.age = age;
            if (this.name !== 'lmx') {
              Person.prototype.sayHi = function() {
                console.log(this.age);
              };
            }
          }
    
          var person1 = new Person('lmx', 18);
          person1.sayHi(); //not a function

     

  •  寄生构造函数模式: 在构造函数中主动创建对象(自定义对象,或原生),并返回新对象, 目的, 在不改变原对象的构造函数情况下,又拥有自定义的方法(原生对象 Array Object等)

          //寄生构造函数模式
          function Person(name, age) {
            this.name = name;
            this.age = age;
            if (this.name !== 'lmx') {
              Person.prototype.sayHi = function() {
                console.log(this.age);
              };
            }
          }
          //创建一个新的构造函数,在原构造函数的基础上更改
          function customPerson(name,age){
            var obj = new Person(name,age);
            obj.sayHellow = function(){
              console.log(this.name);
            }
            return obj;
          }
          var xiaoLiZi = new customPerson('xiaoli',27);
          xiaoLiZi.sayHellow();
    
          //实例与customPerson无关
          console.log(customPerson.prototype.isPrototypeOf(xiaoLiZi))//false
          console.log(Person.prototype.isPrototypeOf(xiaoLiZi))//true

     

  •  稳妥构造函数模式:  稳妥对象就是指函数内部不使用this.调用时不用new创建,除了使用特定方法外,无法获取函数内部的变量.
      //稳妥构造函数模式
      function Person(name, age) {
        var o = new Object();
        var sonName = name;
        o.saySonName = function() {
          console.log(sonName);
        };
        return o;
      }
      var person1 = Person('lmx', 18);
      person1.saySonName();
      console.log(person1.sonName);//undefined

  3.继承:  js只有实现继承(没有接口继承), js实现继承主要是通过原型链实现的.

  • 原型链 : 本质就是 让一个构造函数的原型等于另一个构造函数的实例,这个实例的原型又等于另外的构造函数的实例,以此类推,层层递进之后就变成了链条的形式这就是原型链.

原型链继承,就是通过重写prototype来实现的,与字面量重写原型需要注意的点相似, 实例xiaoMing的原型的contructor指向的并不是默认的SubClass 而是其父类 SuperClass构造函数

      //父类
      function SuperClass(name, age) {
        this.name = name || 'lmx';
        SuperClass.prototype.sayHello = function() {
          console.log(this.name);
        };
      }
      //子类
      function subClass() {
        this.age = 18
      }
      //让子类的prototype = 父类实例
      subClass.prototype = new SuperClass();

      //子类实例
      var xiaoMing = new subClass();
      xiaoMing.sayHello();
      console.log(Object.getPrototypeOf(xiaoMing).constructor);//SuperClass
      console.log(xiaoMing instanceof subClass) //true
      console.log(xiaoMing instanceof SuperClass) //true
      console.log(xiaoMing instanceof Object) //true

1.别忘记默认原型: 引用类型都是继承自Object, 构造函数的原型中也存在[[prototype]],它指向了 Object.prototype.

2.确定原型和实例的关系:  instanceOf  构造函数.prototype.isPrototypeOf() 前文讲过,不复述

3.谨慎的定义方法: 子类需要重写父类的方法时,指定要放在将代码放在替换原型语句之后,否则原型被重写我们实际调的还是父类的方法.

      //父类
      function SuperClass(name, age) {
        this.name = name || 'lmx';
        SuperClass.prototype.sayHello = function() {
          console.log(this.name);
        };
      }
      // //再重写前重写构造函数的方法
      // subClass.prototype.sayHello = function(){
      //   console.log('hahah');
      // }
      
      //子类
      function subClass() {
        this.age = 18;
      }

      //让子类的prototype = 父类实例
      subClass.prototype = new SuperClass();
      subClass.prototype.sayHello = function() {
        console.log('hahah');
      };

      //子类实例
      var xiaoMing = new subClass();
      xiaoMing.sayHello(); //代码在重写实例之前还是lmx 代码在重写之后hahah

4.原型链的问题: 

    问题1:  使用原型链继承,如果属性值为引用类型(数组,对象),那么会被所有实例共享

    问题2:  没有办法在不影响所有实例的情况下,给构造函数传参

  • 借用构造函数

为了解决原型链引用类型与传参的问题,可以使用借用构造函数,实质就是在构造函数中使用call或者apply方式调用父类,保证是子类调用的方法,将其属性复制了一份,同时子类也可以自己定义想定义的属性,

      //父类
      function SuperClass(name, age) {
        this.name = name || 'lmx';
      }

      //子类
      function subClass() {
        SuperClass.apply(this, arguments);
        this.age = 18;
      }

      //让子类的prototype = 父类实例
      subClass.prototype = new SuperClass();

      //子类实例
      var xiaoMing = new subClass('woani');
      console.log(xiaoMing.name);//woani
  • 组合继承

将借用构造函数,与原型链相结合, 使用原型链中共享的方法同时每个实例还有自己的属性,实例之间互不影响

      //父类
      function SuperClass(name, age) {
        this.name = name || 'lmx';
        this.sons = ['xx','oo']
      }
      SuperClass.prototype.sayHellow = function(){
        console.log('fff团万岁')
      }
      //子类
      function subClass() {
        SuperClass.apply(this, arguments);
        this.age = 18;
      }

      //让子类的prototype = 父类实例
      subClass.prototype = new SuperClass();

      //子类实例
      var xiaoMing = new subClass('woani');
      var xiaoLi = new subClass('xx00');
      xiaoMing.sons.push('风往哪吹');
      xiaoLi.sons.push('明天');
      console.log( xiaoMing.sons); //3) ["xx", "oo", "风往哪吹"]
      console.log( xiaoLi.sons) //(3) ["xx", "oo", "明天"]
  • 原型式继承

原型式继承的目的就是 基于已有对象, 在不自定义构造函数的前提下,创建新对象,  实质就是 创建一个临时构造函数,让他的原型等于目标对象, 在返回这个临时构造函数的实例, 这样既不用创建构造函数,还可以使用父类属性, 但并未解决属性是应用类型的问题

      //原型式继承
      var person = {
        name: '溟',
        age: 25
      };

      function copyObj(obj) {
        function F() {}
        F.prototype = obj;
        return new F();
      }

      var xiaoMing = copyObj(person);
      console.log(xiaoMing.name);//溟

es5 标准化了原型式继承 使用 Object.create() 函数, 参数1 目标对象  参数2 已操作符的形式来修改属性 , 会覆盖父类同名属性.

      //原型式继承
      var person = {
        name: '溟',
        age: 25
      };

      var xiaoHomg = Object.create(person, {
        name: {
          enumerable: false,
          value: '明天'
        }
      });
      console.log(xiaoHomg.name + '--------' + xiaoHomg.age);// 明天--------25
  • 寄生式继承

寄生式继承与原生继承相似, 创建一个封装继承的函数,然后再函数内部增强对象(添加方法或属性) 最后返回这个对象

      //寄生式继承
      var person = {
        name: '溟',
        age: 25
      };

      function copyObj(obj) {
        function F() {}
        F.prototype = obj;
        return new F();
      }

      function createAnother(obj){
        var other = copyObj(obj);
        other.sayHello = function(){
          console.log('nihao');
        }
        return other
      }
      var xiaoMing = createAnother(person);
      console.log(xiaoMing.name); //溟

 

  • 寄生组合式继承 

与组合式继承相似, 寄生组合式继承同样也是通过借用构造函数的方式继承属性,利用原型链继承方法, 但是我们会发现在组合式继承中,调用了两次父类型的构造函数,一次在子类借用时,一次在重写子类原型的时候,寄生组合式继承,把组合式继承与寄生继承相结合,在原型继承中继承的是父类的prototype既只继承原型,而不是构造函数的实例,然后在寄生式中更改子类的原型,这样就实现了只调用了一次父类的构造函数.

正常的继承思路:

new SubClass ==> SubClass.prototype ==> SuperClass.prototype ==> Object.prototype

这也是为什么在寄生组合式继承中, 让临时的构造函数的prototype 等于 SuperClass.prototype 然后让Subclass.prototype = new 临时构造函数(), 如果直接修改 SubClass.prototype = SuperClass.prototyoe 那么原型链的顺序就会出错 变成了

new SubClass ==>  SuperClass.prototype ==> Object.prototype

      //寄生组合式继承
      //父类
      function SuperClass(name) {
        this.name = name || 'lmx';
        SuperClass.prototype.sayHi = function() {
          console.log(this.name);
        };
      }
      //子类
      function SubClass(name){
        SuperClass.apply(this,arguments);
        this.age = 18
      }
      //孙子类
      function GrandSonClass(name){
        SubClass.apply(this,arguments);
        this.description = '孙子儿';
      }
      //原生继承
      function copy(proto) {
        //使临时构造函数的原型 = 父类的原型
        function F() {}
        F.prototype = proto;
        return new F();
      }
      //寄生继承
      function inhert(sub, supreme) {
        var other = copy(supreme.prototype);
        //修正子类的构造函数
        sub.prototype.constructor = sub;
        //让子类的原型等于 临时构造函数的实例
        sub.prototype = other;
      }
      inhert(SubClass,SuperClass)
      inhert(GrandSonClass,SubClass)


      var xiaoMing = new GrandSonClass('小明');
      console.log(xiaoMing.name);// 小明
      console.log(xiaoMing.age); // 18 
      console.log(xiaoMing.description); //孙子

错误的寄生组合式继承思路

      //错误思路 虽然也可以进行继承 但是他们其实共用了一个原型
      //与原型链继承顺序不符合
      //父类
      function SuperClass(name) {
        this.name = name || 'lmx';
        this.ah = 'ah';
        SuperClass.prototype.sayHi = function() {
          console.log(this.name);
        };
      }
      //子类
      function SubClass(name){
        SuperClass.apply(this,arguments);
        this.age = 18
      }

      SubClass.prototype = SuperClass.prototype;

      var person = new SubClass('小明');
      person.sayHi();
      console.log(person.ah);

      console.log(Object.getPrototypeOf(person) == SubClass.prototype) //true
      console.log(Object.getPrototypeOf(person) == SuperClass.prototype) //true
      console.log(person instanceof SuperClass); // true
      console.log(person instanceof SubClass); // true
      console.log(Object.getPrototypeOf(person).constructor);//SuperClass(name) 

 

你可能感兴趣的:(js)