【JavaScript】JS 对象

该随笔是一篇关于 JS 对象学习笔记,学习网站是w3school

有些地方感觉不够具体,所以添加了些细节,辅助笔者进一步理解。

创建 JS 对象

对象文字方法

最简答的方法(推荐)

通过一对花括号包含的名称:值对集合创建对象。

var person = {
    name: "Xiao Ming",
    gender: "male",
    age: 20,
    greet: funcion(){
        retrun "Hello!"
    }
};

new 关键词创建

var person = new Object();
person.name = "Xiao Ming";
person.gender = "male";
person.age = 20;
person.greet = () => "Hello";

通过类创建对象

class Person {
    constructor(name, gender, age){
        this.name = name;
        this,gender = gender;
        this.age = age;
    }
    greet() {
        return "Hello";
    }
}
const person = new Person("Xiao Ming", "male", 20);

Object.create()

在 ECMAScript 5 中,也可以通过函数 Object.create() 来创建对象。

Object.create(proto, [propertiesObject])

JS 对象属性

JavaScript 对象可以说是由属性组成的,因为方法能看作是包含函数定义的属性。

属性可以被修改、添加、删除,但是某些属性是只读的。

访问属性

访问属性的途径有:

objectName.property    //person.name
//或
objectName["property"] //person["name"]
//或
objectName[expression]  //exp = "name"; person[exp]  表达式必须计算为属性名   

for...in 遍历对象属性:

const person = {name: "Xiao Ming", gender: "male", age: 20}
let str = "";
for(let prop in person) {
   str += person[prop] + " ";
}

添加新属性

通过简单的赋值,向已存在的对象添加新属性:

var person = {name: "Xiao Ming", gender: "male"}
person.age = 20;

注:避免使用 JavaScript 预留词命名属性

删除属性

通过 delete关键词删除对象属性

var person = {name: "Xiao Ming", gender: "male", age: 20}
delete person.age; //person.age 变为 undefined

delete 关键词会同时删除属性值和属性本身。

不应该删除预定义的 JavaScript 对象属性,否则程序会崩溃。

特性

所有属性都有名称。此外它们还有值。

值是属性的特性之一。

其他特性包括:可列举、可配置、可写。

这些特性定义了属性被访问的方式(是可读的还是可写的?)

在 JavaScript 中,所有属性都是可读的,但是只有值是可修改的(只有当属性为可写时)。

(ECMAScript 5 拥有获取和设置所有属性特性的方法)

原型属性

JavaScript 对象继承了它们的原型的属性。

delete 关键词不会删除被继承的属性,但是如果您删除了某个原型属性,则将影响到所有从原型继承的对象。

JS 对象方法

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

JavaScript 方法是包含函数定义的属性。

创建对象方法

var objectName = {
    methodName : function() { 
      //code
    }
};

访问对象方法

objectName.methodName()

把 methodName() 描述为 objectName 对象的方法,把 methodName 描述为属性。

methodName 属性在被通过 () 调用后会以函数形式执行。

var person = {
    greet : function() {
        return "Hello";
    }
};
person.greet(); //访问方法 返回 Hello
person.greet; //返回函数定义 function() { return "Hello"; }

添加新的方法

通过简单的赋值即可

person.eat = (food) => return "eat" + food;

JS 对象显示

显示 JavaScript 对象将输出 [object Object]

