javascript面向对象详解--原型链

in JavaScript with 0 comment

> 上来先说定义:JavaScript 是一种基于原型而不是基于类的基于对象(object-based)语言。

这句话其实就说出了javascript面向对象的本质,javascript中许多比较难以理解的东西比如 原型链,闭包 都跟javascript面向对象相关,因此与传统语言比如java或php的面向对象进行比较,就能很好的理解javascript面向对象的本质。

再看知乎大神的说法:

> 面向对象本质上就是函数作用域的一种封装。函数的闭包和对象原型链,就可以实现类似java的面向对象,闭包和原型链语法更加底层,也就是说更加强大和难理解。

那对于比较熟悉面向对象的同学来说,通过对比与java等面向对象的异同就能很快理解javascript的面向对象实现思路

点这里看javascript与java面向对象详细对比

  1. 先简单看一下继承实现方法一,通过Manager.prototype = new Employee()实现
//可以在Employee构造函数中添加属性方法,这样子类对象可以继承
    //也可以在Employee构造函数外通过原型prototype添加属性和方法
    //那这有什么不同?
    //其实没什么不同,在Employee构造函数外其实是更加灵活,所以javascript是动态语言
    //当然一般都会把方法放到原型链上,所有实例可以共享,不然每个实例一个方法对象有点浪费
    function Employee(name, dept) {
      this.name = name || "";
      this.dept = dept || "general";
      this.showInfo = function () {};
    }
    Employee.prototype.specialVal = "Employee都有份";
    Employee.prototype.specialfunction = function () {};

    function Manager(name, dept, reports) {
      this.reports = reports || [];
    }
    //通过这种方法把原型链连接起来
    Manager.prototype = new Employee('dealdot','marketing')
    //Employee.prototype.specialVal = "Manager都有份";
    var m1 = new Manager('zlw', 'marketing',['re1','re2'])
    //手动指定一下,不然Manager的constructor有问题
    Manager.prototype.constructor = Manager
    //首先在Manager构造函数里找没找到
    //再去Manager的prototype里找 Manager.prototype 没找到 
    //因为 Manager.prototype--> new Employee,去Employee构造函数里找,没找到
    //再去 Employee.prototype里找,找到了
    //现在如果再找一个叫toString()的方法,还会继承往链上找,即查找Employee.prototype.__proto__
    console.log(Manager.prototype.constructor === Manager) //false
    console.log(m1.__proto__.__proto__.__proto__ === Object.prototype) //true
  1. 在Manager构造函数里 Employee.call(this, name, dept) 实现直接继承,缺点:原型链上的不能继承
function Employee(name, dept) {
      this.name = name || "";
      this.dept = dept || "general";
      this.showInfo = function () {console.log('showInfo')};
    }
    Employee.prototype.specialVal = "Employee都有份";
    Employee.prototype.specialfunction = function () {console.log('specialfunction')};

    function Manager(name, dept, reports) {
      //通过这种方法实现继承,这种方式只能继承Employee构造函数的属性,不能继承到原型链上的
      Employee.call(this, name, dept);
      this.reports = reports || [];
    }
    //Manager.prototype.specialVal = "Manager都有份";
    var m1 = new Manager('dealdot', 'marketing', ['re1','re2'])
    console.log(m1.specialVal)
    console.log(m1.__proto__.__proto__ === Object.prototype) //true
  1. 在第二点基础上用 Employee.call() 继承构造函数的方法 + Object.create(Employee.prototype)实现原型链继承
