写了几篇Vue入门的内容了,今天写点其它的放松一下,简单讲讲javascript中的面相对象。

在面向对象的语言中,都有类的概念,当然es6中开始javascript中也有类的概念了,这里以es5为基础开始讲解,毕竟当前即使写的es6代码,一般还是会通过babel等转码器翻译到es5来执行的;

在js中虽然没有类的概念,但是我们却可以创建对象,一般创建对象有两种方式(这里指自定义对象):

1、使用构造函数

function Person(){}var p=new Person();

2、使用字面量

var p={//各种属性
}

这里着重介绍一下构造函数方式创建对象;以上面代码为例,function Person(){},这个和普通函数有什么区别么?其实除了命名约定外(建议首字母大写)声明上没有任何区别,主要是调用方式的不同,构造函数调用使用new 操作符,使用new 操作符调用构造函数,主要经过四个步骤:

1)、创建一个新的对象;

2)、把this指向新对象;

3)、给对象添加属性

4)、返回新对象

对我们上面的代码进行一点简单的修改:

function Person(){this.name=  "zhangsan";this.age=10;
};var p=new Person();
console.log(p.name,p.age) ;//'zhangsan 10' 

再对比上面使用new操作符调用构造函数的过程,是不是很容易理解输出的结果。

刚才也说了,构造函数和普通函数调用完全相同,那么是不是说可以把构造函数进行普通调用呢?当然可以,对我们的例子再次进行修改:

function Person(){this.name=  "zhangsan";this.age=10;
};//var p=Person();
Person();
console.log(window.name,window.age) ;//'zhangsan 10' 

不使用new操作符调用,不会经过上述四个步骤的隐式处理,所以这个时候不会有新对象的创建和this指向的改变,那么此时的this就指向了全局对象,在浏览器中即window对象,所以可以使用window.name来访问,得到正确的输出。

OOP三大特性:封装、继承、多态;这里只讲述在javascript中如何进行继承(限es5)。

继承只是是代码重用的一种手段,那么在js中如何实现继承呢?

基于原型的继承

首先,函数也是对象,因为所有函数都是Function对象的实例,function 函数名,这种方式定义函数只是一种快捷方式,理论上和var 函数名= new Function(p1,body)等价,从这一点上看函数名其实就是指向函数的指针,但是两者不同的是,通过new Function来创建函数,会被js解释器解释两次,一次是声明时,第二次是对body部分的解析。

其次,任何一个函数创建后,都有一个原型对象,如:

声明一个Person函数,那么我们可以使用Person.prototype来打印一下其原型对象,可以看到其原型对象是一个Object类型,里面包括一个属性(不算__proto__,这个术语内部变量,是实例到原型的指针)constructor,指向了函数Person;另外原型对象本身就是另一个对象的实例(这是是Object函数的实例,=new Object()),任何一个对象的实例,都包含一个内部变量__proto__(chrome浏览器)(Object.create(null)创建的对象除外),指向创建这个对象实例的类型的原型,此处__prop__指向的是Object.prototype.而且可以看到Object.prototype上包含的几个方法(toString、valueOf等)。

上面说了任何一个对象的实例,都包含一个指向其构造函数原型的内部变量,那么我们创建一个Person 的实例:

可以看到是同一个,所以我们可以总结构造函数、实例、和构造函数原型之间的关系如下:

虽然画的是很难看,但是我觉得应该也表达出我要表达的意思了啊,?。

综上,构造函数的原型是一个对象,默认情况下,该对象是Object对象的实例;由于访问时,查找规则如下:先查找当前对象的实例属性,如果找到则返回,否则,查找__proto__指向的原型对象上的同名属性,找到则返回,否则继续原型对象的__proto__指向的对象上的同名属性,一直到Object 实例的__proto__,也就是Object.prototype为止,上述由__proto__构建的这个链接就叫做原型链。

请看如下代码:

