浅谈javascript面向对象三大特点,通过面向对象看原型原型

2022-02-09  本文已影响0人  Fairy_妍

面向对象

面向对象的三大特点:封装、继承、多态。

先使用一个个的对象结构集中存储现实中事物的属性和功能。 然后,再按需使用不同对象中的不同属性和功能。这就是面向对象编程。

面向对象使其便于大量数据的管理维护

一、封装

封装对象三种方式:

  1. 直接使用{}。访问时使用对象名.属性名对象名.方法名()

    var lilei={
      sname:"Li Lei",//姓名
      sage:11,//年龄
      intr:function(){
        console.log(`I'm ${this.sname}, I'm ${this.sage}`)
      }
    }
    
  2. 使用new Object()

    var lilei = new Object();  // 创建空对象
    lilei.sname = 'Li Lei';
    lilei.intr = function(){
      console.log(`I'm ${this.sname}`)
    }
    

    该方法揭示了js语言底层最核心的原理:js中所有对象底层都是 关联数组

关联数组和对象
a. 关联数组和对象的存储结构都是 键值对 的形式

b. 访问成员时的方法都是`对象名/数组名['成员名']`,简写`对象名/数组名.成员名`

c. 强行给不存在的位置或属性赋值不会报错,且添加新属性。

d. 强行访问不存在的位置/属性的值不会报错

e. 可以用for...in..循环遍历`for(var 属性名 in 对象名/数组名){...}`
  1. 使用构造函数

    使用前两种方法创建对象,如果想创建多个结构相同的对象时,代码会重复很多,不便维护。解决办法:构造函数-描述同一类型的所有对象的统一结构的函数。

    // 使用构造函数步骤1:定义构造函数
    function Student(name, age) {
        this.name = name
        this.age = age
        this.intr = function(){
            console.log(`I'm ${this.name}, I'm ${this.age}`)
        }
    }
    // 步骤2:使用构造函数反复创建多个对象
    var lilei = new Student('lilei', 20)
    var liumei = new Student('liumei', 18)
    

总结

  1. 使用new时,此处new做了4件事:①创建一个新的空对象;②设置子对象的__proto__属 性,指向构造函数的原型对象(下文详细讨论继承);③调用构造函数时,将构造函数的this指向新new出来的对象;同时在构造函数内通过强行赋值方式,为新对象添加规定的属性和方法。④返回新对象的地址保存到左侧变量中。

  2. 此处出现了this的2种情况:①obj.fun()调用obj中的fun方法时,fun中的this指向obj对象。②new Fun()时,Fun中的this指向新new出来的对象。

二、继承

在上面的构造函数中,每次new一个新的对象时,都要给新对象赋值一个intr的function,每次定义function时相当于new Function操作,每次都会在内存中开辟新的空间保存这个function,实际上的funtion的内容都是一样的,没必要每次都新创建,造成内存浪费。

解决方法:子对象都需要的相同功能和属性值可以用继承解决。

继承:父对象中的成员,子对象无需重复创建,就可直接使用。

js中继承都是通过原型对象实现的。

原型

使用了原型对象后,在子对象访问属性或方法时,js引擎会优先在子对象内部查找自有属性或方法。当子对象中没有时,js引擎会沿着__proto__属性去父对象(原型)上查找。若原型上存在该方法或属性,就直接使用,和使用自身的属性方法一样。这个沿着原型查找方法和属性的链路即为原型链。原型链是由多级父对象逐级继承形成的链式结构。

总结:构造函数中方法可以定义在原型对象中,为所有子对象共有。

function Student(name, age) {
    this.name = name
    this.age = age
}
// 将intr方法定义在构造函数的原型对象上
Student.prototype.intr = function(){
    console.log(`I'm ${this.name}, I'm ${this.age}`)
}
var lilei = new Student('lilei', 20)
lilei.intr();   //I'm lilei, I'm 20
var liumei = new Student('liumei', 18)
liumei.intr();  //I'm liumei, I'm 18

此处出现了this的第③种情况:在原型对象共有的方法中使用了this,此处不看定义只看调用,此时谁调用这个公共的方法,this就指向谁。

总结以上三种this情况:谁调用,this就指向谁。

内置类型

三、多态

多态指的是同一个函数在不同情况下表现出不同的状态

一般认为多态包括2种

如下例:toString()的方法调用会产生多种不同的结构

function Student(sname,sage){
    this.sname=sname;
    this.sage=sage;
}
var lilei=new Student("Li Lei",11);
console.log(lilei);     // Student {sname: "Li Lei", sage: 11}
var arr=[1,2,3];
var now=new Date();
console.log(lilei.toString());  // [object Object]
console.log(arr.toString());    // 1,2,3
console.log(now.toString());    // Wed Feb 09 2022 18:17:05 GMT+0800 (中国标准时间)

发生这样的原因在于:①lilei的toString方法调是用的Object.prototype上的方法;②数组的toString方法在Array.prototype上重新定义了toString方法,所有没有再往上查找Object.prototype上的toString方法;③Date类型跟Array同理,对自己类型的原型对象进行了重写,没有继续往上查找toString方法。④以Array类型为例,查找顺序是:arr -> Array.prototype -> Object.prototype -> null。

所以如果Student不想用Object.protoype上的tostring方法,则可以在自己原型上重写

Student.prototype.toString=function(){
    return `my name is ${this.sname}`
}
console.log(lilei.toString());  // my name is Li Lei

面向对象总结:

  1. 封装:创建对象两种方式。
  2. 继承:所有子对象共用的属性值和方法,都要 放在构造函数的原型对象中
  3. 多态:重写——只要觉得从父对象继承来的成员 不要用,都在子对象中重写同名成员。
上一篇 下一篇

猜你喜欢

热点阅读