# Object

# 基本概念

对象是包括属性与方法的数据类型。

面向过程编程

let name = "Js小白";
let grade = [
  { lesson: "js", score: 99 },
  { lesson: "mysql", score: 85 },
  { lesson: "css", score: 80 },
  { lesson: "Html", score: 96 }
];
function average(grade, name) {
  const total = grade.reduce((t, a) => t + a.score, 0);
  return name + ":" + total / grade.length + "分";
}
console.log(average(grade, name));

面向对象编程

let user = {
    name : "Js小白",
    grade : [
      { lesson: "js", score: 99 },
      { lesson: "mysql", score: 85 },
      { lesson: "css", score: 80 },
      { lesson: "Html", score: 96 }
    ],
    average(grade, name) {
      const total = this.grade.reduce((t, a) => t + a.score, 0);
      return this.name + ":" + total / this.grade.length + "分";
    }
}
console.log(user.average());

# OOP面向对象

  • 对象是属性和方法的集合即封装
  • 将复杂功能隐藏在内部,只开放给外部少量方法,更改- 对象内部的复杂逻辑不会对外部调用造成影响即抽象
  • 继承是通过代码复用减少冗余代码
  • 根据不同形态的对象产生不同结果即多态

# 对象的组成

对象是属性和方法的无序集合,由键名属性值组成

【键名/属性名】

对象的所有键名都是字符串,所以加不加引号都可以,如果不是字符串也会自动转换成字符串

【注意】

  • 1、如果键名不符合标识符命名规则,则必须加上引号,否则会报错
//错误示例
/*var o = {
    1p: 123
}*/  

//正确示例
var o = {
    '1p': 123
}
  • 2、属性的 key 是一个唯一的字符串

    【后面与前面重名的会将前面的覆盖】

  • 3、访问一个不存在的属性不会抛出错误,但是会返回 undefined。

  • 4、当使用括号表示法,属性的 key 不要求是有效的标识符 —— 可以是任意值。

【属性值】

  • 1、属性值可以是任何类型的表达式,最终表达式的结果就是属性值的结果
var o ={
    a: 1+2
}
console.log(o.a);//3
  • 2、如果属性值为函数,则通常把这个属性称为"方法"
