面向对象(较深入)

面向对象

面向对象是现实的抽象方法

  • 对象是JavaScript中一个非常重要的概念,这是因为对象可以将多个相关联的数据封装到一起,更好的描述一个事物:

    • Car: { 颜色:color, 价格:price, 品牌:brand }
  • 用对象来描述事物,更有利于我们将现实的事物,抽离成代码中某个数据结构

  • JavaScript支持多种编程范式,包括函数式编程和面向对象编程:

    • JavaScript中的对象被设计成一组属性的无序集合,像是一个哈希表,有key和value组成;
    • key是一个标识符名称,value可以是任意类型,也可以是其他对象或者函数类型;
    • 如果值value是一个函数,那么我们可以称之为是对象的方法;
  • 创建对象的方式

    // JavaScript
    // 1.
    var obj1 = new Object()
    obj1.name = "zzwi"
    obj1.age = 18
    obj1.gender = "man"
    obj1.going = function() {
        console.log(this.name + "want to be a great person")
    }
    // 2.
    var obj2 = {
        name: "zzwi",
        age: 18,
        gender: "man",
        going: function() {
            console.log(this.name + "want to be a great person")
        }
    }
    

对属性操作的控制

delete

代码如下:

// JavaScript
// 承接上文代码
delete obj1.age // 删除对象obj1的age属性
console.log(obj1) // {name: 'zzwi', gender: 'man', going: ƒ}
// 可以看到,age属性确实被移除了

Object.defineProperty()

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

Object.defineProperty(obj,prop,descriptor)

  • 可接收的三个参数:

    • obj:要定义属性的对象
    • prop:要定义或修改的属性的名称或Symbol
    • descriptor:要定义或修改的属性描述符
  • 返回值:

    • 被传递给函数的对象
  • 属性描述符的类型有两种:

    • 数据属性描述符
    • 存取属性描述符
    configurableenumerablevaluewritablegetset
    数据描述符××
    存取描述符××
configurable:

表示是否允许delete删除属性,是否可以修改属性值,是否可以将其(数据属性)修改为存取属性描述符

  • 当我们直接在一个对象上定义某个属性时,这个属性的configurable为true
  • 当我们通过属性描述符定义一个属性时,这个属性的configurable默认为false

承接上文代码

// JavaScript
Object.defineProperty(obj2,"age",{
      configurable: false // 设置属性age不可更改
})
delete obj2.age
console.log(obj2) // {name: 'zzwi', age: 18, gender: 'man', going: ƒ}
// 属性age没有被删除
// JavaScript
// 如果这样敲代码则会报错
Object.defineProperty(obj2,"age",{
      configurable: false 
})
Object.defineProperty(obj2,"age",{
      configurable: true 
})
// 控制台报错
enumerable:

表示属性是否可以通过for-in或者Object.keys()返回该属性;

  • 当我们直接在一个对象上定义某个属性时,这个属性的enumerable为true
  • 当我们通过属性描述符定义一个属性时,这个属性的enumerable默认为false

代码承接上文

// JavaScript
Object.defineProperty(obj2,"age",{
    configurable: false,
    enumerable: false
})
console.log(Object.keys(obj2)) // ['name', 'gender', 'going']
writable:

表示是否可以修改属性的值;

  • 当我们直接在一个对象上定义某个属性时,这个属性的writable为true
  • 当我们通过属性描述符定义一个属性时,这个属性的writable默认为false

承接上文代码

// JavaScript
Object.defineProperty(obj2,"age",{
    // configurable: false,
    enumerable: false,
    writable: false // 不允许修改该属性的值
})
obj2.age = 20
// delete obj2.age
console.log(obj2) // {name: 'zzwi', gender: 'man', age: 18, going: ƒ}
// 属性age的值无法修改
value:

属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改;

  • 默认情况下这个值是undefined

承接上文代码

// JavaScript
Object.defineProperty(obj2,"age",{
    configurable: false,
    enumerable: false,
    writable: true, // 允许修改该属性的值
    value: 20 // 将属性age的值修改为20
})
// delete obj2.age
console.log(obj2) // {name: 'zzwi', gender: 'man', age: 20, going: ƒ}
get:

获取属性时会执行的函数。默认为undefined

承接上文代码

// JavaScript
var address = "普宁市"
Object.defineProperty(obj2,"address",{
    configurable: true,
    enumerable: true,
    get: function() {
        return address
    }
})
console.log(obj2.address) // 普宁市
set:

设置属性时会执行的函数。默认为undefined

承接上文代码

// JavaScript
var address = "普宁市"
Object.defineProperty(obj2,"address",{
    configurable: true,
    enumerable: true,
    get: function() {
        return address
    },
    set: function(value) {
        address = value
    }
})
console.log(obj2.address) // 普宁市
obj2.address = "广州市"
console.log(obj2.address) // 广州市

另外的写法:

