【JavaScript】【对象】Object

190 阅读10分钟

一、对象介绍

1.1 什么是对象

什么是对象无序属性的集合,其属性可以包含基本值、对象或者函数

对象是JavaScript的基本数据类型,是一种复合值,是带有属性和方法的特殊数据类型

复合值可以通过名字访问这些值,我们可以将对象看做事从字符串到值的映射,当然对象不仅仅是字符串到值的映射,除了自身的属性还可以从原型继承属性

JavaScript中,除了原始值,都是对象注意点:使用 new 关键字定义的 String、Number、Boolean 是对象

1.2 对象分类

  • 用 new 关键词定义的 布尔
  • 用 new 关键词定义的 数字
  • 用 new 关键词定义的 字符串
  • 日期永远都是对象
  • 算术永远都是对象
  • 正则表达式永远都是对象
  • 数组永远都是对象
  • 函数永远都是对象
  • 对象永远都是对象

1.3 原始值

原始值指的是没有属性或方法的值

原始数据类型指的是拥有原始值的数据;

原始数据类型:String、Number、Boolean、Null、Undefined

由于原始值是不可变的,因此无法为它们添加属性或方法,所以当我们尝试访问这些属性时,会得到 undefined 的结果

// 字符串
var str = 'hello';
console.log(typeof str); // 输出 "string"
str.prop = 'value';
console.log(str.prop); // 输出 "undefined"

// 数字
var num = 123;
console.log(typeof num); // 输出 "number"
num.prop = 'value';
console.log(num.prop); // 输出 "undefined"

// 布尔
var bool = true;
console.log(typeof bool); // 输出 "boolean"
bool.prop = 'value';
console.log(bool.prop); // 输出 "undefined"

1.4 对象的特点

  1. 对象由若干个属性和方法组成

    • 属性:是一种用于存储值的"容器"
    • 方法:是一种可以执行操作的函数
    • 访问和操作对象属性和方法:
      • 点运算符:使用 . 运算符访问对象的属性和方法
      • 方括号表示法:使用方括号 [] 来访问对象的属性和方法
      let maomi = {
          name: "福来",
          age: "5个月",
          say: function() {
              return "喵喵喵喵喵喵喵喵喵喵喵喵~"
          }
      }
      console.log(maomi.name) // 福来
      console.log(maomi['age']) // 5个月
      console.log(maomi.say()) // 喵喵喵喵喵喵喵喵喵喵喵喵~
      console.log(maomi['say']()) // 喵喵喵喵喵喵喵喵喵喵喵喵~
      
  2. 对象可以进行增删改查操作属性或者方法。并且属性值可以是任意JS数据类型

    • 添加属性/方法 的方法:
      • 直接修改
        let maomi = {
            name: "福来",
            age: "5个月",
            say: function() {
                return "喵喵喵喵喵喵喵喵喵喵喵喵~"
            }
        }
        maomi.age = "1岁啦"
        maomi.color = "乳白色"
        console.log(maomi.age) // 1岁啦
        console.log(maomi.color) // 乳白色
        
      • Object.defineProperty()。Object.defineProperty(obj, prop, desc) 一共有三个参数, obj为需要定义属性或者方法的对象;prop为需要定义的属性名或者方法名; desc为属性值(属性描述符)
  3. 查找对象的属性/方法时,如果当前对象没有此属性/方法,会通过对象的原型和原型链依次往上查找

二、对象属性

属性是对象相关的值。

访问对象属性的语法是:objectName.propertyName

示例:

// 这个例子使用了 String 对象的 length 属性来获得字符串的长度
var message="Hello World!";
var x=message.length; // 12

三、对象的方法

方法是能够在对象上执行的动作。

访问对象的方法的语法:objectName.methodName()

// 使用了 String 对象的 toUpperCase() 方法来将文本转换为大写:
var message="Hello world!";
var x=message.toUpperCase(); // HELLO WORLD!

四、创建对象

创建对象的方法:

  • 使用 Object 定义并创建对象的实例
  • 使用函数来定义对象,然后创建新的对象实例