var o = {
  p: function (x) {
    return 2 * x;
  }
};![image](https://note.youdao.com/favicon.ico)
o.p(1);  //2
  • 3、由于对象的方法就是函数,因此也有name属性。方法的name属性返回紧跟在function关键字后面的函数名。如果是匿名函数,ES5环境会返回undefined,ES6环境会返回方法名
var obj = {
  m1: function f() {},
  m2: function () {}
};
obj.m1.name // "f"
obj.m2.name //ES5: undefined
obj.m2.name //ES6: "m2"

var obj1 = {
    name:"zhangsan",
    age:30,
    show(){
    console.log('我是对象中的方法')
    } 
}
obj1.show(); //我是对象中的方法

# 引用特性

对象和函数、数组一样是引用类型,即复制只会复制引用地址。

  • 1、如果不同的变量名指向同一个对象,那么他们都是这个对象的引用,也就是说他们指向同一个内存地址,修改其中一个变量,会影响到其他所有变量。
var o1 = {};
var o2 = o1;
o1.a = 1;  //向o1对象添加a属性,值为1
console.log(o2.a);// 1
o2.b = 2;  //o2对象实际上是对o1的引用,改变o2的属性实际上也是改变o1的属性
console.log(o1.b);// 2

// 对多的比较是对内存地址的比较所以使用 == 或 === 一样

let obj1 = {};
let obj2 = obj1;
let obj3 = {};
console.log(obj1 == obj2);   //true
console.log(obj1 === obj2);  //true
console.log(obj1 === obj3);  //false
  • 2、如果取消某一个变量对于原对象的引用,不会影响到另一个变量
var o1 = {};
var o2 = o1;
o1 = 1;
console.log(o2);//{}
  • 对象做为函数参数使用时也不会产生完全赋值,内外共用一个对象
let user = { age: 22 };
function addTen(user) {
  user.age += 10;
}
addTen(user);
console.log(user.age); //32

# 创建对象

# 1、对象字面量

JavaScript提供了叫做字面量的快捷方式,用于创建大多数原生对象值,使用字面量只是隐藏了与使用new操作符相同的基本过程,于是也叫做语法糖。

var obj = {}; //等价于 var obj = new Object();
var person = {
    name: 'Bob',
    age: 20,
    tags: ['js', 'web', 'mobile'],
};

[👣注意]

  • 使用对象字面量的方式创建的对象,属性名会自动转换成字符串。
  • 一般对象字面量最后一个属性后的逗号将忽略,但是在IE7-浏览器中导致错误

# 2、使用new构造函数

使用new操作符后跟Object构造函数用以初始化一个新创建的对象

var person  = new Object();
// 如果不给构造函数传递参数可以不加括号 
// var person = new Object;
    person.name = 'Tom';
    person.age = '20';
    person.job = 'software Enginner';
    person.sayName = function(){
        console.log(this.name)
    }   
console.log(person)

创建无属性的空对象

var obj1 = new Object();
var obj2 = new Object(undefined);
var obj3 = new Object(null);
var obj4 = new Object;

如果括号里面的参数是一个对象,则之间返回这个对象

var o1 = {a:1};
var o2 = new Object(o1);
console.log(o2)   // {a: 1}
console.log(o1 === o2);// true

如果括号里面的参数是一个函数对象,则之间返回这个函数对象

var f1 = function(){};
var f2 = new Object(f1);
console.log(f1 === f2);// true

# 3、Object.create()

ES5定义了一个名为Object.create()的方法,它创建了一个新对象,第一个参数就是这个对象的原型,第二个可选参数用以对对象的属性进行进一步的描述。

# 4、使用对象构造器

// 使用函数来构造对象
function person(firstname,lastname,age,eyecolor) 
{
    this.firstname=firstname; 
    this.lastname=lastname; 
    this.age=age; 
    this.eyecolor=eyecolor; 
}

# 操作属性

  • 使用点语法获取
let user = {
    name: "jc-sir"
};
console.log(user.name);
  • 使用[] 获取
console.log(user["name"]);
  • 如果属性名不是合法变量名就必须使用扩号的形式了
let user = {};
user["my-age"] = 28;
console.log(user["my-age"]);
  • 对象和方法的属性可以动态的添加或删除。
const user = {
  name: "李思晓"
};
user.age = "10";
user.show = function() {
  return `${this.name}已经${this.age}岁了`;
};
console.log(user.show());
console.log(user);

delete user.show;
delete user.age;

console.log(user);
console.log(user.age); //undefined

# this

this 指当前对象的引用,始终建议在代码内部使用this 而不要使用对象名,不同对象的this只指向当前对象。

下例是不使用 this 时发生的错误场景

  • 删除了user变量,但在函数体内还在使用user变量造成错误
  • 使用 this 后始终指向到引用地址,就不会有这个问题
let user = {
  name: "小左",
  show() {
    return user.name;
  }
};
let user1 = user;
user = null;
console.log(user1.show()); //Error

改用this 后一切正常

let user = {
  name: "小左",
  show() {
    return this.name;
  }
};
let user1 = user;
user = null;
console.log(user1.show()); //Error

# 对象属性管理

# 添加属性

let obj = {};
obj.name = "ObjName";
obj.creater = "Jc-sir"

# 删除属性

let obj = {name:"张三"};
delete obj.name;
cosole.log(obj.name) // undefined
// 如果在对象中未找到属性则返回undefined

# 检测属性

# in运算符

左侧是属性名(字符串),右侧是对象。如果对象的自有属性或者继承属性中包含这个属性则返回true

var obj = { name: 'Tom',age:18};
"name" in obj;      // true  name是obj的自有属性
"sex" in obj;       // flase sex不是obj的属性
// 以下是两个继承属性
"toString" in obj;       // true  
"hasOwnProperty" in obj; // true 

# !==

使用!==判断属性是否为undefined 。

var obj = { x:1 };
obj.x !== undefined   // true  obj 拥有x属性
obj.y ! == undefined   // false obj 没有y属性
obj.toString ! == undefined   // true obj继承了该属性

DANGER

对于值为undefined的属性 不能通过 !== 判断。

var obj = { x:undefined }          // 属性被显示赋值 undefined
obj.x !== undefined                // false  属性存在但是值为undefined
obj.y !== undefined                // false  此时是因为属性不存在

// 使用 in
"x" in obj    // true
"y" in onj    // false

# hasOwnProperty()

检测对象自身是否包含指定的属性(自有属性 非继承属性),不检测原型链上继承的属性

let obj = { name: 'jc-sir'};
console.log(obj.hasOwnProperty('name'));     //true
console.log(obj.hasOwnProperty('sex'));      //false
console.log(obj.hasOwnProperty('toString')); //false  原型上的属性将返回false
// 写法优劣对比
// bad
console.log(object.hasOwnProperty(key));

// good
console.log(Object.prototype.hasOwnProperty.call(object, key));

// best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
console.log(has.call(object, key));
/* or */
import has from 'has'; // https://www.npmjs.com/package/has
console.log(has(object, key));
/* or */
console.log(Object.hasOwn(object, key)); // https://www.npmjs.com/package/object.hasown

# propertyIsEmumerable()

WARNING

propertyIsEmumerable() 是 hasOwnProperty()的增强版,只有检测到 自有属性 且该属性的可枚举性为true 时才返回 true 。某些内置属性是不可枚举的。

var o = {x:1}
var obj = Object.create(o);    // 不是 assign
obj.z = 2;
obj.propertyIsEnumerable("x")  // false
obj.propertyIsEnumerable("z")  // true

可枚举性决定了这个属性能否被for…in查找遍历到。

# 枚举属性

对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的。可枚举性决定了这个属性能否被for…in查找遍历到。

属性的枚举性会影响以下四个函数的结果:

  • for…in
  • Object.keys()
  • Object.values()
  • JSON.stringify

代码示例

var obj = new Object();
obj.x = 1;
obj.y = 2;
obj.z = 3;

Object.defineProperty(obj,"z",{
    enumerable:false
})

for(let i in obj){
    console.log(obj[i]);
}
// 1,2   不会打印3 因为z属性是不可枚举的

console.log(Object.keys(obj))       // ["x", "y"]

console.log(Object.values(obj))     // [1, 2]

console.log(JSON.stringify(obj));   // {"x":1,"y":2}

# assign

可以使用 Object.assign 静态方法 从一个或多个对象复制属性

"use strict";
let obj = { a: 1, b: 2 };
obj = Object.assign(obj, { f: 1 }, { m: 9 });
console.log(obj); //{a: 1, b: 2, f: 1, m: 9}

# 判断为空

# for-in

let isEmpty = (obj) => {
  for(let i in obj){
    return false
  }
  return true
}
console.log(isEmpty({}))// true
console.log(isEmpty({num:1}))// false

# JSON.stringify方法

let isEmpty = (obj)=>{
    return JSON.stringify(obj) === '{}'
}

# Object.keys方法

let isEmpty = (obj) => {
  return !Object.keys(obj).length
}

# 对象实例方法

# valueOf()

该方法返回当前对象

var obj = {
  str: "jc-sir",
  num: 1, 
};
obj.valueOf();   //{str: "jc-sir", num: 1}

# toString()

该方法返回当前对象的字符串形式

var o1 = new Object();
o1.toString() // "[object Object]"

var o2 = {a:1};
o2.toString() // "[object Object]"

# toLocaleString()

toLocaleString()方法并不做任何本地化自身的操作,它仅调用toString()方法并返回对应值

var o = {a:1};
o.toLocaleString() // "[object Object]"

# 对象的解构赋值

# 基本用法

var user = {
    name:"xiaozuo",
    sex:'男'
}
let {name:n,sex:s} = user;
console.log(n,s);
// xiaozuo 男

//如果属性名与变量相同可以省略属性定义
let {name,sex} = user;

函数返回值直接解构到变量

function userFun() {
  return {
    name: '小左',
    age: 18
  };
}
let {name : n, age : a} = userFun();
console.log(n); // 小左

函数传参

"use strict";
function people({ name, age }) {
  console.log(name, age); // 张三   18
}
people({ name: "张三", age: 18 });

# 严格模式

非严格模式可以不使用声明指令,严格模式下必须使用声明。所以建议使用 let 等声明。

// "use strict";
({name,sex} = {name:'张三',sex:'女'});
console.log(name, sex);

 "use strict";
let {name,sex} = {name:'张三',sex:'女'};
console.log(name, sex);

# 嵌套解构

可以操作多层复杂数据结构

const people = {
  name:'',
  skills:{
    title:'JS'
  }
}
const {name,skills:{title}}  = people;
console.log(name,title); 

# 默认值

为变量设置默认值

let [name, site = 'jc-sir.github.io'] = ['juncai'];
console.log(site); // jc-sir.github.io

使用默认值特性可以方便的对参数预设

function createElement(options) {
  let {
    width = '200px',
    height = '100px',
    backgroundColor = 'red'
  } = options;
  
  const h2 = document.createElement('h2');
  h2.style.width = width;
  h2.style.height = height;
  h2.style.backgroundColor = backgroundColor;
  document.body.appendChild(h2);
}
createElement({
	backgroundColor: 'green'
});

# 属性特征

JS中可以对属性的访问特性进行控制。

# 查看特征

使用 Object.getOwnPropertyDescriptor查看对象属性的描述。

"use strict";
const user = {
  name: "jc-sir",
  age: 18
};
let desc = Object.getOwnPropertyDescriptor(user, "name");
console.log(JSON.stringify(desc, null, 2));
// {
//   "value": "jc-sir",
//   "writable": true,
//   "enumerable": true,
//   "configurable": true
}