// JavaScript
var obj = {
    _age: 18, // 社区规范:_开头代表不希望被修改(私有属性)
    running: function() {console.log("Running now")},
    set age(value) {
        this._age = value
    },
    get age() {
        return this._age
    }
}
console.log(obj.age) // 18
obj.age = 19
console.log(obj.age) // 19

这种写法默认属性操作符configurable / enumerable 的值都为true.

同时定义多个属性:

承接上文代码

// JavaScript
Object.defineProperties(obj1,{
    name: {
        writable: true,
        value: "coder"
    },
    going: {
        configurable: true,
        value: "excellent"
    }
})
console.log(obj1) // {name: 'coder', gender: 'man', going: 'excellent', age: 18}

注意:

value/writable 和 get/set 不共存

Object.defineProperty()的 get/set 便是vue2的响应式原理

Object.getOwnPropertyDescriptor

承接上文代码

// JavaScript
// 获取某一个特性属性的属性描述符
console.log(Object.getOwnPropertyDescriptor(obj1,"name"))
// 获取对象的所有属性描述符
console.log(Object.getOwnPropertyDescriptors(obj1))

对象内属性限制方法的补充:

// JavaScript
// 禁止对象继续添加新的属性
Object.preventExtensions(obj1)
obj1.address = "广州市"
console.log(obj1.address) // undefined
// JavaScript
// 禁止对象内的属性变化
Object.seal(obj1)
delete obj1.age
obj1.address = "广州市"
console.log(obj1.address) // undefined
console.log(obj1.age) // 18
// JavaScript
// 不允许对象内的属性值发生改变
Object.freeze(obj1)
obj1.age = 19
console.log(obj1.age) // 18

创建多个相似对象的方案

方案一:逐个创建

如下代码所示

// JavaScript
var p1 = {
    name: '张三',
    age: 18,
    height: 1.60
};
var p2 = {
    name: '李四',
    age: 19,
    height: 1.70
};
var p3 = {
    name: '王五',
    age: 20,
    height: 1.80
};
...

缺点:创建同样的对象时,需要编写重复的代码

方案二:工厂模式

如下代码所示

// JavaScript
function createPerson(name,age,height,address) {
    var p = {
        name,
        age,
        height,
        address,
        eating: function() {
            console.log(this.name+"在吃饭");
        },
        running: function() {
            console.log(this.name+"在跑步");
        }
    }
    return p
}
var v1 = createPerson("张三",18,1.60,"北京市")
var v2 = createPerson("李四",19,1.70,"上海市")
var v3 = createPerson("王五",20,1.80,"广州市")
...

缺点:打印创建对象时,对象的类型都是Object类型

方案三:构造函数

约定俗成的规范(非强制):构造函数首字母大写

JavaScript的构造函数和其他编程语言内的有点不太一样

  • js的构造函数也是一个普通的函数,从表现形式来说,和千千万万个普通的函数没有任何区别;
  • 如果有这么一个普通的函数被使用new操作符来调用了,那么这个函数就称之为是一个构造函数,并且它会执行如下操作:
    1. 在内存中创建一个新的对象(空对象)
    2. 这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性
    3. 构造函数内部的this,会指向创建出来的新对象
    4. 执行函数的内部代码(函数体代码)
    5. 如果构造函数没有返回非空对象,则返回创建出来的新对象

如下代码所示

// JavaScript
function Person(name,age,height,address) {
    this.name = name
    this.age = age
    this.height = height
    this.address = address
    this.eating = function() {
        console.log(this.name+"在吃饭");
    }
    this.running = function() {
        console.log(this.name+"在跑步");
    }
}
var q1 = new Person("张三",18,1.6,"北京市")
var q2 = new Person("李四",19,1.7,"上海市")
var q3 = new Person("王五",20,1.8,"广州市")

缺点:需要为每个对象的函数去创建一个函数对象实例

方案三补充:原型prototype

JavaScript中每个对象都有一个 [[prototype]] ,这个属性可以称之为对象的原型(隐式原型)

函数作为对象来说, 它也是有[[prototype]] 隐式原型

又因为它是一个函数, 所以它还会多出来一个显示原型属性: prototype

承接上文代码

// JavaScript
// 打印对象原型的方法
// 浏览器提供
console.log(obj.__proto__) // { ... }
// ES5之后的规范
console.log(Object.getPrototypeOf(obj)) // { ... }

当我们从一个对象中获取某一个属性时,它会触发[[get]]操作

  1. 在当前对象中去查找对应的属性,如果找到就直接使用
  2. 如果没有找到,那么会沿着当前对象的原型(链)去查找
// JavaScript
console.log(obj.hhh); // undefined
obj.__proto__.hhh = "哈哈哈"
console.log(obj.hhh); // 哈哈哈

函数作为对象来说, 它也是有[[prototype]] 隐式原型

又因为它是一个函数, 所以它还会多出来一个显示原型属性: prototype

// JavaScript
function Person() {}
// 函数原型
console.log(Person.prototype) // { ... }
var f1 = new Person()
var f2 = new Person()
console.log(f1.__proto__ === f2.__proto__) // true
console.log(f1.__proto__ === Person.prototype); // true