function Person(){this.name="zhangsan";  this.myFriends=["zhangsan","lisi"]
}Person.prototype.getName=function(){return this.name;
}function Student(){}Student.prototype=new Person();var s1=new Student();
var s2=new Student();s1.myFriends.push("wangwu");
console.log(s1.myFriends);??
console.log(s2.myFriends);??

上面的代码我基于原型模式实现了一个简单的继承,那么上面的输出是什么呢?可以进行测试,发现输出全部是["zhangsna","lisi","wangwu"];为什么会出现这样的结果,我们不难分析出,我们为Student构造函数的原型重新指定了新的对象,那么此时这个对象(Person实例)就成了Student构造函数的原型对象,则其实例属性就变成了Student构造函数的原型属性,所以我们在通过Student实例访问其原型中引用类型属性 的时候会产生共享,从而出现上述输出。

这种输出结构和我们预期不符,如何处理这种问题呢,根据我们的查找规则,我们如果在本对象上查找到相对应的属性,不会去查找原型对象,基于这一点,我们只要覆盖原型上的对象即可,而最简单的方式就是借用构造函数,修改我们的代码如下:

function Person(){this.name="zhangsan";  this.myFriends=["zhangsan","lisi"]
}Person.prototype.getName=function(){return this.name;
}function Student(){Person.call(this);//关键调用,注意这里是普通调用,非构造函数调用,本次调用,通过call改变this指向为Student的实例

}Student.prototype=new Person();var s1=new Student();
var s2=new Student();s1.myFriends.push("wangwu");
console.log(s1.myFriends);
console.log(s2.myFriends);

使用一次普通的方法调用,传递this,然后动态的在this上创建name和myFriends属性,这样实例在输出时就不会查找原型上的属性了,解决了输出不符合预期的问题。

但是上面的代码仔细分析还有什么问题呢?

1)、Student.prototype.constructor 指向了Person

2)、调用了两次Person方法,一次是构造函数调用,一次是普通调用

3)、借用构造函数方式,是创建新的实例属性覆盖原型属性,这样会创建额外的属性。

再次修改我们的代码,处理上面的问题:

function inherit(child,parent){function F(){}F.prototype=parent.prototype;//只要原型上的内容,而不是实例,这样就避免了实例上的属性,只继承原型上的属性(方法)F.prototype.constructor=child;//这里是强制修改constructor属性,让它指向子类型child.prototype=F.prototype;
}
function Person(){this.name="zhangsan";  this.myFriends=["zhangsan","lisi"]
}Person.prototype.getName=function(){return this.name;
}function Student(){Person.call(this);//关键调用,注意这里是普通调用,非构造函数调用,本次调用,通过call改变this指向为Student的实例

}//Student.prototype=new Person();
inherit(Student,Person)var s1=new Student();
var s2=new Student();s1.myFriends.push("wangwu");
console.log(s1.myFriends);
console.log(s2.myFriends);

当然上述代码还是有些不完美,首先Student.prototype.constructor,应该是不可枚举的,我们这里是却可以。还有很多细节可能考虑不全,细节方面推荐大家啃一下《javascript高级程序设计》这本圣经,本篇就这么多吧。

转载于:https://www.cnblogs.com/Johnzhang/p/7294632.html