4.1 使用new Object()

在JavaScript中,几乎所有的对象都是 Object类型的实例,它们都会从 Object.prototype 继承属性和方法。

Object 构造函数创建一个对象包装器

Object构造函数,会根据给定的参数创建对象,具体情况如下:

  • 如果给定值是 null 或 undefined,将会创建并返回一个空对象
  • 如果传进去的是一个基本类型的值,则会构造器包装类型的对象
  • 如果传进去的是引用类型的值,仍然会返回这个值,经它们赋值的变量保有和源对象相同的引用地址
  • 当以非构造函数形式被调用时,Object的行为等同于 new Object()

示例:

var person = new Object(); // person = {}
var person = new Object({ name: "zhangsan" }) // person = name: "zhangsan" }
var person = new Object(true); // 相当于new Boolean(true);

var arr = new Array(); // 创建一个空数组
var date = new Date(); // 创建当前时间的Date对象

通过new关键字来调用构造函数生产对象会经历四个步骤:

  1. 创建一个新对象。
  2. 将新创建的对象设置为构造函数中的this,因此构造函数中的this就指向了新创建的对象。
  3. 逐行执行构造函数中的代码。
  4. 返回新创建的对象。

4.2 使用对象字面量

语法格式:{ name1 : value1, name2 : value2,...nameN : valueN }

示例:

var person = {};
var person = {
    name: "zhangsan",
    age: 22,
};

4.3 使用工厂模式

function createPerson(name,age,job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        alert(this.name);
    };

    return o;
}
var person1 = createPerson('Nike',29,'teacher');
var person2 = createPerson('Arvin',20,'student');

4.4 使用对象构造函数

JavaScript中,this通常指向的是我们正在执行的函数本身,或者是该函数所属的对象(运行时)

function person(firstname,lastname,age,eyecolor) {
    this.firstname=firstname;
    this.lastname=lastname;
    this.age=age;
    this.eyecolor=eyecolor;
}
var myFather=new person("John","Doe",50,"blue");
var myMother=new person("Sally","Rally",48,"green");

4.5 原型创建对象模式

使用原型创建对象的方式,可以让所有对象实例共享它所包含的属性和方法。

function Person(){}
Person.prototype.name = 'Nike';
Person.prototype.age = 20;
Person.prototype.jbo = 'teacher';
Person.prototype.sayName = function(){ alert(this.name);};

var person1 = new Person();
person1.sayName();

每添加一个属性和方法都要敲一遍 Person.prototype。为减少不必要的输入,从视觉上更好封装原型的功能。示例如下:

// 将Person.prototype设置为等于一个以对象字面量形式创建新对象,最终结果相同,但有一个例外:constructor属性不在指向Person了。
function Person(){}
Person.prototype={
    name:"NIke",
    age:11,
    sayName:function() { alert(this.name)},
};

4.6 组合使用构造函数模式和原型模式

实例属性都是在构造函数中定义的,而所有实例共享的属性 constructor 和方法 则是在原型中定义。这种模式是 ECMAScript 中使用最广泛,任何度最高的一种创建自定义类型的方法。

function Person(name,age,job) {
    this.name =name;
    this.age = age;
    this.job = job;
}
Person.prototype = {
    constructor:Person,
    sayName: function() {
        alert(this.name);
    };
}
var person1 = new Person('Nike',20,'teacher');

拓展 - 构造函数相对比工厂模式创建对象的区别:

  • 没有显示地创建对象
  • 直接将属性和方法赋给this对象
  • 没有return语句
  • 可以识别的对象的类型

4.7 Object.create()

Object.create()静态方法以现有对象作为原型,创建一个新对象

  • 语法
