JavaScript|对象创建与属性控制

面向对象概念

对象是JavaScript中一个非常重要的概念,这是因为对象可以将多个相关联的数据封装到一起,更好的描述一个事物:
比如我们可以描述一辆车:Car,具有颜色(color)、速度(speed)、品牌(brand)、价格(price),行驶(travel)等等;
比如我们可以描述一个人:Person,具有姓名(name)、年龄(age)、身高(height),吃东西(eat)、跑步(run)等等;

62dfa9c9e7a25

用对象来描述事物,更有利于我们将现实的事物,抽离成代码中某个数据结构:所以有一些编程语言就是纯面向对象的编程语言,比Java;你在实现任何现实抽象时都需要先创建一个类,根据类再去创建对象;

JavaScript的面向对象

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

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

创建JS对象的方法

单个对象创建的方法

// 方法一:
var obj = {
    name: 'foo',
    age: 18,
};

// 方法二:
var obj = new Object();
obj.name = 'foo';
obj.age = 18;

多个对象创建的方法

工厂模式

可以通过上述方式创建,但如果需要创建相同属性的多个对象,可以使用工厂模式;
工厂模式其实是一种常见的设计模式,通常我们会有一个工厂方法,通过该工厂方法我们可以产生想要的对象;

// 工厂模式:
function createPerson(name, age) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    return obj;
}

var person1 = createPerson('foo', 18);
var person2 = createPerson('bar', 20);

构造函数

构造函数也称之为构造器(constructor),通常是我们在创建对象时会调用的函数;在其他面向的编程语言里面,构造函数是存在于类中的一个方法,称之为构造方法;但是JavaScript中的构造函数有点不太一样;

function foo() {
  console.log("foo~, 函数体代码")
}

// foo就是一个普通的函数
foo() // foo~, 函数体代码

// 换一种方式来调用foo函数: 通过new关键字去调用一个函数, 那么这个函数就是一个构造函数了
var f1 = new foo // foo~, 函数体代码
console.log(f1) // [Function: foo]

如果一个函数被使用new操作符调用了,那么它会执行如下操作:

  1. 在内存中创建一个新的对象(空对象);
  2. 这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性;;
  3. 构造函数内部的this,会指向创建出来的新对象;
  4. 执行函数的内部代码(函数体代码);
  5. 如果构造函数没有返回非空对象,则返回创建出来的新对象;
// 规范: 构造函数的首字母一般是大写
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 p1 = new Person("张三", 18, 1.88, "广州市") // 创建一个新的对象
var p2 = new Person("李四", 20, 1.98, "北京市")

console.log(p1) // Person {name: "张三", age: 18, height: 1.88, address: "广州市"}
console.log(p2)
p1.eating() // 张三在吃东西~
p2.eating()

操作JS对象的方法

普通的属性操作方式:

// 设置属性:
obj.name = 'bar';
// 获取属性:
console.log(obj.name);
// 删除属性:
delete obj.name;

数据属性描述符

属性操作的控制:通过属性描述符可以精准的添加或修改对象的属性,属性描述符需要使用 Object.defineProperty 来对属性进行添加或者修改:

Object.defineProperty(obj, 'name', {
    value: 'bar',
    writable: true, // 可写
    enumerable: true, // 可枚举, 可以通过for in循环获取到, 如果设置为false, 则不能通过for in循环获取到, 也不能console.log(obj)获取到
    configurable: true , // 可配置,可以删除属性
});

构造函数缺点:每次创建一个新的对象,都需要重新创建一个函数对象实例,会占用大量的内存;

存取属性描述符

存取属性描述符:1.隐藏某一个私有属性被希望直接被外界使用和赋值 2.如果我们希望截获某一个属性它访问和设置值的过程时, 也会使用存储属性描述符

obj = {
    _name: 'foo',
}

function foo(value){
    console.log('get value', value);
}

function bar(value){
    console.log('set value', value);
}

Object.defineProperty(obj, 'name', {
    enumerable: true,
    configurable: true,
    // value: 'foo',  //使用存取属性描述符的话,只能使用getter和setter来控制属性的访问和赋值
    // writable: true, // 同上
    get: function() {
        foo(this._name);
        return this._name;
    },
    set: function(value) {
        bar(value);
        this._name = value;
    }
});

同时定义多个属性描述符

Object.defineProperties() 方法直接在一个对象上定义多个新的属性或修改现有属性,并且返回该对象。

Object.defineProperties(obj, {
    name: {
        value: 'foo',
        writable: true,
        enumerable: true,
        configurable: true,
    },
    age: {
        value: 18,
        writable: true,
        enumerable: true,
        configurable: true,
    }
});

获取某一个属性的属性描述符

获取对象的属性描述符:getOwnPropertyDescriptor, getOwnPropertyDescriptors

var obj = {
  // 私有属性(js里面是没有严格意义的私有属性)
  _age: 18,
  _eating: function() {}
}

Object.defineProperties(obj, {
  name: {
    configurable: true,
    enumerable: true,
    writable: true,
    value: "foo"
  },
  age: {
    configurable: true,
    enumerable: true,
    get: function() {
      return this._age
    },
    set: function(value) {
      this._age = value
    }
  }
})

// 获取某一个特性属性的属性描述符
console.log(Object.getOwnPropertyDescriptor(obj, "name"))
console.log(Object.getOwnPropertyDescriptor(obj, "age"))

// 获取对象的所有属性描述符
console.log(Object.getOwnPropertyDescriptors(obj))

Object的方法对对象限制

禁止对象扩展新属性:preventExtensions
给一个对象添加新的属性会失败(在严格模式下会报错);

密封对象,不允许配置和删除属性:seal
实际是调用preventExtensions p并且将现有属性的configurable:false

冻结对象,不允许修改现有属性: freeze
实际上是调用seal p并且将现有属性的writable: false

    Object.freeze() // 冻结对象,不能再修改属性值,不能再删除属性,不能再添加属性,不能再修改属性的可枚举性,不能再修改属性的可配置性,不能再修改属性的可写性
    Object.seal() // 将对象封闭,不能再修改属性值,不能再删除属性,不能再添加属性,可以修改属性的可枚举性,可以修改属性的可配置性,可以修改属性的可写性
    Object.preventExtensions() // 阻止对象增加新的属性,不能再修改属性值,不能再删除属性,可以修改属性的可枚举性,可以修改属性的可配置性,可以修改属性的可写性
    Object.isFrozen() // 判断对象是否被冻结