学习笔记(四):JavaScipt对象

对象

  • 文字语法:

    const obj = {};
    
  • 构造类型:

    const obj = new Object();
    
  • 能使用文字形式就不要使用构造形式。

  • typeof null 是"object"类型:不同对象的底层都表示为二进制,在js中二进制的前三位都为0的话会被判断为object,null的二进制表示是全0,所以typeof会返回“object”。

  • js六种主要类型:

    • string
    • number
    • boolean
    • null
    • undefined
    • object
  • 基本类型(string、number、boolean、null、undefined)本身并不是对象。

  • 对象拥有很多特殊的子类型。

对象的子类型
  • 函数(可调用的对象);
  • 数组;
  • 内置对象(或者成为内置函数);
  • 原始值var a = 'my a’只是一个字面量,并不是对象,但我们能访问它的长度和使用相关的方法。原因在于语言会自动帮我们将字面量转换成一个String对象。
  • nullundefined只有字面量形式没有构造形式;Date只有构造形式,没有字面量形式;
内容(储存位置)
  • 对象的内容或者说属性是一些储存在特定命名位置的值组成的;
  • 在引擎内部这些值一般并不会存在对象容器内部。储存在对象容器内部的是这些属性的名称,它们就像指针,指向这些值真正的储存位置。
  • 属性访问:.name;键访问:[‘name’];区别在于属性访问是固定的名称,键访问可以是动态的;
  • 在对象中属性名永远是一个字符串,如果使用字符串以外的其他值作为属性名,那它会被转换成字符串;
    const obj = {};
    obj[true] = 'a';
    obj[1] = 'b';
    obj[obj] = 'c';
    
    console.log(obj['true']); // a
    console.log(obj['1']);// b
    console.log(obj['[object Object]']); // c
    
  • 可计算属性名:通过键访问实现,例如obj[prefix + ‘name’]
属性与方法
  • 对象中的函数调用实际上也是属性访问,只是这个属性是对不同函数的引用;
  • 一个函数永远不会“属于”一个对象;
  • 数组的特殊之处:
    • 通过下标(索引)访问;
    • 数组也是对象,可以给数组添加属性,但是数组的长度不会发生变化;
    • 如果试图向数组添加一个属性,但是这个属性名看起来像一个数字,那么它会修改数组的长度:
      const arr = [1, 'sss', 'ddd']
      arr['2'] = 'hhh'
      console.log(arr); // [1, 'sss', 'hhh']
      
复制对象
  • 浅复制:Object.assign(),或者ES6中的展开语法;
  • 深复制:不同js框架有不同的实现方式。另外一种巧妙的实现方式是通过JSON.parse(JSON.stringify(obj))来实现,但这种方式要保证json是安全的,只适用于部分情况。
属性描述符
  • 对象默认拥有四种基础属性描述符:

    • value: 值;
    • writable:是否可写;
    • configurable:是否可配置;
    • enumerate:是否可枚举。
  • 如果configurable为true,则可通过Object.defineProperty可以修改属性描述符的值。注意:如果修改的是configurable,则无法再次修改回来,属于单向操作。例外的是configurable为false时可以修改writable,但同样是单向操作。同时也不支持删除属性。

不变性
  • 对象常量:configurablewritable设置为false;
  • 禁止扩展:Object.preventExtensions(),严格模式下新增属性时会报错;
  • 密封:Object.seal(),不能修改对象的配置,也不能新增属性,但可以修改现有的属性值,这个方法实际上是调用了Object.preventExtensions()并把现有属性标记为configurable: false
  • 冻结:code>Object.freeze(),创建一个冻结对象。实际上是在现有对象上调用Object.seal()并修改writable: false。这个方法实现的是最高级别的不可变性,但要注意的是,对象内部引用的其他对象是不受影响的。如果确定冻结全部层级对象,那么可以使用递归。
[[Get]]
  • 在访问对象的一个属性时,如obj.a时,实际上是调用了一个[[Get]]函数,这个函数首先会在当前对象上查找是否具有同名的属性,如果没找到,就会沿着原型链向上查找,直到找到或未找到返回undefined为止。
[[Put]]
  • 如果对象上已经存在要操作的属性,那么[[Put]]会执行一下几种操作:
    • 属性是否是访问描述符(执行getter操作)?如果是且存在setter就执行setter;
    • writable为false: 非严格模式静默失败,严格模式抛出异常;
    • 都不是,立即设置属性值。
  • 如果不存在属性:
    • 待补充
getter和setter
  • 访问描述符:getter;
  • 数据描述符:setter;
  • 通过文字语法或者Object.defineProperty()显示创建的对象,在访问属性时会默认调用一个get函数,他的返回值会被当作属性的值返回。
  • 一般getter和setter是成对出现的,否则会出现下面例子中,无法修改属性值的情况:
    const obj = {
      get a() {
        return 'my a'
      }
    };
    obj.a = 'update a'
    console.log(obj.a); // my a
    
  • setter会覆盖单个属性默认的[[Put]](赋值)操作
    const obj = {
      get a() {
        return this._a
      },
      set a(val) {
        this._a = 'my a ' + val
      }
    };
    obj.a = 'update a'
    console.log(obj.a); // my a update a
    
存在性
  • 判断对象的属性是否存在:
    • in操作符会检查属性是否存在与对象及其原型链,方式一:
      const obj = { a: 1 };
      console.log('a' in obj); // true
      console.log('b' in obj); // false
      
    • 方式二:通过obj.hasOwnProperty()obj.hasOwnProperty不会检查其原型链。
    • 方式二有可能会失败,因为它实际上通过prototype访问的如果对象上没有prototype(例如Object.create(null)创建的对象),那么可以以一种显示绑定的方式,如:Object.prototype.hasOwnProperty.call(obj, ‘属性名称’)。
  • 可枚举意味着对象的属性是否能够出现在对象属性的遍历中。
  • Object.getOwnPropertyNames()会返回一个数组,包含所有属性,无论是否可枚举,Object.keys()会返回一个数组,只包含可枚举的属性。
遍历
  • 通过for in,但这种方式遍历是无序的,每一次遍历的顺序都可能不同;
  • 通过数组的方式遍历。
  • 为了解决for in无序的问题,可以通过for of
  • for of可以遍历带有@@iterator对象并且调用它的next()。实际上是请求调用了对象的迭代器对象(或者自定义的)执行next()函数来遍历所有返回值,注意,普通对象没有内置的@@iterator,下面是《你不知道的JavaScript 上》中自定义对象迭代器的的代码:
    const myObj = { a: 2, b: 3 };
    
    Object.defineProperty(myObj, Symbol.iterator, {
      enumerable: false,
      writable: false,
      configurable: true,
      value: function () {
        let o = this
        let idx = 0;
        let ks = Object.keys(o);
        return {
          next: function () { // 闭包
            return {
              value: o[ks[idx++]], // 取值
              done: (idx > ks.length) // 终止条件
            }
          }
        }
      }
    })
    
    // 手动遍历
    const it = myObj[Symbol.iterator]();
    console.log(it.next()); // {value: 2, done: false}
    console.log(it.next()); // {value: 3, done: false}
    console.log(it.next()); // {value: undefined, done: true}
    
  • for of循环时每次调用next()时内部指针都会向前移动并返回对象属性列表的下一个值;
  • 数组有内置的迭代器对象
    const arr = [1, 'hahah', 222, function() {}]
    const it = arr[Symbol.iterator]();
    // 手动遍历
    console.log(it.next());
    console.log(it.next());
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值