JS基础--JS对象及其基本用法

302 阅读7分钟

什么是对象?如何声明?

对象的定义

  • 对象就是一堆无序数据的集合
  • 这些数据集合以键值对的形式一一对应
  • 这些键值对的键,我们称作 属性名(key) ;值,我们称作 属性值(value)
  • 在内存中,对象类型的数据被存储在Heap区中

对象的声明

// 1. 常用方式:简单声明,直接通过 {} 代码块进行声明一个变量
let obj = {'name' : 'frank' , 'age' : 18 }

// 2.正规方式,通过 new Object 构造函数进行声明
let obj = new Object({'name' : 'frank' , 'age' : 18 })

// 3.非主流写法
// 这里也声明了对象,但是并没有变量接收这个对象
console.log({'name' : 'frank' , 'age' : 18 }) 

对象声明的一些细节

  1. 对象中的键名几乎所有是 字符串 (Symbol也可以作属性名,非常少用)!!!但是可以不加引号,我们可以通过Object.keys()来验证这个结论;

  1. 对象中的键名加引号是,其可以包含任意字符,可以是空字符串,只有空格的字符串,emoji表情,中文,总之任意字符都可以;
  2. 对象中的键名不加引号时,其必须按照标识符来进行书写,但是额外允许数字作第一个字符。
  3. !!! 在声明对象的时候,属性名建议一定要 加引号

属性名一些刁钻点

  1. Object.keys(obj)可以获取obj对象的所有属性名。

  2. 所有属性名都会自动变成字符串,在不加引号的情况下,对于有些属性名,JS甚至会帮它计算后再变成字符串。

  1. 使用变量作为属性名
    • 属性名加了 [] 会被当做变量先进行求值,如果变量的值不是 字符串 则会自动把值变成字符串
    • 不加 [] 的属性名则会自动变成字符串
let p1  = 'name'
let obj = { p1 : 'JackM' } // 这种情况 'JackM' 的属性名为 "p1",即 { "p1" : "JackM" }
let obj = { [p1] : 'JackM'} // 这种情况 'JackM' 的属性名为 p1变量的值,即 { "name" : "JackM" }

对象的隐藏属性

let obj = {}
// obj是一个空对象,为什么下面这行代码不报错?因为obj原型中有 toString() 
obj.toString() 
  1. JS中每一个对象都有一个隐藏属性
  2. 这个隐藏属性储存这个对象的共有属性组成的对象的地址
  3. 这个共有属性组成的对象就叫做原型。
  4. 也就是说隐藏属性储存原型的地址。
  5. 因为原型其实是共有属性组成的对象,所以原型也是一个对象,所以原型也有它的原型。

Symbol作属性名

  • Symbol作属性名几乎不用,一般在“迭代”的时候才会使用到

对象属性的增删查改

删除属性

语法

  • delete obj.xxxdelete obj['xxx'] 即可删除 obj对象 中的 xxx 属性
  • 'xxx' in obj 可以查询obj中是否有一个叫 xxx 的属性,这个查询不仅会在自身对象中查找,还会沿着原型链查询。

区分删除属性和属性值为undefined

  • 删除属性是把 属性名+属性值 整个键值对从对象中删除,而不仅仅只是把属性值改为undefined。这两种方法最大的区别就是:删除属性后对象不再有该属性,而属性值改为undefined后对象仍拥有该属性,只是该属性没有赋值。
        let obj = {
            'aaa''aaa',
            'bbb''bbb'
        }
        delete obj.aaa
        obj.bbb = undefined
        console.log('aaa' in obj) // false
        console.log('bbb' in obj) // true
  • 所以判断一个属性是否在该对象中,不能使用 obj.xxx === undefined,要使用'xxx' in obj
  • JS关于删除属性一个小问题:JS中删除对象中一个原本就不存在的属性并不会报错。

查看对象属性