OOP in Javascript相关推荐

  1. JavaScript中OOP——面向对象中的继承/闭包

      前  言  OOP  JavaScript中OOP-->>>面向对象中的继承/闭包 1.1面向对象的概念 使用一个子类继承另一个父类,子类可以自动拥有父类的属性和方法.      ...

  2. 学习JavaScript之前,这些知识你应该知道?

    如果你想成为一名专业的程序员,你就需要在你的工具箱中安装 JavaScript. 如今,JavaScript 已成为就业市场上最受欢迎和需求量最大的语言之一.开发人员经常使用它来构建网站.设计游戏和制 ...

  3. JavaScript是否具有接口类型(例如Java的“接口”)?

    本文翻译自:Does JavaScript have the interface type (such as Java's 'interface')? I'm learning how to make ...

  4. 大叔手记(12):我的一次面试经历(谈大叔如何应对面试官)

    本文目的 写本文的目的,大叔不是为了装逼(虽然说话的口气有时候也确实有点装逼,性格导致的,咳...我得改),其实大叔在公司也只是小罗罗,本文的目的主要是为了向大家展示如何通过各种软技能应对面试官,这个 ...

  5. VSCode自定义代码片段9——JS中的面向对象编程

    JavaScript的面向对象编程 {// JS'OOP// 9 如何自定义用户代码片段:VSCode =>左下角设置 =>用户代码片段 =>新建全局代码片段文件... =>自 ...

  6. 如何成为更好的前端工程师?如何才能进入下一个阶段?

    我不断收到很多恰好在学习Web开发方面有丰富经验但被困于不断开发UI模板和项目的人的询问,这确实令人沮丧.更严重的是,他们如何才能进入下一个阶段? 这篇文章更像是备忘单或者清单,我认为这就像我们在友好 ...

  7. JavaScript OOP(2)定义JavaScript类

    JavaScript OOP(2)定义JavaScript类 5.1.7 定义类的方式(工厂方式.构造函数.原型方式.混合方式) 在面向对象的开发中,类被认为是对象的模板.在JavaScript中,可 ...

  8. JavaScript面向对象(一)——JS OOP基础与JS 中This指向详解

    前  言 学过程序语言的都知道,我们的程序语言进化是从"面向机器".到"面向过程".再到"面向对象"一步步的发展而来.类似于汇编语言这样的面 ...

  9. javascript消除字符串两边空格的两种方式,面向对象和函数式编程。python oop在调用时候的优点...

    主要是javascript中消除字符串空格,比较两种方式的不同 //面向对象,消除字符串两边空格 String.prototype.trim = function() { return this.re ...

  10. 浅谈javascript的面向对象(OOP)

    1. 什么是JavaScript JavaScript 是一种描述性的脚本语言(Script Language),它可以非常自由地被嵌入到HTML 的文件之中.使用JavaScript 可以做什么呢? ...

最新文章

  1. 你有哪些深度学习(rnn、cnn)调参的经验?
  2. 计算机四年级下册教案泰山版,泰山版信息技术四年级下册4、制作作息时间表教案设计...
  3. 利用ACS来实现AAA服务
  4. 7-35 部落 (10 分)
  5. Spring的9种设计模式(二)
  6. win10照片查看器不能点下一张的方法
  7. android在体检报告叫什么,体检报告分析app-体检报告分析软件-最火手机站
  8. EPUB电子书阅读必备
  9. 雅虎首席产品官Blake Irving:打造个性化产品的“架子鼓手”
  10. Vue实现在线编辑excel
  11. 征稿 | 听你聊科研,轻松赚稿费!
  12. 《三国演义》之二 十常侍乱政
  13. switch语句用法
  14. 原创 | 2020年数据科学与大数据技术专业填报指南(附院校及专业介绍)
  15. 2022山东视力防控大会,中国护眼产品展,济南近视矫正设备展
  16. Python 利用聚类算法对图片进行颜色压缩
  17. 未明学院:别焦虑,在命运为你安排的时区里,一切都准时
  18. 零基础新手自学Python编程教程入门精通学习资料网站大全
  19. oracle rac v3500_OEL6.X IBM v3500存储多路径配置
  20. vba关于查找方法(Find方法)的应用(一)

热门文章

  1. 电路——I/O口定时翻转电平驱动蜂鸣器注意事项
  2. java matches方法
  3. multipathd dead but pid file exists
  4. (删)Java线程同步实现二:Lock锁和Condition
  5. !!! SQL 数据库开发基础 传智!
  6. Latex 资料汇总(持续更新)
  7. IE安全防护方面强于Firefox和Chrome
  8. Cping (cmd shell )版本
  9. Maven常用命令汇总
  10. 指针的高阶用法——指向指针的指针