使用Object.getOwnPropertyDescriptors查看对象所有属性的描述

"use strict";
const user = {
  name: "jc-sir",
  age: 18
};
let desc = Object.getOwnPropertyDescriptors(user);
console.log(JSON.stringify(desc, null, 2));

数据属性包括以下四种特性

特性 说明 默认值
configurable 能否使用delete、能否需改属性特性、或能否修改访问器属性 true
enumerable 对象属性是否可通过for-in循环,或Object.keys() 读取 true
writable 对象属性是否可修改 true
value 对象属性的默认值 undefined

存取器属性包括以下属性

特性 说明 默认值
configurable 能否使用delete、能否需改属性特性、或能否修改访问器属性 true
enumerable 对象属性是否可通过for-in循环,或Object.keys() 读取 true
get 🤡 获取存取器属性 undefined
set 🤠 定义存取器属性 undefined

代码示例

var obj = {
    name:"jc-sir",
    num:18,
    get age(){
        return ++this.num;
    }
}
Object.getOwnPropertyDescriptor(obj,"age");
// {
//   configurable: true
//   enumerable: true
//   get: ƒ age()
//   set: undefined
// }

WARNING

Object.getOwnPropertyDescriptor() 只能得到自有属性的描述符 要想获得继承属性的特性,需要遍历原型链 Object.getPrototypeOf()