Object.create(proto)
Object.create(proto, propertiesObject)
  • 传参:

    • proto:新创建对象的原型对象
    • propertiesObject:如果该参数被指定且不为 undefined,则该传入对象可枚举的自有属性将为新创建的对象添加具有对应属性名称的属性描述符。这些属性对应于 Object.defineProperties() 的第二个参数。
  • Object.create() 与 {} 的区别:

    image.png

    • Object.create()是让我们可以创建一个非常干净,并且可高度定制的对象
    • {} 是会继承原型链上的所有方法的 -示例:
    Object.create(null, { name: { value: "张三" } })
    

    image.png

    Object.create(Object.prototype, {
      // foo 是一个常规数据属性
      foo: {
        writable: true,
        configurable: true,
        value: "hello",
      },
      // bar 是一个访问器属性
      bar: {
        configurable: false,
        get() {
          return 10;
        },
        set(value) {
          console.log("Setting `o.bar` to", value);
        },
      },
    });
    

    image.png

  • 拓展

    let obj = {};
    // 约等于
    let obj = Object.create(Object.prototype)
    

    image.png

    使用 Object.create(),我们可以创建一个原型为 null 的对象。在字面量初始化对象语法中,相当于使用 __proto__ 键。

    o = Object.create(null);
    // 等价于:
    o = { __proto__: null };
    

    image.png

五、对象的使用

5.1 检索

  • 访问常量:
    • Object.name
    • Object[name]
  • 访问函数:
    • Object.methodName()

避免异常的写法:

  • object && object.name
  • ES6:object?.name

5.2 引用

对象通过引用来传递

var obj = { name: "zhangsan" };

var b = obj.name;

5.3 原型

对象中的属性/方法查找,对象中没有时,则会原型中查找

5.4 方法

  • 浅拷贝 - Object.assign()

第一个参数为接受拷贝的对象,第二参数为传递拷贝的对象

用于创建一个新对象,需要传入一个参数,作为新建对象的原型对象

const target = { a: 1, b: 2 }
const source = { b: 3, c: 4 }

const result = Object.assign(target, source)

console.log(target)  //  { a: 1, b: 3, c: 4 }
console.log(result)  // { a: 1, b: 3, c: 4 }
console.log(target === result) // true
  • Object.is() - 用于判断两个值是否相等
Object.is('foo', 'foo') // true
Object.is([], []) // false

Object.is(null, null) // true
Object.is(NaN, NaN) // true