显示 JavaScript 对象的一些常见解决方案是:

  • 按名称显示对象属性(objectName.prop
  • 循环显示对象属性(for...in
  • 使用 Object.values() 显示对象
  • 使用 JSON.stringify() 显示对象

其中前两个方案前面章节已经介绍过了。

使用 Object.values()

通过使用 Object.values(),任何 JavaScript 对象都可以被转换为数组:

const person = {name:"Xiao Ming", gender:"male", age:20};
const myArray = Object.values(person); //["Xiao Ming", "male", 20]

使用 JSON.stringify()

任何 JavaScript 对象都可以使用 JavaScript 函数 JSON.stringify() 进行字符串化(转换为字符串):

const person = {name:"Xiao Ming", gender:"male", age:20};
//结果将是一个遵循 JSON 标记法的字符串:
let myString = JSON.stringify(person); //{"name":"Xiao Ming","gender":"male","age":20}

注意:JSON.stringify 不会对方法进行字符串化。

对象访问器

ECMAScript 5 (2009) 引入了 Getter 和 Setter,允许您使用类似于获取或设置属性的语法来定义对象方法。

Getter 和 Setter 允许您定义对象访问器(被计算的属性)。

  • getter 是一种获得属性值的方法。当我们去读取属性的值时,调用getter。
  • setter是一种设置属性值的方法。当我们去设定或修改属性值时,调用setter。

get && set

本例使用 lang 属性来获取 language 属性的值,本例使用 lang 属性来设置 language 属性的值。

// 创建对象:
var person = {
  language : "en",
  get lang() {
    return this.language.toUpperCase();
  },
  set lang(lang) {
    this.language = lang;
  }
};
// 使用 setter 来设置对象属性:
person.lang = "en";
// 使用 getter 来显示来自对象的数据:
document.getElementById("demo").innerHTML = person.lang;

getter/setter 方法的名称不能与属性名称相同。

许多程序员在属性名称前使用下划线字符 _ 将 getter/setter 与实际属性分开:

var person = {
  language : "en",
  get language() {
    return this._language.toUpperCase();
  },
  set language(lang) {
    this._language = lang;
  }
};

为什么使用 Getter 和 Setter?

  • 它提供了更简洁的语法
  • 它允许属性和方法的语法相同
  • 它可以确保更好的数据质量
  • 有利于后台工作

Object.defineProperty() 方法也可用于添加 Getter 和 Setter:

// 定义对象
var person = {language : ""};

// 创建一个 setter 和 getter 来确保语言的大写更新:
Object.defineProperty(person, "language", {
  get : function() { return language },
  set : function(value) { language = value.toUpperCase()}
});

// 操作计数器:
person.language = "en";
person.language; // 返回 EN

JS 对象构造器

在前面的例子中我们都是只创建了一个对象,但是有时我们需要创建很多相同“类型”的对象,这时候就需要一个对这些相同类别的东西进行抽象,形成一份“蓝图”,然后我们根据这份蓝图批量创建多个相同类型的对象。

创建“对象类型”的方法是使用对象构造函数,通过 new 关键词调用构造器函数可以创建相同类型的对象:

//用大写首字母对构造器函数命名是个好习惯
function Person(name, age, lang) {
    this.name = name;
    this.age = age;
    this.language = lang;
}
var myFriend = new Person("Bob", 22, "en");
var myBrother = new Person("Jack", 26, "cn");

:在构造器函数中,this 是没有值的。它是新对象的替代物。 当一个新对象被创建时,this 的值会成为这个新对象。请注意 this 并不是变量。它是关键词。您无法改变 this 的值。


为对象添加属性和方法不会影响到其他对象!

myBrother.nationality = "China";
myFriend.greet = () => "Hello";

myFriend.nationality; // 返回 undefined
myBrother.greet(); // 控制台报错,myBrother.greet is not a function

无法为对象构造器添加新属性和方法:

Person.nationality = "English";

myBrother.nationality; // 返回 undefined;

如需向构造器添加一个新属性和方法,必须在构造器函数内部向一个对象添加:

function Person(name, age, lang) {
    this.name = name;
    this.age = age;
    this.language = lang;
    this.nationality = "China"; //这样对象属性就可以拥有默认值。
    this.changeAge = (age) => this.age = age;
}

myBrother.nationality; // 返回 China
myBrother.changeAge(28);
myBrother.age; // 返回 28

JavaScript 提供用于原始对象的构造器:

var x1 = new Object();    // 一个新的 Object 对象
var x2 = new String();    // 一个新的 String 对象
var x3 = new Number();    // 一个新的 Number 对象
var x4 = new Boolean();   // 一个新的 Boolean 对象
var x5 = new Array();     // 一个新的 Array 对象
var x6 = new RegExp();    // 一个新的 RegExp 对象
var x7 = new Function();  // 一个新的 Function 对象
var x8 = new Date();      // 一个新的 Date 对象

Math() 对象不再此列。Math 是全局对象。new 关键词不可用于 Math。

正如以上所见,JavaScript 提供原始数据类型字符串、数字和布尔的对象版本。但是并无理由创建复杂的对象。原始值快得多!

var x1 = {};            // 新对象
var x2 = "";            // 新的原始字符串
var x3 = 0;             // 新的原始数值
var x4 = false;         // 新的原始逻辑值
var x5 = [];            // 新的数组对象
var x6 = /()/           // 新的正则表达式对象
var x7 = function(){};  // 新的函数对象

JS 对象原型

所有 JavaScript 对象都从原型继承属性和方法。

日期对象继承自 Date.prototype。数组对象继承自 Array.prototypePerson对象继承自 Person.prototype

Object.prototype 位于原型继承链的顶端。

日期对象、数组对象和 Person 对象的继承链最上方是 Object.prototype

原型继承

要了解原型链,首先要了解 JS 对象。

JS 对象大致可以分为两类:普通对象和函数对象。

一般而言,通过 new Function 产生的对象是函数对象,其他都是普通对象。

所有的对象,都有一个__proto__属性,指向构造该对象的构造函数的原型,从而保证了实例能访问构造函数原型中定义的属性和方法(注意是 proto 前后是双下划线)。

JavaScript 规定每个函数都有一个 prototype 属性,这个属性是一个指针,指向原型对象(包含所有实例共享的属性和方法)。

而原型对象也有个 constructor属性,能指回原构造函数。

先来看下普通对象的原型链:

let parent = {a: 1};
let child = {
    b: 2,
    __proto__: parent
};

image-20220216145938401

通过对象构造器构造出来的对象的原型链:

function F(a) {
    this.a = a;
};
F.prototype.b = 2;
let obj = new F(1); 

image-20220216153106027

通过原型链的延续我们可以实现 Js 中的继承,原型链的顶端是 Object.prototype.__proto__,而 Object.prototype 是一切对象的原型,所以这个对象上所有的属性都能被我们使用,它不能有任何对象为原型,所以递归访问__proto__的终点Object.prototype.__proto__等于null。

prototype 属性

JavaScript prototype 属性允许您为对象构造器添加新属性和方法:

function Person(name, age, lang) {
    this.name = name;
    this.age = age;
    this.language = lang;
    this.nationality = "China"; //这样对象属性就可以拥有默认值。
    this.changeAge = (age) => this.age = age;
}
Person.prototype.nationality = "China";
Person.prototype.chageAge = (age) => this.age = age;

myBrother.nationality; // 返回 China
myBrother.changeAge(28);
myBrother.age; // 返回 28

prototype 可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

作为一个对象,当你访问其中的一个属性或方法的时候,如果这个对象中没有这个 方法或属性,那么 Javascript 引擎将会访问这个对象的__proto__ 属性所指向的对象,并在那个对象中查找指定的方法或属性,如果不能找到,那就会继续通过那个对象 的__proto__属性指向的对象进行向上查找,直到这个链表结束。

注:请只修改您自己的原型。绝不要修改标准 JavaScript 对象的原型。

JS ES5 对象方法

ECMAScript 5 (2009) 向 JavaScript 添加了许多新的对象方法。

管理对象:

// 以现有对象为原型创建对象
Object.create()

/*
定义对象属性和/或更改属性的值和/或元数据。
*/
Object.defineProperty(object, property, descriptor)

// 添加或更改对象属性
Object.defineProperties(object, descriptors)

// 访问属性
Object.getOwnPropertyDescriptor(object, property)

// 以数组返回所有属性
Object.getOwnPropertyNames(object)

// 访问原型
Object.getPrototypeOf(object)

// 以数组返回可枚举属性
Object.keys(object)

保护对象:

// 防止向对象添加属性
Object.preventExtensions(object)

// 如果属性可以添加到对象,则返回 true
Object.isExtensible(object)

// 防止更改对象属性(不是值)
Object.seal(object)

// 如果对象被密封,则返回 true
Object.isSealed(object)

// 防止对对象进行任何更改
Object.freeze(object)

// 如果对象被冻结,则返回 true
Object.isFrozen(object)

Object.defineProperty()

Object.defineProperty(object, property, descriptor) 能够定义一个对象的新属性,或者修改一个对象已有的属性值,返回这个被处理过的对象。

object:要操作的对象

property:object 上要定义或修改的属性

descriptor:属性描述器,用于定义或修改该属性的特性。

该属性描述器分为两类:数据描述符和存取描述符。

数据描述符:一个具有值的属性,该值可能是可写的,也可能不是可写的。

存取描述符:由getter-setter函数对描述的属性。

描述符必须是这两种形式之一;不能同时是两者,否则报错。

更改元数据:

ES5 允许更改以下属性元数据:

writable : true|false      // 属性值是否可更改
enumerable : true|false    // 属性是否可枚举
configurable : true|false  // 属性是否可重新配置
  • 仅为数据描述符:value、writable

value:属性对应的值,可以是任意类型的值,默认为undefined

let person = Object.create(null);
Object.defineProperty(person, "name", {value:"Jack"});

writable:属性的值是否能够被重新赋值,true为可以重新赋值,false为不可以重新赋值,默认为false
注意:writable为false,但是实际去重新赋值了,不报错,值也不改变

Object.defineProperty(person, "name", {writable:true});
person.name = "Bob";
person.name; //返回 Bob
Object.defineProperty(person, "name", {writable:false});
person.name = "Jack";
person.name; //返回 Bob
  • 仅为存储描述器:set/get

当设置或获取对象的某个属性的值的时候,可以提供getter/setter方法。

Object.defineProperty(person, "name", {
  get : function() { return this.name },
  set : function(name) { this.name = name}
});
  • 既为数据描述符,又为存储描述符:enumerable、configurable

enumerable:此属性是否可以被枚举(使用 for…in 或 Object.keys())。设置为true可以被枚举;设置为false,不能被枚举。默认为false。

const person = {name:"Jack", gender:"male", age:20, language:""};

// 更改属性:
Object.defineProperty(person, "language", {
  value: "EN",
  writable : true,
  enumerable : false,
  configurable : true
});
Object.keys(person); // 返回 name,gender,age

configurable是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)。设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false。

let person = {};
Object.defineProperty(person, "name", {
    value: "Jack",
    configurable: false
});
delete person.name; //不会删除该属性,并且不报错
person.name; //返回 Jack

Object.defineProperty(person, "name", {configurable: true});
delete person.name;
person.name; //返回 undefined
let person = {};
Object.defineProperty(person, "name", {
    value: "Jack",
    configurable: false
});
//重新修改特性
Object.defineProperty(person, "name", {
    value: "Jack",
    writable: true,
    enumerable: true,
    configurable: true
}); //报错 Uncaught TypeError: Cannot redefine property: name

Object.defineProperties()

Object.defineProperties(object, descriptors)Object.defineProperty 相似,只不过该方法可以一次性操作多个属性

let obj = {};
Object.defineProperties(obj, {
    "prop1": {
        value: "",
        writable: true,
        enumerable: true
    },
    "prop2": {
        value: "",
        writable: true
    }
    //etc.
});

Object.getOwnPropertyDescriptor(object, property)

该方法返回指定对象上属性的描述符

const person = {name:"Jack", gender:"male", age:20, language:""};

console.log(Object.getOwnPropertyDescriptor(person, "name")); 
//控制台打印 {value: 'Jack', writable: true, enumerable: true, configurable: true}

Object.getOwnPropertyNames(object)

Object.getOwnPropertyNames(object) 返回对象所有自己的属性名

const person = {name:"Jack", gender:"male", age:20, language:""};
document.write(Object.getOwnPropertyNames(person)); //返回 name,gender,age,language

Object.keys(object)

Object.getOwnPropertyNames(object) 类似,只不过返回对象所有自己的可枚举属性名

const person = {name:"Jack", gender:"male", age:20, language:""};

// 更改属性:
Object.defineProperty(person, "language", {
  value: "EN",
  writable : true,
  enumerable : false,
  configurable : true
});
Object.keys(person); // 返回 name,gender,age

Object.getPrototypeOf(object)

const person = {name:"Jack", gender:"male", age:20, language:""};
const student = Object.create(person);
console.log(Object.getPrototypeOf(student)); 
//控制台打印 person 对象

Object.preventExtensions(object)

该方法让一个对象变的不可扩展,即永远不能再添加新的属性。并且返回原对象。

var obj = {};
var obj2 = Object.preventExtensions(obj);
obj === obj2; //true
Object.isExtensible(obj);
obj.name = "Bob";
console.log(obj.name); //打印 undefined

Object.seal(object)

Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。

Object.seal()做了哪些事情?

  • 设置Object.preventExtension(),禁止添加新属性(绝对存在)
  • 设置configurable为false,禁止配置(绝对存在)
  • 禁止更改访问器属性(getter和setter)

Object.freeze(object)

Object.freeze()方法可以冻结一个对象。

一个被冻结的对象再也不能被修改。

冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。

此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

Object.freeze()做了哪些事情?

  • 设置Object.preventExtension(),禁止添加新属性(绝对存在)
  • 设置writable为false,禁止修改(绝对存在)
  • 设置configurable为false,禁止配置(绝对存在)
  • 禁止更改访问器属性(getter和setter)

Object.freeze()只是浅冻结

使用Object.freeze()冻结的对象中的现有属性是不可变的。用Object.seal()密封的对象可以改变其现有属性。

JS ECMAScript 2017 对象方法

Object.entries() 方法返回对象中键/值对的数组:

const person = {name:"Jack", gender:"male", age:20};
Object.entries(person);  //返回 name,Xiao Ming,gender,male,age,20

//Object.entries() 使循环中使用对象变简单了:
const fruits = {Bananas:300, Oranges:200, Apples:500};
let text = "";
for (let [fruit, value] of Object.entries(fruits)) {
    text += fruit + ": " + value + " ";
}

//Object.entries() 也使得将对象转换为映射变得简单:
const myMap = new Map(Object.entries(fruits));

Object.values 类似 Object.entries,但返回对象值的单维数组:

const person = {name:"Xiao Ming", gender:"male", age:20};
Object.values(person); //Xiao Ming,male,20

JavaScript 对象 Rest 属性

ECMAScript 2018 添加了 Rest 属性。

允许我们破坏一个对象并将剩余物收集到一个新对象上:

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }

