文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JavaScript 中的面向对象

2024-12-02 18:48

关注

本文转载自微信公众号「勾勾的前端世界」,作者西岭。转载本文请联系勾勾的前端世界公众号。

回忆一下什么是对象:Coding 第一奥义:面向对象编程

JavaScript 语言本身的设计缺陷,误打误撞,成了解释最为彻底的“世界原本的样子”的计算机编程语言;

——西岭《凡人凡语》

Everything is object (万物皆对象),JS 语言中将一切都视为 对象 。

JavaScript 语言的对象体系,不基于“类” 创建对象,是基于构造函数(constructor)和原型链(prototype)。

简单方式创建对象

我们可以直接通过 new Object() 创建:

  1. var person = new Object() 
  2. person.name = 'Jack' 
  3. person.age = 18 
  4.  
  5. person.sayName = function () { 
  6.     console.log(this.name

字面量方式创建对象

每次创建通过 new Object() 比较麻烦,所以可以通过它的简写形式对象字面量来创建:

  1. var person = { 
  2.   name'Jack'
  3.   age: 18, 
  4.   sayName: function () { 
  5.     console.log(this.name
  6.   } 

构造函数

JavaScript 语言使用构造函数作为对象的模板。

所谓 "构造函数",就是一个普通的函数,只不过我们专门用它来生成对象,这样使用的函数,就是构造函数。

它提供模板,描述对象的基本结构。一个构造函数,可以生成多个对象,这些对象都有相同的结构。

  1. function Person (name, age) { 
  2.   this.name = name 
  3.   this.age = age 
  4.   this.sayName = function () { 
  5.     console.log(this.name
  6.   } 
  7.  
  8. var p1 = new Person('Jack', 18) 
  9. p1.sayName() // => Jack 
  10.  
  11. var p2 = new Person('Mike', 23) 
  12. p2.sayName() // => Mike 

解析构造函数代码的执行

在上面的示例中,使用 new 操作符创建 Person 实例对象;

以这种方式调用构造函数会经历以下 5 个步骤:

  1. function Person (name, age) { 
  2.   // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象 
  3.   // 然后让内部的 this 指向新创建的对象 
  4.   // 接下来所有针对 this 的操作实际上操作的就是刚创建的这个对象 
  5.  
  6.   this.name = name 
  7.   this.age = age 
  8.   this.sayName = function () { 
  9.     console.log(this.name
  10.   } 
  11.  
  12.   // 在函数的结尾处会将 this 返回,也就是这个新对象 

构造函数和实例对象的关系

构造函数是根据具体的事物抽象出来的抽象模板,实例对象是根据抽象的构造函数模板得到的具体实例对象。

实例对象由构造函数而来,一个构造函数可以生成很多具体的实例对象,而每个实例对象都是独一无二的。

每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数。

反推出来,每一个对象都有其构造函数

  1. console.log(p1.constructor === Person) // => true 
  2. console.log(p2.constructor === Person) // => true 
  3. console.log(p1.constructor === p2.constructor) // => true 

因此,我们可以通过实例对象的 constructor 属性判断实例和构造函数之间的关系。

构造函数存在的问题

以构造函数为模板,创建对象,对象的属性和方法都可以在构造函数内部定义。

  1. function Cat(name, color) { 
  2.   this.name = name
  3.   this.color = color; 
  4.   this.say = function () { 
  5.     console.log('hello'+this.name,this.color); 
  6.   }; 
  7. var cat1 = new Cat('猫''白色');  
  8. var cat2 = new Cat('猫''黑色');  
  9. cat1.say(); 
  10. cat2.say(); 

在该示例中,从表面上看好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象, name 和 say 都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

那么,能不能将相同的内容,放到公共部分,节约计算机资源呢?

原型

JavaScript 的每个对象都会继承一个父级对象,父级对象称为 原型 (prototype) 对象。

原型也是一个对象,原型对象上的所有属性和方法,都能被子对象 (派生对象) 共享,通过构造函数生成实例对象时,会自动为实例对象分配原型对象。而每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。

null 没有自己的原型对象。

这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在构造函数的 prototype 属性上,也就是实例对象的原型对象上。

  1. function Cat(color) { 
  2.   this.color = color; 
  3.  
  4. Cat.prototype.name = "猫"
  5. Cat.prototype.sayhello = function(){ 
  6.     console.log('hello'+this.name,this.color); 
  7. Cat.prototype.saycolor = function (){ 
  8.     console.log('hello'+this.color); 
  9.  
  10. var cat1 = new Cat('白色');  
  11. var cat2 = new Cat('黑色');  
  12. cat1.sayhello(); 
  13. cat2.saycolor(); 

这时所有实例对象的 name 属性和 sayhello() 、saycolor 方法,其实都是在同一个内存地址的对象中,也就是构造函数的 prototype 属性上,因此就提高了运行效率节省了内存空间。

原型及原型链

构造函数的 prototyp 属性,就是由这个构造函数 new 出来的所有实例对象的 原型对象

所有对象都有原型对象。

  1. function Cat(name, color) { 
  2.     this.name = name
  3.  } 
  4.  
  5. var cat1 = new Cat('猫'); 
  6.  
  7. console.log(cat1.__proto__.__proto__.__proto__); 

而原型对象中的属性和方法,都可以被实例对象直接使用。

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。

而这正是多个对象实例共享原型所保存的属性和方法的基本原理。

对象的属性和方法,有可能是定义在自身内,也有可能是定义在它的原型对象上。由于原型本身也是对象,又有自己的原型,所以形成了一条可向上追溯的链条,叫 原型链(prototype chain)。

注意,不在要原型上形成多层链式查找,非常浪费资源。

内置标准库与包装对象

在内置标准对象中,对象是 JavaScript 语言最主要的数据类型,三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”(wrapper)。

所谓“包装对象”,就是分别与数值、字符串、布尔值相对应的Number、String、Boolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。

  1. var v1 = new Number(123); 
  2. var v2 = new String('abc'); 
  3. var v3 = new Boolean(true); 
  4.  
  5. typeof v1 // "object" 
  6. typeof v2 // "object" 
  7. typeof v3 // "object" 
  8.  
  9. v1 === 123 // false 
  10. v2 === 'abc' // false 
  11. v3 === true // false 

包装对象的最大目的,首先是使得 JavaScript 的对象涵盖所有的值,其次使得原始类型的值可以方便地调用某些方法。

原始类型的值,可以自动当作对象调用,即调用各种对象的方法和参数。

这时,JavaScript 引擎会自动将原始类型的值转为包装对象实例,在使用后立刻销毁实例。

比如,字符串可以调用length属性,返回字符串的长度。

  1. 'abc'.length // 3 

上面代码中,abc是一个字符串,本身不是对象,不能调用length属性。JavaScript 引擎自动将其转为包装对象,在这个对象上调用length属性。调用结束后,这个临时对象就会被销毁。这就叫原始类型与实例对象的自动转换。

 

来源:勾勾的前端世界内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