// 特别的:
Object.is(0, -0)            // false
Object.is(+0, -0)           // false
Object.is(0, +0)            // true
Object.is(-0, -0)           // true
Object.is(NaN, 0/0)         // true
  • 键值对

    • Object.entries():返回键值对数组
    let obj = {
        name: 'ruovan',
        age: 24
    }
    Object.entries(obj) // [['name', 'ruovan'], ['age', 24]]
    
    • Object。fromEntries():将键值对数组转换为对象
    let arr = [['name', 'ruovan'], ['age', 24]]
    Object.entries(arr) // { name: 'ruovan', age: 24 }
    
  • 定义对象 给对象定义新属性、修改现有属性

    • Object.defineProperties()
    • Object.defineProperty()
    var obj = {}
    
    // obj 表示要操作的对象, "name"表示要操作的对象属性
    Object.defineProperty(obj, "name", {
        enumerable: false, // 是否可枚举,设置为 false,则 name 不能通过 for...in 遍历到
        configurable: false, // 是否可被删除,设置为false,则 name 无法通过 delete运算符删除
        writable: false, // 是否可写,设置为 false ,则属性不能被重新赋值或修改
        value: "dary", // 被设置的属性值
        // 获取值
        get: function () {
            return v
        },
        // 赋值
        set: function (value) {
            v = value
        }
    })
    
    // defineProperties 可一次性处理多个属性
    Object.defineProperties(obj, {
        'name': {
            value: 'dary',
            writable: true
        },
        'age': {
            value: 19,
            writable: false
        }
    });
    
    • Object.proxy(): 用于修改某些操作的默认行为

    它在目标对象之前架设了一层拦截,外界对该对象的访问,都必须先通过这层拦截。因此它提供了一种机制,可以对外界的访问进行过滤和改写

    proxy: 代理,这里表示用它来代理某些操作。是一个构造函数,接受两个参数:目标对象target和句柄对象handler。handler内的方法是对 target 对象的方法进行拦截处理的方法

    // Proxy(target, handler)
    new Proxy(person, {
        get: function (target, key) {
            return target[key]
        },
        set: function (target, key, value) {
            target[key] = value
            console.log('target[key] is setted')
        }
    })
    
  • 获取属性

    • Object.getOwnPropertyDescription():用于获取对象上的一个自有属性的属性描述。包括:value、writable、get、set、configurable、enumerable
    const obj = {
        number: 42
    }
    
    const des = Object.getOwnPropertyDescriptor(obj, 'number')
    
    // 可以访问其属性描述符
    console.log(des.configurable) // true
    console.log(des.value) // 42
    
    • Object.getOwnPropertyDesciptors():用于获取对象的所有自身属性的描述符
    Object.getOwnPropertyDescriptors(obj)
    // num: {
    //   value: 42,
    //   writable: true,
    //   configurable: true,
    //   enumerable: true,
    // }
    
    • Objec.getOwnPropertyNames():用于获取对象自身拥有的 可枚举属性 和 不可枚举属性 的属性名
    var arr = ["a", "b", "c"]
    console.log(Object.getOwnPropertyNames(arr).sort()) // ["0", "1", "2", "length"]
    
    // 类数组对象
    var obj = { 0: "a", 1: "b", 2: "c"}
    console.log(Object.getOwnPropertyNames(obj).sort()) // ["0", "1", "2"]
    
    • Object.prototype.hasOwnProperty():用于判断对象 自身属性 是否含有指定的属性,不包括从原型链上继承的属性
    const obj = {};
    
    obj.number = 24
    
    console.log(obj.hasOwnProperty('number')) // true
    // 不包括从原型链上继承的属性
    console.log(obj.hasOwnProperty('toString')) // false
    console.log(obj.hasOwnProperty('hasOwnProperty')) //false
    
  • 原型

    • Object.getPrototypeOf():用于返回指定对象的原型,如果没有则返回null
    let obj = {}
    
    Object.getPrototypeOf(obj)
    // 作用和 __proto__ 效果一样
    Object.getPrototypeOf(obj) === obj.__proto__
    
    // 特别的,Object 也是构造函数, 因此,返回的是函数的原型对象
    Object.getPrototypeOf(Object) === Function.prototype === Object.__proto__
    
    • Object.setPrototypeOf():用于设置指定对象的新原型
    Object.setPrototypeOf(obj, prototype)
    
    • Object.prototype.isPrototypeOf():用于检测一个对象是否存在于另一个对象的原型链上
    // 用于检查 prototypeObj 是否在 object的原型链上
    prototypeObj.isPrototypeOf(object)
    let obj1 = {}
    let obj2 = new Object()
    
    console.log(obj1.__proto__.isPrototypeOf(obj2)) // true
    
  • 枚举

    • Object.keys():用于获取给定对象的自身可枚举属性的属性名(键)。

    Object.getOwnPropertyNames()类似

    返回一个由属性名所组成的数组

    • Object.values(obj):用于获取给定对象的自身可枚举属性的属性值(值)

    返回一个由属性值所组成的数组。与 Object.keys() 相反

  • 字符串

    • Object.toString():每个对象都有这个方法,用于返回一个表示该对象的字符串
    let obj = { a: 1, b: 2 }
    let arr = [1,2,3]
    let str = '123'
    
    obj.toString() // [object Objetc]
    arr.toString() // 1,2,3
    str.toString() // 123
    
    • Object.toLocaleString():使用toLocaleString可以将对象根据语言环境来转换字符串
    // 数字,可以进行千位符格式化
    let num = 123456789
    num.toLocaleString() // '123,456,789'
    // 日期
    let date = new Date()
    
    date.toString() // '...' (省略了)
    date.toLocaleString() // '2021/8/12 下午11:00:00'
    

5.5 运算符

  • delete运算符:删除对象指定属性

    不会触及原型链中的任何对象

    let obj = { name: 'zhangsan' }
    delete obj.name
    

参考