JS 对象是易变的

先来看下例子:

var person = {
    name: "Xiao Ming",
    gender: "male",
    age: 20
}
var x = person;
x.age = 1;   //x.age 和 person.age 均为 1

这是因为对象是通过引用来寻址的。

person 并不是把值都拷贝一份给 x,而是把引用地址拷贝给 x,所以x 和 person 指向了同一个对象。

x 和 person 就像一个人的大名和小名一样,不管哪个名,其实都是本人。

JS Map对象

Map 对象存有键值对,其中的键可以是任何数据类型。

Map 对象记得键的原始插入顺序。

Map 对象具有表示映射大小的属性。

属性

Property Description
size 返回Map的大小

基本方法

Method Description
new Map() 创建新的 Map 对象。
set() 为 Map 对象中的键设置值。
get() 获取 Map 对象中键的值。
entries() 返回一个新的包含 [key, value]的 Iterator对象,按插入顺序
keys() 返回 Map 对象中键的 Iterator对象
values() 返回 Map 对象中值的 Iterator对象

其他方法

Method Description
clear() 删除 Map 中的所有元素。
delete() 删除由键指定的元素。
has() 如果键存在,则返回 true。
forEach() 为每个键/值对调用回调。
// 创建对象
const apples = {name: 'Apples'};
const bananas = {name: 'Bananas'};
const oranges = {name: 'Oranges'};

