学而时习之 不亦说乎

现代JavaScript教程 对象笔记

发表于:2021-05-26分类:JavaScript笔记

对象

//创建对象
let user = new Object(); // “构造函数” 的语法
let user = {};  // “字面量” 的语法
let user = {     // 创建对象
  name: "John",  // 属性 键 "name",值 "John"
  age: 30,       // 属性 键 "age",值 30
  num: 4,
  "likes cat": true, //多词属性
};
user.isAdmin = true; //添加属性
delete user.num;//移除属性


//访问对象
alert( user.name ); // John
alert( user.age ); // 30
alert( user.isAdmin ); // true
alert( user["likes cat"] );//访问多词属性,删除也一样
let key = "name";
alert(user[key]);//当使用变量当名称访问时应该加[]


//计算属性
let fruit = "age";
let bag = {
  [fruit]: 5, // 属性名是从 fruit 变量中得到的
  [fruit + 'Num'] : 5 //组成ageNum
};
console.log(bag.age);//得到5
console.log(bag.ageNum);//得到5


//属性值简写
function makeUser(name, age) {
  return {
    name: name,
    age: age,
  };
}
let user = makeUser("John", 30);
console.log(user.name); // John
//可以简写成:
function makeUser(name, age) {
  return {
    name,
    age,
    num : 30  //可以混用
  };
}

//in操作符
//JavaScript中不存在的属性也不会报错,会返回undefined
let user = {};
console.log( user.noSuchProperty === undefined ); //true 没有这个属性
console.log("noSuchProperty" in user) //in用法 false 不存在(左边可以是个变量,检查与值同名的属性)
//以上2个方法区别类似于空值合并的情况,当属性存在但值时undefined是第一个方法无法知道。

for...in循环

//遍历对象中所有的键(当键是整数或“整数”的情况下,会被排序)
let user = {
  name: "John",
  age: 30,
  isAdmin: true,
  1 :1
};
for (let key in user) {
  console.log( key );  //键 name, age, isAdmin
  console.log( user[key] ); //值 John, 30, true
}

对象的引用和复制

let user1 = {name: "John"};
let user2 = user1;//并不是复制,而是引用
user2.name = 'leon';
console.log(user1.name);//leon
console.log(user2.name);//leon

let a = {};
let b = {}; // 两个独立的对象
alert( a == b ); // false 看起来是2个一样空的对象,但是实际情况是内存地址不同,是2个对象

克隆与合并

怎么才能复制一个对象呢?用for in

let user = {
  name: "John",
  age: 30
};
let clone = {}; // 新的空对象
// 将 user 中所有的属性拷贝到其中
for (let key in user) {
  clone[key] = user[key];//clone的user键=user键的值
}


//还可以使用Object.assign(浅拷贝)
let user = {
  name: "John",
  age: 30
};
let clone = {}; // 新的空对象
Object.assign(clone, user);
//其实就是把user合并到clone,可以合并多个对象,更多,user1,user2 这样
//注意同名属性会被后面的覆盖
let clone = Object.assign({}, user);//另一种写法

深度克隆

当对象里包含对象,克隆那个属性说包含的对象实际上就是==引用,所以需要用递归来实现。或者不自己造轮子,使用现成的实现,例如 JavaScript 库 lodash 中的 _.cloneDeep(obj)

垃圾回收

JavaScript是自动完成内存管理的。

可达性:可以被访问到或可用的值一定是存在内存中的,不可达就会被删除

let user = 1;//可达
user = null; //不可达了 删除

let user = {
  name: "John"
};
let admin = user;//引用对象
user = null; //user 不可达了,但是admin依旧可达,内存还在

对象方法

//对象属性中可以创建方法
let user = {
  name: "John",
  age: 30
};

user.sayHi = function() {
  alert("Hello!");
};

user.sayHi(); // Hello!
//更多写法
user = {
  sayHi: function() {
    alert("Hello");
  }
};
//简写
let user = {
  sayHi() { // 与 "sayHi: function()" 一样
    alert("Hello");
  }
};

