面向对象 和 原型链

一. 面向对象

面向对象编程是一种程序设计思想,它具有 3 个显著的特征:封装、继承、多态。

1. 构造函数

封装:以往以普通对象形式封装的代码只是单纯把一系列的变量或函数组合到一起,所有的数据变量都被用来共享(使用 this 访问)。

<script>
  // 普通对象形式的封装
  let beats = {
    name: '周杰伦',
    setName: function (name) {
      this.name = this.name;
    },
    getName() {
      console.log(this.name); 
    }
  }

  beats.setName('周杰伦');
  beats.getName();
</script>

对比以下通过面向对象的构造函数实现的封装:

<script>
  function Person() {
    this.name = '周杰伦';
    // 设置名字
    this.setName = function (name) {
      this.name = name;
    }
    // 读取名字
    this.getName = () => {
      console.log(this.name);
    }
  }

  // 实例对像,获得了构造函数中封装的所有逻辑
  let p1 = new Person();
  p1.setName('张学友'); 
  console.log(p1.name);//张学友

  // 实例对象
  let p2 = new Person();
  console.log(p2.name); //周杰伦
</script>

同样的将变量和函数组合到了一起并能通过 this 实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。

总结:

  1. 构造函数体现了面向对象的封装特性

  2. 构造函数实例创建的对象彼此独立、互不影响

  3. 命名空间式的封装无法保证数据的独立性

2. 原型对象

实际上每一个构造函数都有一个名为 prototype 的属性,译成中文是原型的意思,prototype 的是对象类据类型,称为构造函数的原型对象,每个原型对象都具有 constructor 属性代表了该原型对象对应的构造函数。

// 1. prototype属性构造函数上的一个属性,这个属性是一个对象;
    //    特点:所有这个构造函数创建出来的实例对象,都可以访问prototype属性上的值

    // 创建一个构造函数
    function Star(name, age) {
      this.name = name
      this.age = age
      this.sing = function () {
        console.log('唱歌')
      }
    }
    //所有构造函数上都有一个 prototype属性 这个属性默认是一个对象
    //普通的静态成员  实例对象无法调用
    Star.type = '明星'
    //向prottype属性上绑定的属性和方法 所有实例对象共享
    Star.prototype.gender = '男'
    Star.prototype.dance = '跳舞'
    //创建实例
    const ldh = new Star('刘德华', 20)
    const zxy = new Star('张学友', 10)
    //实例对象上持有的属性 所有实例对象各一份;
    console.log(ldh.sing === zxy.sing) //false
    console.log(ldh.type);

    //prottype属性上绑定的属性和方法 所有实例对象共享
    console.log(ldh.gender === zxy.gender) // true
    console.log(ldh.dance === zxy.dance) //true

原型对象中的this指向

 // 普通的函数,this指向函数的调用者;
    // 1.构造函数中this指向 - 实例对象;
    // 2.prototype中的this指向 - 实例对象;
    // 构造函数的属性绑定写到构造函数中
    function Star(name, age) {
      this.name = name;
      this.age = age;
    }
    // 构造函数中的方法绑定,写到 prototype 上;
    Star.prototype.sing = function (song) {
      //原型方法中的this,指向的不是原型,而是实例对象;
      console.log(this);
      console.log(this.name + '唱歌 - ' + song);
    }
    //创建实例对象
    const ldh = new Star('刘德华', 18);
    ldh.sing('冰雨');
    //创建实例对象
    const zxy = new Star('张学友', 20);
    zxy.sing('吻别')

constructor作用

  // 1.  构造函数可以通过 prototype 属性找到原型对象
    // 2.  原型对象上有一个属性 constructor  可以找到构造函数

    function Star(name, age) {
      this.name = name
      this.age = age
    }
    // 作用 - 能够让实例对象,找到构造函数;
    const zxy = new Star('张学友', 18)
    console.log(zxy)
    console.log(zxy.constructor)
    //  通过实例对象, 可以找到它的构造函数
    const ldh = new zxy.constructor('刘德华', 20)
    console.log(ldh)
    console.log(ldh.constructor)

原型继承

继承 :  就是一个(子) 对象 继承另一个(父) 对象 的成员

原型继承:把 父对象 作为 子对象 构造函数的 原型。使用他里面的属性和方法。就是把一个father对象 添加到另一个son对象的构造函数中的prototype中,这样son就可以使用father里面的属性和方法了。

    //父解构函数 - 将来让Father构造函数,Son构造函数继承
    function Father(name, age) {
      this.name = name
      this.age = age
    }
    //方法写在原型中
    Father.prototype.somking = function () {
      console.log('抽烟')
    }

    //子构造函数 Son
    function Son(name, age) {
      this.name = name
      this.age = age
    }

    //原型继承 - 将来Son的实例对象,上面没有的属性 就可以去Father的原型去找
    Son.prototype = new Father()
    //原型继承 - 如果用对象覆盖调用一个构造函数的原型对象,需要手动修改他的constructor
    Son.prototype.constructor = Son
    //设置完继承 就可以添加任意方法了
    Son.prototype.talking = function () {
      console.log('说话')
    }
    //测试构造函数
    console.log(Son.prototype.constructor)
    //创建实例 Father
    const m1 = new Son('爸爸', 28)
    console.log(m1)
    //创建实例 Son
    const m2 = new Son('儿子', 8)
    // son 里面现在也可以使用Father里面的方法了
    m2.somking()
    console.log(m2)
// 记住 prototype里面的属性和方法,所有实例对象都可以共享

对象原型

对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。

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

我画了一张图来表示他们三者之间的关系(有点丑不要建议)

从上图构造函数,原型对象,对象实例三者之间的关系中,我们可以引申出原型链的概念。

原型链

这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链,

在 JavaScript 对象中包括了一个非标准备的属性 __proto__ 它指向了构造函数的原型对象,通过它可以清楚的查看原型对象的链状结构。

 

① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。

② 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)

③ 如果还没有就查找原型对象的原型(Object的原型对象)

④ 依此类推一直找到 Object 为止(null)

⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线

⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

⑦这个Object的构造函数new的实例对象由js的解释器执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值