// 创建新的 Map
const fruits = new Map();
// Add new Elements to the Map
fruits.set(apples, 500);
fruits.set(bananas, 300);
fruits.set(oranges, 200);
/*
或将 Array 传递给 new Map() 构造函数:
const fruits = new Map([
  [apples, 500],
  [bananas, 300],
  [oranges, 200]
]);
*/

//获取 map 的大小
fruits.size; // 返回 3

//map.get(key)
fruits.get(apples); // 返回 500

//map.entries()
let fruitsIterator = fruits.entries();
console.log(fruitsIterator.next().value); //[apples, 500]
console.log(fruitsIterator.next().value); //[bananas, 300]
console.log(fruitsIterator.next().value); //[oranges, 200]

//keys()
fruitsIterator = fruits.keys();
console.log(fruitsIterator.next().value); //apples
console.log(fruitsIterator.next().value); //bananas
console.log(fruitsIterator.next().value); //oranges

//map.values()
fruitsIterator = fruits.values();
console.log(fruitsIterator.next().value); //500
console.log(fruitsIterator.next().value); //300
console.log(fruitsIterator.next().value); //200

//map.forEach()
let txt = ""; 
fruits.forEach((num,fruit) => (txt += fruit.name + ":" + num + " "));
document.write(txt); //Apples:500 Bananas:300 Oranges:200