# 设置特征

使用Object.defineProperty 方法修改属性特性,通过下面的设置属性name将不能被遍历、删除、修改。

"use strict";
const user = {
  name: "jc"
};
Object.defineProperty(user, "name", {
  value: "jc-sir",
  writable: false,
  enumerable: false,
  configurable: false
});

通过执行以下代码对上面配置进行测试,| 分别打开注释进行测试

// 不允许修改
// user.name = "Tom"; //Error

// 不能遍历
// console.log(Object.keys(user));

//不允许删除
// delete user.name;
// console.log(user);

//不允许配置
// Object.defineProperty(user, "name", {
//   value: "小左先生",
//   writable: true,
//   enumerable: false,
//   configurable: false
// });

# 设置/修改多个属性

如果要同时创建或修改多个属性 使用 Object.defineProperties() 第一个参数是要修改的对象,第二个参数是一个映射表

代码示例

var o = {name:"o"}
var p = Object.defineProperties(o,{
  x:{value:3,writeable:true,enumerable:true,configurable:true},
  y:{value:4,writeable:true,enumerable:true,configurable:true},
  z:{
    get:function(){return Math.sqrt(this.x*this.x+this.y*2)},
    enumerable:true,
    configurable:true
  }
})
// {
//   name: "o"
//   x: 3
//   y: 4
//   z: 4.123105625617661
// }