function Employee(name, dept) {
      this.name = name || "";
      this.dept = dept || "general";
      this.showInfo = function () {console.log('showInfo')};
    }
    Employee.prototype.specialVal = "Employee都有份";
    Employee.prototype.specialfunction = function () {console.log('specialfunction')};

    function Manager(name, dept, reports) {
      //通过这种方法实现继承,这种方式只能继承Employee构造函数的属性,不能继承到原型链上的
      Employee.call(this, name, dept);
      this.reports = reports || [];
    }
    //Manager.prototype.specialVal = "Manager都有份";
    //通过Object.create() 实现原型链接继承
    Manager.prototype = Object.create(Employee.prototype)
    //或者直接用下边这种
    // Manager.prototype = Employee.prototype
    //手动指定一下,不然Manager 的 constructor有问题
    Manager.prototype.constructor = Manager

    var m1 = new Manager('dealdot', 'marketing', ['re1','re2'])
    console.log(m1.specialVal)
    console.log(m1.__proto__.__proto__.__proto__ === Object.prototype)

在 JavaScript 中,您可以在运行时为任何对象添加属性,而不必受限于构造函数提供的属性。--动态语言的灵活性

那么方法1和方法3比较有什么不同呢,可以打印对象看下区别

方法1打印结果

微信图片_20210419120503.png

方法3打印结果

微信图片_20210419120537.png

可以看到方法一更符合继承逻辑,即Manager实例只有自己的属性,而方法三则是把Employee的属性直接添加到Manager中了,但是第三种方法给父构造函数传参更方便,综合考虑js继承还是推荐方法三

  1. 下边再看代码:
    //可以在Employee构造函数中添加属性方法,这样子类对象可以继承
    //也可以在Employee构造函数外通过原型prototype添加属性和方法
    //那这有什么不同?
    //其实没什么不同,在Employee构造函数外其实是更加灵活,所以javascript是动态语言
    // 当然一般都会把方法放到原型链上,所有实例可以共享
    function Employee(name, dept) {
      this.name = name || "";
      this.dept = dept || "general";
      this.showInfo = function () {};
    }
    Employee.prototype.specialVal = "Employee都有份";
    Employee.prototype.specialfunction = function () {};

    function Manager(reports) {
      //取到Employee的属性,后边都是一样的套路
      Employee.call(this);
      this.reports = reports || [];
    }
    //把Manager对象的原型链加到Employee的原型链上
    //Manager.prototype = Object.create(Employee.prototype)
    Manager.prototype = new Employee();
    Manager.prototype.specialValUse = "Manager都有份";

    function WorkBee(name, dept, projects) {
      //通过指定base属性实现继承也可以,但用call与apply最好
      // this.base = Employee
      // this.base(name, dept)
      Employee.call(this, name, dept);
      this.projects = projects || [];
    }
    //WorkBee.prototype = Object.create(Employee.prototype)
    WorkBee.prototype = new Employee();
    WorkBee.prototype.specialValUseWorkBee = "WorkBee都有份";

    function SalesPerson(quota) {
      WorkBee.call(this);
      this.dept = "sales";
      this.quota = quota || "";
    }
    //SalesPerson.prototype = Object.create(WorkBee.prototype)
    SalesPerson.prototype = new WorkBee();

    // function Engineer(machine) {
    //   WorkBee.call(this)
    //   this.dept = 'engineering'
    //   this.machine = machine || ''
    // }
    function Engineer(name, dept, projects, machine) {
      // this.base = WorkBee
      // this.base(name, dept, projects)
      //WorkBee.call(this, name, dept, projects)
      WorkBee.apply(this, [name, dept, projects]);
      //machine属性是本地值,其它则是继承值
      this.machine = machine || "";
    }
    //Engineer.prototype = Object.create(WorkBee.prototype)
    Engineer.prototype = new WorkBee();

    var jane = new Engineer(
      "dealdot",
      "市场部",
      ["project1", "project2"],
      "GK machine"
    );
    jane.bounds = 300;
    // jane.projects = ["navigator"];
    console.log(jane);
    //console.log(jane.__proto__.__proto__.__proto__ === Employee.prototype)
    //构造函数对应prototype,实例对象对应__proto__ 都可以查看出原型链
    console.log(jane.__proto__ === Engineer.prototype);
    console.log(jane.__proto__.__proto__.__proto__.__proto__.__proto__);
  1. 看分析图

微信图片_20210310151344.jpg

  1. 实际打印对象分析

微信图片_20210310160011.png

评论已关闭.