//map.delete(key)
console.log(fruits.has(apples)); //true
fruits.delete(apples);
console.log(fruits.has(apples)); //false

//map.clear()
fruits.clear();
fruits.size;// 返回 0

JavaScript 对象 vs Map

JavaScript 对象和 Map 之间的差异:

对象 Map
Size 对象没有 size 属性 Maps 有 size 属性
键类型 对象键必须是字符串(或符号) Map 键可以是任何数据类型
键顺序 对象键没有很好地排序 Map 键按插入排序
默认 对象有默认键 Map 没有默认键

JS Set对象

Set 是唯一值的集合。

每个值在 Set 中只能出现一次。

一个 Set 可以容纳任何数据类型的任何值。

属性

Property Description
size 返回元素计数

方法

Method Description
new Set() 创建新的 Set 对象。
add() 向 Set 添加新元素。
clear() 从 Set 中删除所有元素。
delete() 删除由其值指定的元素。
entries() 返回新的 Iterator 对象,包含 [value, value] 给定的每个元素的数组Set,按照插入顺序。
has() 如果值存在则返回 true。
forEach() 为每个元素调用回调。
keys() 返回新的 Iterator 对象,包含 Set 的元素
values() 与 keys() 相同。
// 创建 Set
const letters = new Set();

// Add the values to the Set
letters.add("a");
letters.add("b");
letters.add("c");
//或 const letters = new Set(["a","b","c"]);

//如果添加相等的元素,则只会保存第一个元素:
letters.add("a");
letters.size; //3

//map.forEach
letters.forEach(
    function(value) {
        txt += value + " ";
    }
);
document.write(txt); //a b c

//map.entries()
let setIter = letters.entries();
console.log(setIter.next().value); // ["a", "a"]
console.log(setIter.next().value); // ["b", "b"]
console.log(setIter.next().value); // ["c", "c"]

//map.keys() 和 map.values() 结果相同
setIter = letters.values();
console.log(setIter.next().value); // "a"
console.log(setIter.next().value); // "b"
console.log(setIter.next().value); // "c"


//map.has(value) 
letters.has("a"); //true
//map.delete(value)
letters.delete("a"); 
letters.has("a"); //false

//map.clear()
letters.clear();
letters.size; //0
posted @   hzyuan  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)

喜欢请打赏

扫描二维码打赏

支付宝打赏

点击右上角即可分享
微信分享提示