这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性

上方代码中,f1和f2的原型完全相同的原因是它们都指向同一个原型对象,即new操作后的构造函数Person所指向的原型对象prototype

prototype原型

从上图中,我们还可以看到

prototype.constructor = 构造函数本身

// JavaScript
console.log(Person.prototype.constructor); // ƒ Person() {}
console.log(Person.prototype.constructor === Person); // true

修改原型:

方法一:逐个更改

// JavaScript
Person.prototype.name = "coder"
Person.prototype.age = 18
console.log(f1.name,f1.age) // coder 18

方法二:修改整个对象

// JavaScript
Person.prototype = {
    name: "zzwi",
    age: "21",
    gender: "man",
    eating: function() {
        console.log(this.name+"在吃饭");
    },
    running: function() {
        console.log(this.name+"在跑步");
    }
}
/* 因为属性constructor的属性描述符enumerable值为false,
       即不可枚举,所以采用Object.defineProperty()进行添加*/
Object.defineProperty(Person,"constructor",{
    configurable: true,
    enumerable: false,
    writable: true,
    value: Person
})

参考

深入JavaScript高级语法-coderwhy

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 面向过程编程是一种计算机编程范式,其中程序员通过定义一系列的过程来实现计算机的功能。这些过程包含了输入、输出、计算和控制等功能。 面向对象编程是另一种计算机编程范式,其中程序员通过定义一系列的对象来实现计算机的功能。这些对象包含了数据和对应的操作,可以看作是封装了数据和操作的小规模程序。 面向过程编程和面向对象编程之间的区别在于,前者侧重于定义一系列的过程,后者侧重于定义一系列的对象。 在实际应用中,面向过程编程和面向对象编程并不是互相排斥的,它们可以相互补充,并且在某些情况下可以结合使用。 ### 回答2: 面向过程和面向对象是软件开发中的两种不同的编程思想和方法。 面向过程(Procedure Oriented Programming,POP)是以任务为中心的编程方式,强调问题的解决过程。它将程序划分为一个个的函数或过程,通过函数的调用和数据的传递来完成任务。面向过程的特点是流程性、结构化、注重过程和数据之间的处理关系。在面向过程的编程中,程序代码主要关注如何解决问题,使用函数来执行一系列的动作,数据是作为函数的输入和输出。 面向对象(Object-Oriented Programming,OOP)是以对象为中心的编程方式,强调问题的解决者。它将程序划分为一个个的对象,通过对象之间的交互和消息传递来完成任务。面向对象的特点是封装性、继承性、多态性,注重对象的行为和状态。在面向对象的编程中,程序代码主要关注对象及其行为和属性,通过定义类来创建对象,利用对象之间的关系来完成任务。 面向过程和面向对象都是一种编程思想和方法,它们并不是相互排斥的关系,而是可以互相借鉴和结合使用的。在实际的软件开发中,可以根据需求和问题的特点选择合适的编程方式。面向过程适用于简单的程序和对计算机的底层操作有较深入理解的场景,而面向对象适用于复杂的程序和对问题域有较深入理解的场景。 面向对象的编程思想在面向过程的程序中可以引入封装、继承和多态等概念,提高代码的可复用性和可维护性。而面向过程的编程思想可以在面向对象的程序中引入函数式的编程概念,提高代码的模块化和可测试性。 总之,面向过程和面向对象是两种不同的编程思想和方法,各有其适用的场景和优势。在实际的软件开发过程中,可以根据具体的需求和问题选择合适的方式来进行编程。 ### 回答3: 面向过程(Procedural Programming)与面向对象(Object-Oriented Programming)是两种不同的编程思想和方法。面向过程是一种以过程为中心的编程方式,通过一系列的步骤来实现特定的功能。面向对象则是以对象为中心的编程方式,将数据和操作封装在对象中,通过对象之间的交互来实现功能。 面向对象的编程思想是在面向过程的基础上发展而来的。它强调将问题抽象成对象,并通过对象之间的交互来解决问题。在面向对象的编程思想中,对象是数据与操作的封装体,具有属性(数据)和方法(操作)。通过创建对象、定义对象的属性和方法,以及对象之间的协作,实现程序的设计和开发。 面向过程和面向对象在编程思想和编程方式上有一定的区别。面向过程注重解决问题的步骤和流程,更关注具体的执行过程和指令的集合。而面向对象则更关注数据和操作的封装,以及对象之间的交互和协作。在面向对象中,对象是核心,而在面向过程中,流程和步骤是核心。 然而,面向过程和面向对象并不是完全对立的,而是可以相互结合和互补的。在实际的软件开发中,可以根据具体问题的复杂度和需求,选择合适的编程思想和方式。有时候,面向过程可以更加简洁和高效,而面向对象则可以更加灵活和可扩展。 总结来说,面向过程和面向对象是两种不同的编程思想和方式。面向过程注重步骤和流程,面向对象注重数据和操作的封装。它们可以相互结合和互补,根据具体问题需求选择合适的方式,实现程序的设计和开发。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值