查看所有属性

  1. 查看所有属性的属性名(键)
    • Object.keys(obj)
  2. 查看所有属性的属性值(值)
    • Object.values(obj)
  3. 查看所有属性的属性名和属性值
    • Object.entries(obj)
  4. 查看自身属性+原型对象中的属性
    • console.dir(obj) 控制塔打印obj的目录详细信息
    • obj.\_\_proto\_\_ 查看obj的原型对象中的属性
  5. 判断一个属性是否是自身属性而不是原型中的属性
    • 用:obj.hasOwnProperty('xxx')
    • 不要使用 'xxx' in obj 这个方法会沿着原型链查找属性

查看属性

  1. 中括号语法

    • obj['xxx'] 注意:使用引号包裹属性名,直接使用属性名
    • obj[xxx] 使用变量,会先计算 xxx变量的值 再把这个值变成字符串 再查询属性
  2. 点语法

    • obj.xxx 注意:这个 xxx 没有引号,但是一个字符串
  3. 一道代码题分清楚上面3种情况

let list = ['name''age''gender']
        let person = {
            name'frank'age18gender'man'
        }
        for (let i = 0; i < list.length; i++) {
            let name = list[i]
            console.log(???) // 这里 ??? 替换成什么使person所有属性被打印出来
        }
        // 1.console.log(person.name) 2.console.log(person[name])
        // 答案:console.log(person[name])

增加&修改对象属性

  • 增加和修改对象属性方法一样。当对象已经存在某属性时,给该属性赋值则是修改属性;如果对象不存在某属性,给该属性赋值就是增加对象属性。

单个属性操作

let obj = { name'frank' } 
obj.name = 'Jack' 
obj['name'] = 'frank'
obj[name] = 'frank'  // 错误,因为不知道name变量并没有声明赋值
obj['na' + 'me'] = 'frank' // 正确,先计算 两个字符串的拼接,再进行增改操作
let key = 'name'; 
obj[key] = 'frank' // 正确,相当于 obj['name'] = 'frank'
let key = 'name'; 
obj.key = 'frank'  // 错误,但是可以在 obj 新增一个属性名为 key 的属性

批量赋值,ES6新特性

let obj = { 'name''Jack' }
Object.assign(obj, { 'age'18'gender''male' }) // 增加 'age' 、 'gender' 属性

属性增改与原型的关系

  1. 一般,增加和修改属性只能作用于自身对象,无法影响到原型
let obj1 = {}
let obj2 = {}
obj1.toString = 'xxx' // 首先查询toString属性,发现obj1自身上没有这个属性,新增 toString 并赋值 'xxx',并没有影响原型
console.log(obj1.toString) // 'xxx'。先在自身上找toString,发现存在属性,直接引用该属性
console.log(obj2.toString) // toString函数。先在自身上找toString,找不到,沿着原型链向原型中找,找到并引用
  1. 增改原型上的属性(错误) 虽然原型上的属性可以增改,但是非常不建议这么做,会引起很多问题

这种修改原型的方式,是修改原型上的属性,原型会直接受到影响,导致其他对象如果要通过原型链调用里面的属性,会出现报错。

let obj1 = {}
let obj2 = {'name' : 'jack'}
// 1.通过 __proto__ 修改原型上的属性,不推荐使用 __proto__进行任何操作
obj1.__protot__.toString = 'xxx'

// 2.通过obj1的上一级对象的 prototype 属性进行修改原型上的属性
Object.prototype.toString = 'xxx'

obj2.toString() // 报错,因为原型上的属性已经被修改
  1. 增改原型上的属性(正确) 如果要修改原型那么就在一开始创建对象的时候就修改

这种增改的方式其实就是再插入多一个原型!而不是单一修改原型的某个属性值

// 1.通过  __proto__  不推荐直接使用 __proto__
let obj = { name'frank' }
let common = { kind'human' }
obj.__proto__ = common // 此时:obj的__proto__指向的是 common ,而common的__proto__指向的是Object.prototype。
// obj不仅可以通过原型链调用 common 中的属性,还能调用 Object.prototype 中的属性,如toString。

// 2. 通过 Object.create
// Object.create()第二个参数 格式:{name:{value:'jack'}}   指的是创建对象的自身属性
// new Object 与  Object.create 是不一样的
let common = { kind'human' }
let obj = Object.create(common)
obj.name = 'frank'
let obj2 = Object.create(common, { 'name' : { value : 'jack' } } )