this

对象中的方法需要访问对象中的其他属性时,就用this

let user = {
  name: "John",
  age: 30,
  sayHi() {
    // "this" 指的是“当前的对象”,访问name
    alert(this.name);
  }

};
user.sayHi(); // John

“this” 不受限制,不是对象也能用,所以可以先写在预先声明的函数中,这并没有语法错误(实际运行时,非严格模式下,不在对象中使用this,就是全局对象,严格模式会报错)

构造器 new

内部机制的步骤:1.新建空对象,并分配this。2.函数内部开始执行,为this增加属性。3.返回this

function User(name) {
  // this = {};(相当于隐式创建) 
  this.name = name;
  this.isAdmin = false;
  // return this;(相当于隐式返回)
}
//构造函数必须是大写开头
let user = new User("Jack");

alert(user.name); // Jack
alert(user.isAdmin); // false


//也可以这样写,user成了对象,但这种方法只能创建单个,毕竟是匿名函数
let user = new function() {
  this.name = "John";
  this.isAdmin = false;
};

构造器的return

构造器可以没有return的,构造器中如果返回的是一个对象,就返回这个对象,其他情况就返回自身

//返回对象的情况
function BigUser() {
  this.name = "John";
  return { name: "Godzilla" };  // <-- 返回这个对象
}
alert( new BigUser().name );  // Godzilla,得到了那个对象的name

//返回非对象的情况
function SmallUser() {
  this.name = "John";
  return; // <-- 返回 this
}
alert( new SmallUser().name );  // John

可选链 ?.

let user = {}; // 一个没有 "address" 属性的 user 对象
alert(user.address); // undefined! 没有这个属性所以未定义
alert(user.address.street); // Error! 没有定义这个属性的对象属性报错

//简单的2种方法,不优雅难理解
console.log(user.address ? user.address.street : undefined); //判断是否存在对象再去拿值,不存在就是未定义,存在但没有属性还是未定义
console.log( user.address && user.address.street && user.address.street.name ); //遇到未定义就返回了,不会触发报错

这时候就需要可选链?.(请注意:?. 语法使其前面的值成为可选值,但不会对其后面的起作用)

let user = null; // user都不存在
console.log( user?.address?.street ); // undefined(不报错)

//判断方法
let userGuest = {};
userGuest.admin?.(); // 啥都没有(没有这样的方法)

let user1 = {
  firstName: "John"
};
let user2 = null; // 为空
let key = "firstName";
alert( user1?.[key] ); // John 成功
alert( user2?.[key] ); // undefined

Symbol 类型

之前简单说过,这是唯一标识符,对象的属性键只能是字符串(整数键其实被转为字符串了)或者Symbol ,它不参与for...in,Object.keys(user)也不会有,但是Object.assign会复制

//使用symbol 增加隐藏属性
let user = { // 属于另一个代码
  name: "John"
};
let id = Symbol("id");
user[id] = 1;
user[id] = 1;
console.log(user[Symbol("id")]);//无法访问
console.log(user[id]);//可以访问

//对象字面量种使用
let id = Symbol("id");
let user = {
  name: "John",
  [id]: 123 // 而不是 "id":123
};

//全局注册表,可以通过id访问完全相同的属性
// 从全局注册表中读取
let id = Symbol.for("id"); // 如果该 Symbol 不存在,则创建它
// 再次读取(可能是在代码中的另一个位置)
let idAgain = Symbol.for("id");
// 相同的 Symbol
alert( id === idAgain ); // true

//更多用法不再深入

对象原始值转换

对象之间运算,对象的布尔值都是true,所以只存在字符串和数数值转换。
除了Date 对象之外,所有内建对象都以和 "number" 相同的方式实现 "default" 转换。我们也可以这样做。

参考:对象 — 原始值转换 (javascript.info)

评论已关闭