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

面向对象

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

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

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

一、封装

  • 封装就是创建一个对象,集中保存现实中一个事物的属性和功能。
  • 将零散的数据封装进对象结构中,极其便于大量数据的管理维护。
  • 今后,只要使用面向对象思想开发时,第一步都是先封装各种各样的对象结构备用。

封装对象三种方式:

  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中继承都是通过原型对象实现的。

原型

  • 原型对象: 替所有子对象集中保存共有的属性和方法的父对象。

  • 何时使用原型对象:当多个子对象有相同属性或功能时,就可以将相同的属性和方法集中定义在原型对象中。

  • 如何创建原型对象:无需额外操作。在定义构造函数时,会自动生成空的原型对象

  • 如何访问原型对象:构造函数中都有一个自带属性prototype,指向自己的原型对象。

  • 如何实现继承:在上面封装模块案例中,通过new构造函数可以实现子对象继承构造函数的原型对象。new使得子对象的__proto__属 性,指向构造函数的原型对象。

  • 如何向原型对象中添加共有属性:直接对其需要的属性赋值:构造函数.prototype.属性名= 属性值构造函数.prototype.方法名 = function(){...}

    关于原型和原型链及继承会在这篇文章中详述: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就指向谁。

内置类型

  • 内置类型:ES标准中规定的,浏览器已实现的,我们可以直接使用的类型。
  • 目前已有11种类型:StringNumberBooleanArrayDateRegExpFunctionObjectErrorMathglobal/window。(在es6种新增了Symbol类型)
  • 类型:每种类型需要有2部分组成:①构造函数:负责创建该类型的子对象(可以被new的);②原型对象:负责为该类型所有子对象集中保存公共的方法和属性。
  • 所有上面的11种类型中有9种由构造函数和原型对象组成。Math和global/window实际上只是一个内置对象。

三、多态

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

一般认为多态包括2种

  • 重载overload:同一个函数,参数列表不同时,执行的逻辑不同,效果不同。
  • 重写overrider:在子对象中有与父对象重名的方法。当父对象中的该方法不好用时,可以在子对象中定义同名方法,来覆盖父对象中的该方法

如下例: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. 多态:重写——只要觉得从父对象继承来的成员 不要用,都在子对象中重写同名成员。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容