# 禁止添加属性

Object.preventExtensions禁止向对象添加属性

"use strict";
const user = {
  name: "jc-sir"
};
Object.preventExtensions(user);
user.age = 18; //Error  在严格模式下报错

Object.isExtensible 判断是否能向对象中添加属性

"use strict";
const user = {
  name: "jc-sir"
};
Object.preventExtensions(user);
console.log(Object.isExtensible(user)); //false

# 封闭对象

Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为 configurable: false

"use strict";
const user = {
  name: "jc",
  age: 18
};

Object.seal(user);
console.log(
  JSON.stringify(Object.getOwnPropertyDescriptors(user), null, 2)
);

Object.seal(user);
console.log(Object.isSealed(user));
delete user.name; //Error

Object.isSealed 如果对象是密封的则返回 true,属性都具有 configurable: false

"use strict";
const user = {
  name: "jc-sir"
};
Object.seal(user);
console.log(Object.isSealed(user)); //true

# 冻结对象

Object.freeze 冻结对象后不允许添加、删除、修改属性,writable、configurable都标记为false

"use strict";
const lesson = {
  name: "html"
};
Object.freeze(lesson);
lesson.name = "javascript"; //Error

**Object.isFrozen()**方法判断一个对象是否被冻结

"use strict";
const lesson = {
  name: "html"
};
Object.freeze(lesson);
console.log(Object.isFrozen(lesson)); // true

# 属性访问器

getter方法用于获得属性值,setter方法用于设置属性,这是JS提供的存取器特性即使用函数来管理属性。

  • 用于避免错误的赋值
  • 需要动态监测值的改变
  • 属性只能在访问器(存取器属性)和普通属性(数据属性)任选其一,不能共同存在

# getter/setter

// 为obj创建一个伪属性latest,它会返回log数组的最后一个元素。
"use strict";
var obj = {
    data: {
        name: 'jc-sir',
        age: null
    },
    log: ['example', 'test'],
    get latest() {
        if (this.log.length == 0) return undefined;
        return this.log[this.log.length - 1];
    },
    set age(value) {
        if (typeof value != "number" || value > 100 || value < 10) {
            throw new Error("年龄格式错误");
        }
        this.data.age = value;
    },
    get age() {
        return `年龄是: ${this.data.age}`;
    }
}
console.log(obj.latest);      // "test"
// 如果一个属性同时拥有getter和setter方法,那么这个属性是可读可写的如 age 
// 如果只要getter方法,那么该属性只是可读属性,如latest
// 如果资源setter方法,那么该属性只是可写属性。读取只写属性总是反回undefined
obj.latest = "修改后的latest" // 并不会被修改 没有setter方法
obj.age = 101 //Error

# 删除getter

使用delete操作符删除 getter

delete obj.latest;

# 定义getter

1、创建对象时定义

2、在原有对象添加

要随时将 getter 添加到现有对象,使用 Object.defineProperty()

var o = { a:0 }

Object.defineProperty(o, "b", { get: function () { return this.a + 1; } });

console.log(o.b)

# 定义 setter

  1. 在对象初始化时定义 setter
var language = {
  set current(name) {
    this.log.push(name);
  },
  log: []
}

language.current = 'EN';
console.log(language.log); // ['EN']

language.current = 'FA';
console.log(language.log); // ['EN', 'FA']

// current属性是未定义的,访问它时将会返回 undefined。
  1. 使用 defineProperty 为当前对象定义 setter
var o = { a:0 };

Object.defineProperty(o, "b", { set: function (x) { this.a = x / 2; } });

o.b = 10; 
console.log(o.a) // 5

# 删除setter

用 delete 操作符移除一个 setter

delete language.current;
Last Updated: 3/6/2023, 10:52:48 AM